对渲染管线的一点理解:opengl把渲染流程设计成一套“渲染管线”,把相同的操作抽象出来设计成“黑盒”,对开发者透明,把可以定制的操作抽象成API接口,提供给开发者,就像做填空题一样。于是就有了“顶点着色器”、“片元着色器”,开发者不用和复杂的GPU硬件接口打交道,就能实现酷炫的图像效果。
opengl渲染管线简化理解
简单的方式实现纹理混合
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
错误示例-去掉discard
正确示例-增加discard
void main()
{
vec4 texColor = texture(texture1, TexCoords);
if (texColor.a < 0.1)
{
discard;
}
// FragColor = vec4(vec3(texture(texture1, TexCoords)), 1.0);
FragColor = texture(texture1, TexCoords);
}
纹理使用的理解:纹理必须要贴在一个几何表面上,草的纹理不能凭空绘制出来,也是附着在一个正方形上。正方形平移四次绘制,就可以生成4个草的纹理。注意:顶点着色器中position有位移操作,但是纹理不需要再单独进行位移操作 草纹理附着的四边形
float transparentVertices[] = {
// positions // texture Coords (swapped y coordinates because texture is flipped upside down)
0.0f, 0.5f, 0.0f, 0.0f, 0.0f,
0.0f, -0.5f, 0.0f, 0.0f, 1.0f,
1.0f, -0.5f, 0.0f, 1.0f, 1.0f,
0.0f, 0.5f, 0.0f, 0.0f, 0.0f,
1.0f, -0.5f, 0.0f, 1.0f, 1.0f,
1.0f, 0.5f, 0.0f, 1.0f, 0.0f
};
// 绘制草
glBindVertexArray(transparantVAO);
glBindTexture(GL_TEXTURE_2D, transparentTexture);
for (unsigned int i = 0; i<vegetation.size(); i++) {
model = glm::mat4(1.0f);
model = glm::translate(model, vegetation[i]);
shader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
白色条纹
// 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_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
clamp去条纹
纹理缓和的计算也不复杂,根据alpha通道值做叠加或减除融合,详细可参考opengl-混合
这里,我们重点看下混合中的问题及解决防范
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
出现不合理的遮挡
运气好一切OK
运气好
正常运行效果的代码,其实就做了微小的调整,按照窗户从后到前的顺序绘制窗户,上面“不正常效果”是随机绘制
// 窗户纹理位移坐标
vector<glm::vec3> vegetation
{
glm::vec3(0.5f, 0.0f, -0.6f),
glm::vec3(-1.5f, 0.0f, -0.48f),
glm::vec3(-0.3f, 0.0f, -0.23f),
glm::vec3(1.5f, 0.0f, 0.51f),
glm::vec3(0.0f, 0.0f, 0.7f)
};
深度测试并不能智能的分析出哪些片元需要考虑混合,只会“死脑筋”的按照绘制顺序做深度测试,如果先绘制了前面的图形(即使前面的图元有透明的部分),后面绘制的图形进行深度测试会失败会被丢弃,根本没机会进入到后面的混合渲染流程。
所以,按照从后到前顺序(Z坐标由远及近)绘制是没有问题的,那么问题来了,总不能每次手动调整图形绘制的前后顺序吧?
比较简单的处理,通过排序来调整绘制顺序。
注意STL map默认是对key值做升序排序
vector<glm::vec3> vegetation
{
glm::vec3(-1.5f, 0.0f, -0.48f),
glm::vec3( 1.5f, 0.0f, 0.51f),
glm::vec3( 0.0f, 0.0f, 0.7f),
glm::vec3(-0.3f, 0.0f, -2.3f),
glm::vec3( 0.5f, 0.0f, -0.6f)
};
std::map<float, glm::vec3> sorted;
for (unsigned int i = 0; i < vegetation.size(); i++) {
float distance = glm::length(camera.Position - vegetation[i]);
sorted[distance] = vegetation[i];
}
...
省略其他代码
...
for (std::map<float, glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it) {
model = glm::mat4(1.0f);
model = glm::translate(model, it->second);
shader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
实现效果正常