OpenGL纹理叠加
在 OpenGL纹理贴图的基础上继续深入学习纹理相关内容,本篇文章的主要目的是为了实现OpenGL纹理叠加,需要引入 纹理单元的相关概念。
1. 纹理贴图的实现
按照之前纹理贴图的实现方法在两个三角形组成的矩形内贴上星空和胡桃如下所示
2. 纹理单元
之前在绘制纹理贴图时着色器的编写如下所示:
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture sampler
uniform sampler2D texture1;
void main()
{
FragColor = texture(texture1, TexCoord);
}
注意到sampler2D
变量是个uniform,但在之前渲染程序中却没用glUniform给它赋值(之前不需要)一个纹理的位置值通常称为一个纹理单元(Texture Unit)。一个纹理的默认纹理单元是0,它是默认的激活纹理单元,所以教程前面部分我们没有分配一个位置值。
使用glUniform1i可以给纹理采样器分配一个位置值,能够在一个片段着色器中设置多个纹理。
具体实现:
- 通过把纹理单元赋值给采样器,我们可以一次绑定多个纹理,只要我们首先激活对应的纹理单元。就像glBindTexture一样,我们可以使用glActiveTexture激活纹理单元,传入我们需要使用的纹理单元:
//纹理单元GL_TEXTURE0默认总是被激活,所以我们在前面的例子里当我们使用`glBindTexture`的时候,无需激活任何纹理单元
glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture1);//绑定这个纹理到当前激活的纹理单元
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
注意: OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8
-
记得创建并导入另一张纹理图
-
修改片段着色器
需要编辑片段着色器来接收另一个采样器
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture samplers
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
// linearly interpolate between both textures (40% texture1, 60% texture2)
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.6);
}
最终输出颜色是两个纹理的结合。GLSL内建的mix
函数需要接受两个值作为参数,并对它们根据第三个参数进行线性插值。如果第三个值是0.0
,它会返回第一个输入;如果是1.0
,会返回第二个输入值
- 使用glUniform1i设置每个采样器告诉OpenGL每个着色器采样器属于哪个纹理单元,因为只需要设置一次,所以放在渲染循环的前面
ourShader.use(); // 不要忘记在设置uniform变量之前激活着色器程序!
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); // 手动设置
ourShader.setInt("texture2", 1); // 或者使用着色器类设置
while (!glfwWindowShouldClose(window))
{
// input
processInput(window);
// render
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// bind textures on corresponding texture units
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
// render
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glfwSwapBuffers(window);
glfwPollEvents();
}
实现的结果如图所示:
在片段着色器中可以设定叠加比例,上面的叠加是固定的,我们也可以将其设置为一个变量
片段着色器代码修改为:
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), mixValue);
然后只需要修改渲染部分的代码,在渲染区域添加
float timeValue = glfwGetTime();
mixValue = static_cast<float>(sin(timeValue * 1) / 2.0 + 0.5);
ourShader.setFloat("mixValue", mixValue);
最后得到随时间变化的纹理图片如下所示: