alpha通道

alpha通道的概念很简单。我们以前的计算结果是RGB值,现在则是RGBA值:

// Ouput data : it's now a vec4
out vec4 color;

前三个分量仍可以通过重组操作符(swizzle operator).xyz访问,最后一个分量通过.a访问:

color.a = 0.3;

与直觉相反,alpha代表的是不透明度;因此alpha = 1代表完全不透明,alpha = 0为完全透明。

这里我们简单地将alpha硬编码为0.3;但更常见的做法是用一个uniform变量表示它,或从RGBA纹理中读取(TGA格式支持alpha通道,而GLFW支持TGA格式)。

结果如下。既然我们能”看透”模型表面,请确保关闭背面剔除(glDisable(GL_CULL_FACE) )。否则我们就发现模型没有了”背”面。

顺序很重要!

上一个截图看上去还行,但这仅仅是运气好罢了。

问题所在

这里我画了一红一绿两个alpha值为50%的正方形。从中可以看出顺序的重要性,最终的颜色显著影响了眼睛对深度的感知。

我们的场景中也出现了同样的现象。试着稍稍改变一下观察点:

原来这还是个十分棘手的问题。您在游戏中也没怎么见过透明的东西,是吧?

常见解决方案

常见解决方案即对所有的透明三角形排序。是的,所有的透明三角形。

  • 渲染场景的不透明部分,让深度缓冲丢弃被遮挡的透明三角形。
  • 对透明三角形按深度由远及近排序。
  • 渲染透明三角形。

可以用C语言的qsort函数或者C++的std::sort函数来排序。细节就不多说了,因为……

警告

这么做可以解决问题(下一节还会介绍它),但是:

  • 您将受限于填充率。亦即每个片段会写10、20次,也许更多次。这对力不从心的内存总线来说太繁重了。通常,深度缓冲可以自动丢弃”远”片段;但这里我们已显式地对片段进行了排序,因此深度缓冲实际上没起作用。
  • 这些操作,每个像素上都会执行4遍(我们用了4倍多重采样抗锯齿(MSAA)),除非用了什么巧妙的优化。
  • 透明三角形排序很耗时
  • 若要逐个三角形地切换纹理,或者更糟糕,切换着色器–性能会大打折扣。不要这么做。

一个足够好的解决方案是:

  • 限制透明多边形的数量
  • 对所有透明多边形使用同一个着色器和纹理
  • 若这些透明多边形本就是外表各异的,那就用不同的纹理
  • 若不排序时效果还凑合,那就不排序好了。

顺序无关透明

如果您的引擎确实需要顶级的透明效果,这里有一些值得研究的技术:

注意,即便是《小小大星球》(Little Big Planet)这种刚出炉的、运行在高端主机上的游戏,也只用了一层透明。

混合函数

要让上述代码运行起来,得设置好混合函数。

// Enable blending
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

这意味着

New color in framebuffer =
           current alpha in framebuffer * current color in framebuffer +
           (1 - current alpha in framebuffer) * shader's output color

上图红色方块居上的效果由如下等式得来:

new color = 0.5*(0,1,0) + (1-0.5)*(1,0.5,0.5); // (the red was already blended with the white background)
new color = (1, 0.75, 0.25) = the same orange