引自:
https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/03%20Blending/
1,如果一张2D的草地是这样的,但是背景会有一部分是白色的,而白色的部分,实际上是我们可以透视过去的,这就需要我们需要设置图片的透明度来达到透明的效果,也就是设置图片的Alpha值。
Alpha颜色值是颜色向量的第四个分量,你可能已经看到过它很多遍了。在这个教程之前我们都将这个第四个分量设置为1.0,让这个物体的透明度为0.0,而当alpha值为0.0时物体将会是完全透明的。当alpha值为0.5时,物体的颜色有50%是来自物体自身的颜色,50%来自背后物体的颜色。
2,丢弃片段
有些图片并不需要半透明,只需要根据纹理颜色值,显示一部分,或者不显示一部分,没有中间情况。比如说草,如果想不太费劲地创建草这种东西,你需要将一个草的纹理贴在一个2D四边形(Quad)上,然后将这个四边形放到场景中。然而,草的形状和2D四边形的形状并不完全相同,所以你只想显示草纹理的某些部分,而忽略剩下的部分。
下面这个纹理正是这样的,它要么是完全不透明的(alpha值为1.0),要么是完全透明的(alpha值为0.0),没有中间情况。你可以看到,只要不是草的部分,这个图片显示的都是网站的背景颜色而不是它本身的颜色。
所以当添加像草这样的植被到场景中时,我们不希望看到草的方形图像,而是只显示草的部分,并能看透图像其余的部分。我们想要丢弃(Discard)显示纹理中透明部分的片段,不将这些片段存储到颜色缓冲中。在此之前,我们还要学习如何加载一个透明的纹理。
要想加载有alpha值的纹理,我们并不需要改很多东西,stb_image
在纹理有alpha通道的时候会自动加载,但我们仍要在纹理生成过程中告诉OpenGL,我们的纹理现在使用alpha通道了:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
同样,保证你在片段着色器中获取了纹理的全部4个颜色分量,而不仅仅是RGB分量:
void main() { // FragColor = vec4(vec3(texture(texture1, TexCoords)), 1.0); FragColor = texture(texture1, TexCoords); }
既然我们已经知道该如何加载透明的纹理了,是时候将它带入实战了,我们将会在深度测试小节的场景中加入几棵草。
我们会创建一个vector,向里面添加几个glm::vec3
变量来代表草的位置:
vector<glm::vec3> vegetation; vegetation.push_back(glm::vec3(-1.5f, 0.0f, -0.48f)); vegetation.push_back(glm::vec3( 1.5f, 0.0f, 0.51f)); vegetation.push_back(glm::vec3( 0.0f, 0.0f, 0.7f)); vegetation.push_back(glm::vec3(-0.3f, 0.0f, -2.3f)); vegetation.push_back(glm::vec3( 0.5f, 0.0f, -0.6f));
每个草都被渲染到了一个四边形上,贴上草的纹理。这并不能完美地表示3D的草,但这比加载复杂的模型要快多了。使用一些小技巧,比如在同一个位置加入一些旋转后的草四边形,你仍然能获得比较好的结果的。
因为草的纹理是添加到四边形对象上的,我们还需要创建另外一个VAO,填充VBO,设置正确的顶点属性指针。接下来,在绘制完地板和两个立方体后,我们将会绘制草:
glBindVertexArray(vegetationVAO); glBindTexture(GL_TEXTURE_2D, grassTexture); for(unsigned int i = 0; i < vegetation.size(); i++) { model = glm::mat4(); model = glm::translate(model, vegetation[i]); shader.setMat4("model", model); glDrawArrays(GL_TRIANGLES, 0, 6); }
运行程序你将看到:
显然这不是我们所需要的,白色的部分是我们需要透明的部分。
出现这种情况是因为OpenGL默认是不知道怎么处理alpha值的,更不知道什么时候应该丢弃片段。我们需要自己手动来弄。幸运的是,有了着色器,这还是非常容易的。GLSL给了我们discard
命令,一旦被调用,它就会保证片段不会被进一步处理,所以就不会进入颜色缓冲。有了这个指令,我们就能够在片段着色器中检测一个片段的alpha值是否低于某个阈值,如果是的话,则丢弃这个片段,就好像它不存在一样:
#version 330 core out vec4 FragColor; in vec2 TexCoords; uniform sampler2D texture1; void main() { vec4 texColor = texture(texture1, TexCoords); if(texColor.a < 0.1) discard; FragColor = texColor; }
这里,我们检测被采样的纹理颜色的alpha值是否低于0.1的阈值,如果是的话,则丢弃这个片段。片段着色器保证了它只会渲染不是(几乎)完全透明的片段。现在它看起来就正常了:
源码可以去Learn OpenGL去下载