前言:该系列教程主要参考自网站www.opengl-tutorial.org,基于开源GUI框架imgui v1.61实现,imgui自带的例子里面直接集成了glfw+gl3w环境,本系列教程将gl3w换成了glew,glew具体环境配置可参考:OpenGL环境配置教程:VS2012 + GLEW + GLFW + GLM。
教程目录(持续更新中):
现代OpenGL教程(一):绘制三角形(ImGui+OpenGL3.3)
现代OpenGL教程(二):矩阵变换(ImGui+OpenGL3.3)
现代OpenGL教程(三):绘制彩色立方体(ImGui+OpenGL3.3)
现代OpenGL教程(四):立方体纹理贴图(ImGui+OpenGL3.3)
现代OpenGL教程(五):obj模型加载(ImGui+OpenGL3.3)
现代OpenGL教程(六):鼠标和键盘(ImGui+OpenGL3.3)
现代OpenGL教程(七):基础光照——颜色(ImGui+OpenGL3.3)
现代OpenGL教程(八):基础光照——Phong光照模型(ImGui+OpenGL3.3)
现代OpenGL教程(九):基础光照——材质(ImGui+OpenGL3.3)
本节教程在上一节(现代OpenGL教程(三):绘制彩色立方体(imgui+OpenGL3.3))的基础上,实现了BMP图像到OpenGL纹理的创建、绑定、填充和配置。代码下载地址:https://github.com/maijiaquan/blog-code/tree/master/t5
1.下列函数实现了一个BMP图像文件加载器,读取完BMP文件后,分别使用glGenTextures()
、glBindTexture()
、glTexImage2D()
进行纹理的创建、绑定和填充。
GLuint loadBMP_custom(const char *imagepath)
{
printf("Reading image %s\n", imagepath);
// Data read from the header of the BMP file
unsigned char header[54];
unsigned int dataPos;
unsigned int imageSize;
unsigned int width, height;
// Actual RGB data
unsigned char *data;
// Open the file
FILE *file = fopen(imagepath, "rb");
if (!file)
{
printf("%s could not be opened. Are you in the right directory ? Don't forget to read the FAQ !\n", imagepath);
getchar();
return 0;
}
// Read the header, i.e. the 54 first bytes
// If less than 54 bytes are read, problem
if (fread(header, 1, 54, file) != 54)
{
printf("Not a correct BMP file\n");
fclose(file);
return 0;
}
// A BMP files always begins with "BM"
if (header[0] != 'B' || header[1] != 'M')
{
printf("Not a correct BMP file\n");
fclose(file);
return 0;
}
// Make sure this is a 24bpp file
if (*(int *)&(header[0x1E]) != 0)
{
printf("Not a correct BMP file\n");
fclose(file);
return 0;
}
if (*(int *)&(header[0x1C]) != 24)
{
printf("Not a correct BMP file\n");
fclose(file);
return 0;
}
// Read the information about the image
dataPos = *(int *)&(header[0x0A]);
imageSize = *(int *)&(header[0x22]);
width = *(int *)&(header[0x12]);
height = *(int *)&(header[0x16]);
// Some BMP files are misformatted, guess missing information
if (imageSize == 0)
imageSize = width * height * 3; // 3 : one byte for each Red, Green and Blue component
if (dataPos == 0)
dataPos = 54; // The BMP header is done that way
// Create a buffer
data = new unsigned char[imageSize];
// Read the actual data from the file into the buffer
fread(data, 1, imageSize, file);
// Everything is in memory now, the file can be closed.
fclose(file);
// Create one OpenGL texture
GLuint textureID;
glGenTextures(1, &textureID);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, textureID);
// Give the image to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, data);
// OpenGL has now copied the data. Free our own version
delete[] data;
// Poor filtering, or ...
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// ... nice trilinear filtering ...
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
// ... which requires mipmaps. Generate them automatically.
glGenerateMipmap(GL_TEXTURE_2D);
// Return the ID of the texture we just created
return textureID;
}
2.在C++代码中使用刚写好的函数加载一个纹理:
GLuint Texture = loadBMP_custom("uvtemplate.bmp");
该纹理为BMP格式,图像如下:
1.片段着色器中,用UV坐标来使用纹理,Sampler2D
用于指定哪一个纹理,texture()
用于访问纹理,该方法返回一个(R,G,B,A)的vec4变量。
#version 330 core
// Interpolated values from the vertex shaders
in vec2 UV;
// Ouput data
out vec3 color;
// Values that stay constant for the whole mesh.
uniform sampler2D myTextureSampler;
void main(){
// Output color = color of the texture at the specified UV
color = texture( myTextureSampler, UV ).rgb;
}
2.顶点着色器中,只需把UV坐标传给片段着色器:
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec2 vertexUV;
// Output data ; will be interpolated for each fragment.
out vec2 UV;
// Values that stay constant for the whole mesh.
uniform mat4 MVP;
void main(){
// Output position of the vertex, in clip space : MVP * position
gl_Position = MVP * vec4(vertexPosition_modelspace,1);
// UV of the vertex. No special space for this one.
UV = vertexUV;
}
3.和上一节不同,上一节使用的是(R,G,B)三元组,而是(U,V)数对,共3*12个数对,分别对应12个三角形的3个顶点:
static const GLfloat g_uv_buffer_data[] = {
0.000059f, 1.0f - 0.000004f,
0.000103f, 1.0f - 0.336048f,
0.335973f, 1.0f - 0.335903f,
1.000023f, 1.0f - 0.000013f,
0.667979f, 1.0f - 0.335851f,
0.999958f, 1.0f - 0.336064f,
0.667979f, 1.0f - 0.335851f,
0.336024f, 1.0f - 0.671877f,
0.667969f, 1.0f - 0.671889f,
1.000023f, 1.0f - 0.000013f,
0.668104f, 1.0f - 0.000013f,
0.667979f, 1.0f - 0.335851f,
0.000059f, 1.0f - 0.000004f,
0.335973f, 1.0f - 0.335903f,
0.336098f, 1.0f - 0.000071f,
0.667979f, 1.0f - 0.335851f,
0.335973f, 1.0f - 0.335903f,
0.336024f, 1.0f - 0.671877f,
1.000004f, 1.0f - 0.671847f,
0.999958f, 1.0f - 0.336064f,
0.667979f, 1.0f - 0.335851f,
0.668104f, 1.0f - 0.000013f,
0.335973f, 1.0f - 0.335903f,
0.667979f, 1.0f - 0.335851f,
0.335973f, 1.0f - 0.335903f,
0.668104f, 1.0f - 0.000013f,
0.336098f, 1.0f - 0.000071f,
0.000103f, 1.0f - 0.336048f,
0.000004f, 1.0f - 0.671870f,
0.336024f, 1.0f - 0.671877f,
0.000103f, 1.0f - 0.336048f,
0.336024f, 1.0f - 0.671877f,
0.335973f, 1.0f - 0.335903f,
0.667969f, 1.0f - 0.671889f,
1.000004f, 1.0f - 0.671847f,
0.667979f, 1.0f - 0.335851f};
4.缓冲的创建、绑定、填充、配置
// Get a handle for our "myTextureSampler" uniform
GLuint TextureID = glGetUniformLocation(programID, "myTextureSampler");
GLuint uvbuffer;
glGenBuffers(1, &uvbuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_uv_buffer_data), g_uv_buffer_data, GL_STATIC_DRAW);
// Bind our texture in Texture Unit 0
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, Texture);
// Set our "myTextureSampler" sampler to use Texture Unit 0
glUniform1i(TextureID, 0);
// 2nd attribute buffer : UVs
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, uvbuffer);
glVertexAttribPointer(
1, // attribute. No particular reason for 1, but must match the layout in the shader.
2, // size : U+V => 2
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void *)0 // array buffer offset
);
参考资料:第五课:带纹理的立方体 opengl-tutorial