当前位置: 首页 > news >正文

【C++游戏引擎开发】第15篇:OpenGL中的纹理加载

一、相关库的下载与安装

1.1 stb_image.h

​GitHub仓库​(直接下载 stb_image.h 文件)
https://github.com/nothings/stb/blob/master/stb_image.h
点击右上角的 Raw 按钮 → 右键 “另存为” stb_image.h

使用注意:

#define STB_IMAGE_IMPLEMENTATION  // 必须定义宏(仅需一次)
#include "stb_image.h"

1.2 GLEW介绍及安装

GLEW(OpenGL Extension Wrangler Library)是一个用于简化OpenGL扩展和核心功能使用的开源库。它主要用于帮助开发者动态加载和管理OpenGL函数指针,特别是在使用OpenGL高级功能或扩展时。
跨平台C++包管理利器vcpkg完全指南

vcpkg install glew
1.2.1 GLEW注意事项
  • 必须在OpenGL上下文创建之后调用glewInit()(例如在GLFW的glfwMakeContextCurrent之后)。
  • 在包含GLEW头文件(#include <GL/glew.h>)前,​不要包含系统自带的OpenGL头文件(如<GL/gl.h>),否则可能引发宏冲突。
#include <GL/glew.h>
// 注意:GLEW 必须在其他 OpenGL 头文件之前包含!
#include <GLFW/glfw3.h>  // 如果使用 GLFW
1.2.1 示例代码
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>

int main() {
   
    // 初始化 GLFW
    if (!glfwInit()) {
   
        std::cerr << "GLFW 初始化失败!" << std::endl;
        return -1;
    }

    // 创建窗口和 OpenGL 上下文
    GLFWwindow* window = glfwCreateWindow(800, 600, "GLEW Demo", NULL, NULL);
    glfwMakeContextCurrent(window);

    // 初始化 GLEW
    GLenum err = glewInit();
    if (err != GLEW_OK) {
   
        std::cerr << "GLEW 初始化失败: " << glewGetErrorString(err) << std::endl;
        glfwTerminate();
        return -1;
    }

    // 检查 OpenGL 版本
    std::cout << "OpenGL 版本: " << glGetString(GL_VERSION) << std::endl;

    // 主循环
    while (!glfwWindowShouldClose(window)) {
   
        glClear(GL_COLOR_BUFFER_BIT);
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glfwTerminate();
    return 0;
}

1.3 纹理图片资源下载

https://polyhaven.com/zh

本文所用的三个资源:
Wood Floor Worn
Metal Plate
Denim Fabric


二、stb_image主要函数

2.1 stbi_load

unsigned char *stbi_load(
    char const *filename, 
    int *x, 
    int *y, 
    *int channels_in_file, 
    int desired_channels
);
  • 功能: 加载常见格式图像(JPEG/PNG/BMP/TGA等)
  • 参数:
    • filename: 图像文件路径
    • x: 返回图像宽度(输出参数)
    • y: 返回图像高度(输出参数)
    • channels_in_file: 返回原始通道数(输出参数)
    • desired_channels: 强制转换的通道数(0=保持原样)
  • 返回值:
    • 成功: 图像数据指针(每像素desired_channels字节)
    • 失败: NULL
  • 示例:
int width, height, channels;
unsigned char *data = stbi_load("image.png", &width, &height, &channels, 3);

2.2 stbi_load_from_memory

unsigned char *stbi_load_from_memory(
    stbi_uc const *buffer, 
    int len, 
    int *x, 
    int *y, 
    *int channels_in_file, 
    int desired_channels
);
  • 功能: 从内存缓冲区加载图像
  • 参数:
    • buffer: 内存中的图像数据指针
    • len: 数据长度(字节)
    • x: 返回图像宽度(输出参数)
    • y: 返回图像高度(输出参数)
    • channels_in_file: 返回原始通道数(输出参数)
    • desired_channels: 强制转换的通道数(0=保持原样)
  • 返回值:
    • 成功: 图像数据指针(每像素desired_channels字节)
    • 失败: NULL

2.3 stbi_image_free

void stbi_image_free(void *retval_from_stbi_load);
  • 功能: 释放stbi_load分配的内存,每个stbi_load调用必须对应一个stbi_image_free-
  • 注意: 必须调用此函数释放资源

三、实战示例

3.1 顶点着色器

#version 460 core  // 指定GLSL版本为4.6
layout (location = 0) in vec3 aPos;     // 顶点位置属性,索引0
layout (location = 1) in vec2 aTexCoord;// 纹理坐标属性,索引1

out vec2 TexCoord;  // 输出到片段着色器的纹理坐标

// 三个变换矩阵的uniform变量
uniform mat4 model;      // 模型空间->世界空间
uniform mat4 view;       // 世界空间->观察空间
uniform mat4 projection;  // 观察空间->裁剪空间

void main()
{
   
    // 应用矩阵变换顺序:投影 * 视图 * 模型 * 位置
    gl_Position = projection * view * model * vec4(aPos, 1.0);
    // 直接传递纹理坐标
    TexCoord = aTexCoord;
}

3.2 片段着色器

#version 460 core
in vec2 TexCoord;        // 从顶点着色器输入的纹理坐标
out vec4 FragColor;       // 最终输出的颜色值

// 三个不同的纹理采样器
uniform sampler2D diffuse0;  // 0号纹理单元(木材)
uniform sampler2D diffuse1;  // 1号纹理单元(金属)
uniform sampler2D diffuse2;  // 2号纹理单元(牛仔布)
uniform int textureIndex = 0;  // 当前选择的纹理索引

void main()
{
   
    // 根据索引选择采样器
    switch(textureIndex) {
   
        case 0: 
            FragColor = texture(diffuse0, TexCoord);  // 采样木材纹理
            break;
        case 1: 
            FragColor = texture(diffuse1, TexCoord);  // 采样金属纹理
            break;
        case 2: 
            FragColor = texture(diffuse2, TexCoord);  // 采样牛仔布纹理
            break;
        default: 
            FragColor = vec4(1.0);  // 默认显示白色
    }

相关文章:

  • 《组合优于继承:构建高内聚低耦合模块的最佳实践》
  • 如何把pdf的内容转化成结构化数据进行存储到mysql数据库
  • 【KWDB创作者计划】_KWDB应用之实战案例
  • java面试题带答案2025最新整理
  • 【动手学强化学习】番外7-MAPPO应用框架2学习与复现
  • 编译构建 WSO2 产品时的一些注意事项
  • Spring事务同步器在金融系统中的应用:从风控计算到交易投递
  • 车载通信架构 --- DOIP系统机制初入门
  • 五款AI论文工具,助力完成论文写作
  • Konga密码重置
  • Node.js项目开启多进程的2种方案
  • C/C++的数据类型
  • 编程通用-配置文件的选择
  • Django从零搭建卖家中心登陆与注册实战
  • 为了四季度的盈利,李斌的换人还在继续
  • Java Stream深度解析 高阶技巧与性能优化实战
  • 高等数学同步测试卷 同济7版 试卷部分 上 做题记录 第三章微分中值定理与导数的应用同步测试卷 B 卷
  • C++中string库常用函数超详细解析与深度实践
  • Java数组初始化全解析:方式、场景与最佳实践
  • 嵌入式基础(二)ARM基础
  • 世界读书日|阅读在上海
  • 上海消保委调查二次元消费:手办与卡牌受欢迎,悦己和社交是动力
  • 4月语言学联合书单|法庭审判话语的态度表达及人际功能研究
  • 马上评|治理计量不准确收费不规范,水电气要有明白账
  • 如何保护人工智能领域的知识产权?上海市知识产权局局长解答
  • 包邮到高原,跨越4083公里送妈妈一张按摩椅