使用GL_DEPTH_TEST和透明纹理时出现渲染故障

3

从某个角度看,我的灌木丛是这样的:

从其他方面看,它们看起来像这样:

我的理论是,从第一个角度观察灌木丛时,所有灌木丛后面的方块已经被绘制出来了,因此在绘制灌木丛时,只需在其上方绘制它们。
然而,从另一个角度来看,它基本上是先尝试绘制灌木丛,然后当它去绘制灌木丛后面的方块时,它会检查深度缓冲区并发现已经有东西挡住了该方块的视线,所以它不会渲染它,导致了深蓝色的正方形(我的清除颜色)。
但我真的不知道如何解决这个问题。禁用深度测试会导致各种其他错误。是否有一些方法可以标记顶点或多边形为透明,以便它知道仍然需要渲染背景?

发现了这个。这是唯一的解决方案吗?要分离我的透明和不透明块,然后在CPU上每帧手动排序它们,因为玩家可以移动吗?肯定有一种方法可以将此委派给GPU...


2
正在开发Minecraft克隆游戏? - Jesus Ramos
2
嘿,那些是Mojang的256像素! :P - Thomas
@JesusRamos:差不多就是这样 :) 游戏玩法和重点会有所不同。 - mpen
1
@Thomas:如果你想要技术上的精确度,那就是65536个像素。 - mpen
3个回答

10

那个链接(以及在CPU上进行的排序)是为了Alpha混合。如果您只需要Alpha测试(而不是混合),则无需对任何内容进行排序。只需启用alpha测试,保持深度测试启用,所有内容都将被正确渲染。

请参阅此处:http://www.opengl.org/wiki/Transparency_Sorting 您需要“Alpha test”,它要求进行Alpha测试,而不是需要排序的“标准半透明”。


启用 alpha 测试会使用 glAlphaFunc,对吗?那我应该使用什么函数呢?默认的似乎不起作用。 "不等于" 几乎可以工作,除了从侧面直接查看时:http://i.imgur.com/HMhSu.png - mpen
@Martins:没事了。你的文章解释得很清楚。我稍后再处理半透明度问题,但我想我会很快解决它。 - mpen
1
哇,你真是我的救星!!!我已经连续战斗了48个小时,最后只需要两行代码就解决了问题...谢谢你,如果可以的话我会给你钱的哈哈。谢谢。 - user2758776

3

如果你使用 WebGL 或 OpenGL ES 2.0(iPhone/Android),则没有 alpha 测试。取而代之的是你需要不绘制透明像素。这样它们就不会影响深度缓冲器,因为没有写入任何像素。为此,你需要在片段着色器中丢弃透明像素。可以硬编码实现。

...
void main() {
   vec4 color = texture2D(u_someSampler, v_someUVs);
   if (color.a == 0.0) {
     discard;
   }
   gl_FragColor = color;
}

或者你可以模拟旧的alpha测试方式,其中你可以设置alpha值。
...
uniform float u_alphaTest;
void main() {
   vec4 color = texture2D(u_someSampler, v_someUVs);
   if (color.a < u_alphaTest) {
     discard;
   }
   gl_FragColor = color;
}

3
解决方案#1:
  1. 首先以启用深度缓存的任意顺序呈现所有不透明对象。这包括使用Alpha测试而不是Alpha混合的所有对象。
  2. 对于采用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)的对象(烟雾/玻璃/草): 从最远到最近的多边形呈现透明场景,禁用深度缓冲区写入(glDepthMask(GL_FALSE))。如果所有透明对象都是凸的且不相交,则可以按对象而不是多边形进行排序。
  3. 对于采用glBlendFunc(GL_SRC_ALPHA, GL_ONE)glBlend(GL_ONE, GL_ONE)(火,"魔法"粒子系统,发光)的对象: 在启用深度缓存的情况下,以任意顺序呈现透明场景,并禁用深度缓冲区写入(glDepthMask(GL_FALSE))。
  4. 在步骤#3之后不要呈现任何启用了深度缓存的对象。
解决方案#2: 使用深度剥离(搜索引擎搜索相关内容)。特别适用于互相交错的透明对象。不适用于需要使用解决方案#1的粒子系统和草。
“然后在CPU上手动排序几乎每一帧” 插入排序对于已经排序或部分排序的数据非常有效。
“必须有一种方法将其委托给GPU......” 我认为您可以使用几何着色器中的贴图(例如Alpha通道),标记具有和不具有草的区域,以生成正确顺序的草多边形。这需要OpenGL 4,并且您可能需要对喂到着色器中以生成草补丁的多边形进行某种高级别的排序。
如果所有方向都是完全对称的,则可通过顶点着色器旋转单个灌木来保持正确的多边形顺序(旋转角度为+-90/180/270度)。
并且有合并排序算法可以很好地并行化,可以在GPU上执行,使用GDGPU方法或OpenCL/CUDA。
但是,对于渲染5株草的情况而言,使用类似此类的方法相当于试图用榴弹发射器杀死一只蟑螂——虽然好玩但并不高效。
我建议忘记“将其卸载到GPU”直到实际遇到性能问题。始终使用分析工具进行测量,然后再进行优化,否则将浪费大量开发时间来进行不必要的优化。

哈!我记得学习排序算法时曾想过“我什么时候才会知道我的数据已经大部分排序了呢?”终于找到应用场景了。我要更深入地研究这个问题。感谢您提供的逐步指导,非常有帮助。 - mpen
@Mark:除非你有非常大的波浪,否则你不需要对水进行排序,因为它是平的,所以你可以一次性渲染所有的水。对于非平坦的水,通常也不需要对其进行排序,只需在所有不透明的物体之后进行渲染即可。当然,如果你要用它来建墙,那就有问题了,需要进行排序。 - SigTerm
1
@Mark:“有人玩Minecraft吗?”不是的。你可能想看看sauerbraten(附带源代码)。 - SigTerm
请问为什么我们需要禁用深度缓冲区写入,即使我们无论如何都是从最远到最近绘制多边形? - parean
@Oroffe 这个答案已经有10年了。因为透明物体仍然可以相互交叉。当你有多个粒子系统时,通常不会同时对世界中的所有粒子进行排序,因为通常你无法在单次渲染中绘制它们全部。这意味着如果你有一个雾粒子系统,并使用深度写入进行排序绘制,它看起来会很好。但是,当你在同一区域添加一个火焰粒子系统时,它将不再看起来很好,并且会被雾粒子留下的深度信息所裁剪。因此,作为一个经验法则,透明度不要进行深度写入。 - SigTerm
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接