OpenGL 编程/透明度
在计算机图形学中,不透明物体很容易绘制。如果我们观察屏幕上的特定像素,场景中可能会有零个、一个或多个与该像素重叠的物体。由于深度缓冲区的存在,您不必担心哪个物体在最上面。您只需开始绘制该像素上的第一个物体,记住它的深度,当您到达下一个物体时,将第二个物体的深度与之前物体的深度进行比较,如果它更小,则用第二个物体的颜色覆盖像素,否则什么也不做。
对于半透明物体,事情就更复杂了。GPU 不能仅仅根据深度值选择覆盖像素的颜色或保留它。相反,GPU 需要将透明物体的颜色与它后面的物体的颜色混合。但是,如果它还没有绘制后面的物体,它就无法知道后面的物体的颜色。深度缓冲区在这里就无济于事;您绘制物体的顺序突然变得非常重要。
这个问题有很多解决方案。显而易见的解决方案是对所有物体从最远到最近进行排序(或者说是这样?)。但是排序的成本可能非常高,尤其是在每次场景发生变化时都需要重新排序,包括每次相机移动时。幸运的是,有一些技术可以避免这样做,但它们都有局限性。
我们可以使用累积缓冲区来模拟透明度。例如,想象一个包含不透明物体和一块 50% 透明的彩色玻璃的场景。我们将使用的技巧是,我们将渲染此场景两次:一次是将所有物体(包括玻璃)都绘制成完全不透明的。第二次我们只渲染不透明物体,并跳过渲染透明物体。使用累积缓冲区,我们计算两个帧的平均值。结果将是,在玻璃窗格所在的位置,它后面物体的 50% 的光线会穿透。
通过在累积缓冲区中平均两个以上的帧,或通过用不同的值乘以每个帧,您可以轻松地改变透明度。然而,这种技术的局限性在于,当您有两个或多个透明物体直接彼此重叠时,它无法正常工作。在现实生活中,两块 50% 透明的玻璃窗格只会使它们后面物体的 25% 的光线穿透。然而,使用累积缓冲区技巧,您仍然可以看到它们后面物体的 50%。
使物体透明的常用方法是更改其纹理中的 alpha 通道。因此,从一个只查找纹理的非常简单的片段着色器开始,我们引入了 alpha 通道的可配置截止值。如果 alpha 值低于截止值,我们就只绘制该片段,否则我们将它绘制成不透明的。
varying vec2 texcoord;
uniform sampler2D texture;
uniform float alpha_cutoff;
void main(void) {
vec4 color = texture2D(texture, texcoord);
// Don't draw pixels with an alpha value lower than the cutoff
if(color.a < alpha_cutoff)
discard;
gl_FragColor = color;
}
然后,我们将场景绘制两次。一次使用 1/3 的截止值。透明度为 50% 的物体将在第一次传递中绘制。第二次我们使用 2/3 的截止值。透明度为 50% 的物体不会在这次传递中绘制。然后我们从累积缓冲区获取两次传递的平均值并将其发送到屏幕。
glUniform1f(uniform_alpha_cutoff, 1.0 / 3.0);
draw_scene();
glAccum(GL_LOAD, 0.5);
glUniform1f(uniform_alpha_cutoff, 2.0 / 3.0);
draw_scene();
glAccum(GL_ACCUM, 0.5);
glAccum(GL_RETURN, 1);
glSwapBuffers();
- 修改上面的代码,使其仍然只绘制场景两次,但使其适用于 80% 透明的物体。
- 修改上面的代码,使其绘制场景超过两次,使其适用于所有透明度为 25%、50% 或 75% 的物体。
- 尝试在任何以前的教程中实现这种技术。
- 你能将这种技术与抗锯齿、运动模糊和/或景深有效地结合起来吗?
- 对物体进行排序似乎是完美的方法。但是,物体可以具有奇怪的形状,并且两个物体之间可能没有明确的顺序。例如,考虑两个互锁的圆环。但是,对所有单个三角形进行排序怎么样?