为什么我需要 depthBuffer 来使用 RenderTexture?

Max*_*kov 5 c# camera render unity-game-engine depth-buffer

我认为我不太了解 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;
Run Code Online (Sandbox Code Playgroud)

但是,如果 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);
Run Code Online (Sandbox Code Playgroud)

这就是我得到的:

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)
Run Code Online (Sandbox Code Playgroud)

为什么它不能混合来自屏幕的深度缓冲区和来自 RenderTexture 的颜色缓冲区?

v.o*_*dou 7

我不太了解 Unity,但我了解它们的基础层,如果它们可以映射 D3D9、D3d10 和 OpenGL,那么它们的抽象必须使用一个共同点。

在这种情况下,D3D10 是最有限制的,您不能在不同大小的渲染目标之间共享深度表面。如果您的屏幕大小和渲染目标各处都相同,那么您确实可以将唯一的深度缓冲区绑定到不同的渲染目标。

深度缓冲区并不是严格意义上必需的,就像您所观察到的那样,您可以在没有深度缓冲区的情况下进行渲染,但结果只是按照绘制命令发出的顺序进行渲染。(绘制调用 = D3D 中的 DrawPrimitive,或 glDrawBuffers 等)甚至可以保证在规范中三角形级别的顺序是一致的,即使图形卡非常并行,它们也拒绝并行发出图元为了通过一次绘制调用的不同运行来保证绘制的一致性。

如果您使用深度缓冲区,则在较低深度绘制的对象之后发生绘制调用的对象将覆盖这些接近的对象(在视图空间中)并给出错误的结果,深度缓冲区有助于逐像素丢弃,对象的像素,其深度(在视图空间中)比之前已在此像素处渲染的对象更深,但深度更接近。

绑定深度缓冲区也有助于提高性能,因为如果一个块中的每个像素都具有特定值的最小深度,则图元光栅化器在退出顶点缓冲区后知道图元的整个部分永远不会在此块上渲染,并丢弃整个街区。这称为早期 Z 剔除,对性能有巨大帮助。因此,最好保持深度缓冲区处于插入状态。

相机在低级图形理论中没有概念,它只是由视图矩阵表示,该视图矩阵是应用于整个世界的逆变换,将世界从世界空间转移到视图空间,作为任何单个顶点变换计算的一部分。这就是为什么在经典的顶点着色器中,位置是从对象空间(在顶点缓冲流中)获取的,然后乘以对象矩阵变换,然后乘以视图矩阵变换,然后乘以投影矩阵变换,然后光栅化器除一切都用“w”来划分视角。

也就是说,通过利用这种管道行为,您可以“创建”相机的概念。Unity 必须通过公开相机类来抽象所有这些。也许相机类甚至有一个“纹理目标”成员来解释该相机的渲染将存储在哪里。

是的,渲染是直接对指定的渲染纹理进行的,没有中间的前缓冲区或其他什么,渲染目标是硬件支持的功能,并且在渲染结束时不需要复制。

渲染目标本身就是一个完整的配置,由于硬件多重采样,它实际上可能绑定所需分辨率大小的多个缓冲区。

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

我希望这有帮助