【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); // 默认显示白色
}