如何在 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),并且该范围之外的深度值将是夹紧。由于 dis
和 tpos.z
都在 视图 space 中,这只是一个旋转和平移的世界 space,它们可能是远大于 1。因此,就深度缓冲区而言,所有几何体的深度正好为 1(因为它被夹紧),这类似于首先不使用深度缓冲区。
我正在使用 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),并且该范围之外的深度值将是夹紧。由于 dis
和 tpos.z
都在 视图 space 中,这只是一个旋转和平移的世界 space,它们可能是远大于 1。因此,就深度缓冲区而言,所有几何体的深度正好为 1(因为它被夹紧),这类似于首先不使用深度缓冲区。