在实际场景中,我们会遇到一些透明的物体,如何显示透明物体是一个问题。
RGBA值中A即为透明度,当A非常小,接近或等于零时,我们可以直接在fragment shader中将这些部分舍弃。
void main()
{
vec4 texColor = texture(texture1, TexCoords);
if(texColor.a < 0.1)
discard;
FragColor = texColor;
}
注意,当采样纹理的边缘的时候,OpenGL会对边缘的值和纹理下一个重复的值进行插值(因为我们将它的环绕方式设置为了GL_REPEAT。这通常是没问题的,但是由于我们使用了透明值,纹理图像的顶部将会与底部边缘的纯色值进行插值。这样的结果是一个半透明的有色边框,你可能会看见它环绕着你的纹理四边形。要想避免这个,每当你alpha纹理的时候,请将纹理的环绕方式设置为GL_CLAMP_TO_EDGE:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, format == GL_RGBA ? GL_CLAMP_TO_EDGE : GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, format == GL_RGBA ? GL_CLAMP_TO_EDGE : GL_REPEAT);
当alpha值较大时,就必须用到blend混合
glEnable(GL_BLEND);
以下是混合方程
通过glBlendFunc(GLenum sfactor, GLenum dfactor)中的两个参数,我们可以控制方程中的参数。
方程默认是相加的,我们还可以通过glBlendEquation(GLenum mode)来改变方程,使其相减。
混合顺序
混合的原理是将当前片段与已绘制的片段进行混合,这有可能与深度测试冲突。
比如我们先绘制靠前的透明窗户,在渲染靠后的箱子,此时箱子进行深度测试,由于深度值值小直接舍弃,并不进行绘制。造成了透明窗户实际遮挡了后面的箱子。
所以绘制时要注意顺序,大体规则是:
- 先绘制所有不透明的物体。
- 对所有透明的物体排序。
- 按顺序从后往前绘制所有透明的物体。
排序透明物体的一种方法是,从观察者视角获取物体的距离。这可以通过计算摄像机位置向量和物体的位置向量之间的距离所获得。
接下来我们把距离和它对应的位置向量存储到一个STL库的map数据结构中。map会自动根据键值(Key)对它的值排序,所以只要我们添加了所有的位置,并以它的距离作为键,它们就会自动根据距离值排序了。
我们使用了map的一个反向迭代器(Reverse Iterator),反向遍历其中的条目,并将每个窗户四边形位移到对应的窗户位置上。
std::map<float, glm::vec3> sorted;
for (unsigned int i = 0; i < windows.size(); i++)
{
float distance = glm::length(camera.Position - windows[i]);
sorted[distance] = windows[i];
}
for(std::map<float,glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it)
{
model = glm::mat4();
model = glm::translate(model, it->second);
shader.setMat4("model", model);
glDrawArrays(GL_TRIANGLES, 0, 6);
}
现在已经有次序无关的透明度算法了,不过比较复杂