将纹理数据写入深度缓冲区

6
我正在尝试实现这篇文章中描述的技术:使用深度合成图像
这个想法是利用已有的纹理(从图像加载)作为深度掩码,基本上是伪造3D效果。
我面临的问题是,在OpenglES中没有glDrawPixels可用。在iPhone上有没有一种方法可以实现同样的效果?
1个回答

7
在OpenGL ES中,深度缓冲比你想象的更加模糊;不仅glDrawPixels不存在,而且GLSL中的gl_FragDepth也已被删除。因此,您不能像推送颜色那样编写自定义片段着色器将值写入深度缓冲区。
最明显的解决方案是将深度信息打包到纹理中,并使用自定义片段着色器对生成的片段和从提供的纹理中查找的片段进行深度比较。只有生成的片段更接近才允许继续进行。正常的深度缓冲区将捕获其他遮挡情况,理论上,您可以使用帧缓冲对象在GPU上创建深度纹理,这将给您一个完整的GPU往返,但它与您的问题没有直接关系。
缺点是绘制会消耗额外的纹理单元,而且纹理使用整数组件。
编辑:为了简化示例,假设您将所有深度信息打包到纹理的红色通道中。这将给您一个非常低的精度深度缓冲区,但为了保持清晰,您可以编写一个快速的片段着色器,例如:
void main()
{
    // write a value to the depth map
    gl_FragColor = vec4(gl_FragCoord.w, 0.0, 0.0, 1.0);
}

为了在红色通道中存储深度,您部分地重新创建了旧的深度纹理扩展。这样,您将拥有一幅图像,靠近物体的像素会有更亮的红色,远离物体的像素则会有更暗的红色。我认为,在您的问题中,您实际上需要从磁盘加载此图像。
然后,在未来的片元着色器中使用该纹理时,您可以执行以下操作:
uniform sampler2D depthMap;

void main()
{
    // read a value from the depth map
    lowp vec3 colourFromDepthMap = texture2D(depthMap, gl_FragCoord.xy);

    // discard the current fragment if it is less close than the stored value
    if(colourFromDepthMap.r > gl_FragCoord.w) discard;

    ... set gl_FragColor appropriately otherwise ...
}

编辑2:你可以在这里看到一种更智能的从深度映射到RGBA值的方法。直接与该文档相关的是,iPad或第三代iPhone肯定不支持OES_depth_texture。我还没有在其他地方进行完整的测试。


说了这么多,GL_OES_depth_texture 看起来确实存在于我现在手头的 iPhone 4 上。我不确定它是否是一个操作系统版本的问题,但我的理解是硬件与 iPad 几乎完全相同,所以我无法解释。也许值得检查一下您想支持的特定 iOS 设备和操作系统版本。 - Tommy
我可能会将这个问题作为一个单独的问题来问,但是当使用片段着色器写入纹理以模拟gl_FragDepth时,如何防止覆盖可能更接近观察者的片段与可能更远离观察者的片段?似乎我们没有能力查询正在写入的纹理,以查看该位置是否已经有东西被写入。 - Brad Larson
第一个片段着色器代码示例中,那不应该是 gl_FragCoord.z 吗? - Jonas Sourlier
@BradLarson,你有没有找到你问题的答案? - Jonas Sourlier
1
@cheeesus - 是的,我做到了。iOS 6.0引入了GL_EXT_shader_framebuffer_fetch扩展,它允许你读取片段中当前颜色的值。使用这个扩展,你可以读取之前片段的颜色,该颜色编码深度,将其与当前片段的深度进行比较,并只输出更接近观察者的深度。在iOS 6之前,我使用了最大混合模式和桶填充方法来处理颜色组件,从而将深度动态范围从256扩展到1024。现在,我可以名义上获得32位动态范围,尽管实际上高精度限制你只能使用16位。 - Brad Larson
显示剩余2条评论

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