为什么使用RenderTexture时需要深度缓冲区?

7

我认为我对Unity渲染引擎并不十分了解。

我使用RenderTexture生成屏幕截图(之后需要进行管理):

    screenshotRenderTexture = new RenderTexture(screenshot.width, screenshot.height, depthBufferBits, RenderTextureFormat.Default);
    screenshotRenderTexture.Create();

    RenderTexture currentRenderTexture = RenderTexture.active;
    RenderTexture.active = screenshotRenderTexture;

    Camera[] cams = Camera.allCameras;

    System.Array.Sort(
            cams,
            delegate(Camera cam1, Camera cam2)
            {
                // It's easier than write float to int conversion that won't floor
                // depth deltas under 1 to zero and will correctly work with NaNs
                if (cam1.depth < cam2.depth)
                    return -1;
                else if (cam1.depth > cam2.depth)
                    return 1;
                else return 0;
            }
        );

    foreach(Camera cam in cams)
    {
        cam.targetTexture = screenshotRenderTexture;          
        cam.Render();
        cam.targetTexture = null;
    }

    screenshot.ReadPixels(new Rect(0, 0, textureWidth, textureHeight), 0, 0);
    screenshot.Apply();

    RenderTexture.active = currentRenderTexture;

然而,如果depthBufferBits为0,则渲染结果会出现各种z缓冲区错误(以错误的顺序渲染的内容)。
我大致了解什么是深度缓冲区。然而,我不明白的是——如果RenderTexture用于合并单个摄像机的渲染结果,为什么需要其中的深度缓冲区?这些抽象如何工作,确切地说——摄像机是否自己创建图像,然后将其提供给RenderTexture,还是摄像机使用RenderTexture的深度缓冲区?由于我遇到的问题(在同一个摄像机内按错误顺序渲染的内容),似乎是后者,但同时它又有点违反了C#级别上的这些抽象结构的常识。
最后,我能否在此基础上使用用于正常渲染的默认深度缓冲区?因为移动设备上每像素16位相当痛苦。
更新:
以下是我的尝试:
    screenshotRenderTexture = new RenderTexture(
            screenshot.width,
            screenshot.height,
            0,
            RenderTextureFormat.Default
        );
    screenshotRenderTexture.Create();

    RenderBuffer currentColorBuffer = Graphics.activeColorBuffer;
    Graphics.SetRenderTarget(screenshotRenderTexture.colorBuffer, Graphics.activeDepthBuffer);
    yield return new WaitForEndOfFrame();
    Graphics.SetRenderTarget(currentColorBuffer, Graphics.activeDepthBuffer);

以下是我得到的内容:

SetRenderTarget can only mix color & depth buffers from RenderTextures. You're trying to set depth buffer from the screen.
UnityEngine.Graphics:SetRenderTarget(RenderBuffer, RenderBuffer)
<ScreenshotTaking>c__Iterator21:MoveNext() (at Assets/Scripts/Managers/ScreenshotManager.cs:126)

为什么不能混合屏幕的深度缓冲和RenderTexture的颜色缓冲?
1个回答

7
我对Unity不是很熟悉,但我了解它们的基础层,如果它们可以在D3D9、D3D10和OpenGL之间进行映射,那么它们的抽象必须使用一个共同的基础。
在这种情况下,D3D10最为限制,在不同大小的渲染目标之间不能共享深度表面。如果您在整个屏幕和渲染目标上都有相同的大小,则确实可以将唯一深度缓冲绑定到不同的渲染目标上。
深度缓冲不是严格意义上必要的,就像您观察到的那样,您可以在没有深度缓冲的情况下进行渲染,但是结果只是按照发出绘图命令的顺序进行渲染。(draw call = D3D中的DrawPrimitive,或者glDrawBuffers等)甚至可以保证规范下的三角形级别的顺序是一致的,即使图形卡非常并行,也会拒绝为了通过一个draw call的不同运行的一致性而并行地发出原语。
如果您使用深度缓冲,那些发生在已经以较低深度被绘制的对象之后的对象将覆盖这些接近的对象(在视图空间中),并给出错误结果,深度缓冲帮助逐像素丢弃一个物体的像素,该物体的深度(在视图空间中)比在它之前已经渲染过这个像素的某个东西更深,但是深度更浅。
绑定深度缓冲还有助于性能,因为如果一个块中的每个像素都具有特定值的最小深度,则基元光栅化程序在顶点缓冲器退出后就知道整个基元的一部分永远不会在此块上进行渲染,并且完全丢弃整个块。这称为早期Z剔除,对性能非常有帮助。因此,保持深度缓冲被连接是非常可取的。
在低级别图形理论中,相机没有概念,它只是由一个视图矩阵表示,该矩阵是应用于整个世界的反向变换,以将世界从世界空间转移到视图空间作为任何单个顶点变换计算的一部分。这就是为什么在经典的顶点着色器中,位置是从对象空间(在顶点缓冲流中)获取的,然后乘以一个对象矩阵变换,然后乘以一个视图矩阵变换,然后再乘以一个投影矩阵变换,最后光栅化器通过'div w'进行透视除法。
正是通过利用这个管道行为,你"创建"了相机的概念。Unity必须通过公开摄像机类来抽象所有这些东西。也许摄像机类甚至有一个"纹理目标"成员,以解释这个相机的渲染将存储在哪里。
是的,渲染直接到指定的渲染纹理中,没有中间的前缓冲或其他什么,渲染目标是硬件支持的特性,并且不需要在渲染结束时进行复制。

有颜色缓冲区,例如MSAA4x中的4个RGBA表面,深度缓冲区通常是固定点24位表示,以及8位模板表面。所有这些表面都代表渲染目标配置,并且对于渲染非常必要。

希望这能帮到你。


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