如何在 HLSL 着色器中进行我自己的深度缓冲?

How to do my own depth buffering in an HLSL shader?

我正在使用 HLSL 和 DirectX 在 Unity 中制作游戏。在我的游戏中,有些情况下会出现一个大三角形,其顶点在正向方向上靠近相机,但在横向和垂直方向上远离相机。这样做的效果是那个三角形的碎片离你很近,覆盖了你的大部分视野。我希望这些三角形出现在其他三角形后面。我关于如何实现这一点的想法不是使用顶点的 z 坐标进行深度缓冲——我相信这是 DirectX 默认所做的——是我计算每个顶点到相机的距离,像往常一样插入该距离,并将其用于深度缓冲。这样如果一个三角形正对着我的脸,但是顶点很远,那么三角形中间的碎片也会很远

只有当我尝试这个时,它才行不通。三角形似乎以随机顺序重叠。这是我的代码草图:

            struct appdata
            {
                ...
            };

            struct v2f
            {
                ... // other members
                float4 vertex : SV_Position;
                float distance : COLOR1;
            };

            struct target
            {
                fixed4 col : SV_Target;
                float depth : SV_Depth;
            };

            v2f vert (appdata v)
            {
                v2f o;

                // math math math
                // here we calculate the position in camera space `tpos`,
                // and `dis` is the length of `tpos`
                // UNITY_MATRIX_P is the built in projection matrix

                o.vertex = mul(UNITY_MATRIX_P, float4(tpos, 1.0f)); // projected space
                o.distance = dis;

                return o;
            }

            target frag (v2f i)
            {
                target o;
                o.depth = i.distance;
                o.col = _Color * i.bright;
                return o;
            }

我也尝试过设置 o.distance = -dis,但这也不起作用。我也试过设置 o.distance = tpos.z 以及 -tpos.z 并且那些也没有用,这让我感到困惑,因为我认为这基本上是光栅化器默认情况下所做的。需要明确的是,当我说这些不起作用时,着色器会运行,但选择哪个三角形更近似乎是随机的。那么有谁知道出了什么问题,或者正确的方法是什么?

另外,很抱歉缺少一个最小的工作示例,我的着色器太依赖于我的其他代码了。希望我的问题足够概念化,没关系。

您对硬件默认情况的解释是错误的。光栅化器默认使用的 'depth' 值是 o.vertex.z / o.vertex.w 的 per-fragment 插值,它是 屏幕 space 中插值的归一化片段深度值(也称为投影 space)。这等于片段到相机的距离,根据投影矩阵中指定的近远平面进行归一化,并考虑了透视投影的非线性缩放。

除此之外,您的方法不起作用的原因是因为深度缓冲区期望值在 0 到 1 的范围内(或者在 OpenGL 的情况下为 -1 到 1),并且该范围之外的深度值将是夹紧。由于 distpos.z 都在 视图 space 中,这只是一个旋转和平移的世界 space,它们可能是远大于 1。因此,就深度缓冲区而言,所有几何体的深度正好为 1(因为它被夹紧),这类似于首先不使用深度缓冲区。