开始学习OpenGL,参考的是著名的LearnOpenGL这个网站,在这里作一些总结性的记录,只是方便本身往后查找或者记录本身的一些拓展思考,关于OpenGL的具体内容请移步:
https://learnopengl-cn.github.io/
或英文原版:https://learnopengl.com/c++
为了可以把纹理映射(Map)到三角形上,咱们须要指定三角形的每一个顶点各自对应纹理的哪一个部分。这样每一个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪一个部分采样(译注:采集片断颜色)。以后在图形的其它片断上进行片断插值(Fragment Interpolation)。git
因此先在顶点数据中加入纹理坐标并记得将其传入顶点着色器:github
float vertices[] = { // ---- 位置 ---- ---- 颜色 ---- - 纹理坐标 - 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上 0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下 -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下 -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上 };
用的stb_image.h这个头文件:函数
//读取图片 int width, height, nrChannels;//图片宽度,高度,颜色通道数量 stbi_set_flip_vertically_on_load(true);//翻转y轴 unsigned char* data = stbi_load("wall.jpg", &width, &height, &nrChannels, 0);
相似于以前生成VAO、VBO的流程:学习
//生成纹理 unsigned int texture1; glGenTextures(1, &texture1);//第一个参数为生成纹理的数量 glBindTexture(GL_TEXTURE_2D, texture1);//绑定纹理对象
设置纹理环绕方式和纹理过滤选项,决定了纹理坐标超过1时如何采样以及多级渐远纹理级别之间的过滤方式,具体含义和效果见LearnOpenGL。网站
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_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
这里要注意glTexImage2D的第3个和第7个参数,决定了纹理的源格式和咱们但愿的处理格式,若是原图带有Alpha通道,这里要改为GL_RGBA,原文中没有说明致使我一直加载不出带透明通道的图,后来看源码才发现。code
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);//生成纹理 glGenerateMipmap(GL_TEXTURE_2D);//生成多级渐远纹理
结束以后记得释放图像的内存:orm
stbi_image_free(data);//释放图像内存
纹理坐标传入顶点着色器后直接传到片元着色器便可,至于纹理对象自己定义为sampler2D类型的uniform变量,经过GLSL的texture
函数便可进行采样。对象
#version 330 core out vec4 FragColor; in vec3 ourColor; in vec2 TexCoord; uniform sampler2D ourTexture; void main() { FragColor = texture(ourTexture, TexCoord); }
结果:blog
能够看到上面片元着色器中定义了uniform的纹理变量,但咱们并无在程序中给这个uniform变量传值,这是由于OpenGL中有不少纹理单元,若是只有一个纹理对象,会默认分配至GL_TEXTURE0,sampler变量也会默认从0号单元中采样,因此若是须要渲染更多的纹理,须要分配不一样的纹理单元。
更改一下片元着色器,用mix
函数让两张图片进行混合:
#version 330 core out vec4 FragColor; in vec3 ourColor; in vec2 TexCoord; uniform sampler2D texture1; uniform sampler2D texture2; void main() { FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.3); }
先按以前的方式读入第二张图(这里读了一张带透明通道的图因此记得glTexImage2D的参数改成GL_RGBA):
unsigned int texture2; glGenTextures(1, &texture2); glBindTexture(GL_TEXTURE_2D, texture2); 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_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); data = stbi_load("face.png", &width, &height, &nrChannels, 0); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D); stbi_image_free(data);
而后再渲染循环以前
给sampler指定对应的纹理单元(注意必定要先激活shader程序):
shader.use(); glUniform1i(glGetUniformLocation(shader.ID, "texture1"), 0); glUniform1i(glGetUniformLocation(shader.ID, "texture2"), 1);
而后激活对应的纹理单元并将纹理对象绑定上去:
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture1); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, texture2);
结果:
在LearnOpenGL这一章的评论区发现有人说,当读取的图片宽度为奇数时显示不正常,而我试了一下宽度为奇数的图片程序直接就崩溃了,评论区也给出了解决方法,加入一行代码以后就正常了:
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
这行代码的做用是设置OpenGL读取数据的对齐方式,OpenGL默认为4字节对齐,也就是说一行图像数据的长度必须是4的倍数,我又尝试了一下宽度为偶数但不是4的倍数的图片,果真显示不正确:
因此上面的代码就是设置读取数据的方式为1个字节对齐,这样OpenGL会一个一个字节读取,不会致使越界,但这样读取效率确定也会下降,因此最好的方法仍是提供宽度为4的倍数的图片。