几何着色器输出可能用最接近的值填充 Z-Buffer? (DirectX)

Geometry shader output filling Z-Buffer with closest value possible? (DirectX)

我需要在我的 CAD 查看器中使用一定粗细的线条,我发现我应该使用几何着色器来完成此操作。
然后我继续找到了一个几何着色器的演示代码,它以给定的粗细创建线条。

此处:https://github.com/paulhoux/Cinder-Samples/blob/master/GeometryShader/assets/shaders/lines1.geom
在这里讨论:https://forum.libcinder.org/topic/smooth-thick-lines-using-geometry-shader

此着色器是用 OpenGL 编写的,但我将其转换为 DirectX。
我现在遇到的问题是着色器生成的面孔忽略了我的 Z 缓冲区(深度缓冲区),我是否错过了一些非常重要的事情?

我的完整着色器代码:

//Input for the vertex shader
struct VS_IN
{
    float4 pos : POSITION;
    float4 col : COLOR;
};

//Input for the geometry shader, as provided by the vertex shader
struct GS_IN
{
    float4 pos : SV_POSITION;
    float4 col : COLOR;
};

//Input for the pixel shader, as provided by the geometry shader
struct PS_IN
{
    float4 pos : SV_POSITION;
    float4 col : COLOR;
};

//Only used in the vertex shader, to give the vertex its final position on screen
cbuffer viewProj : register (b0)
{
    matrix viewProj;
}

//The vertex shader
GS_IN VS(VS_IN input)
{
    GS_IN output = (GS_IN)0;

    //Should multiply it by a world matrix of identity, so the input vertices should be using world coordinates...
    output.pos = mul(input.pos, viewProj);
    output.col = input.col;

    return output;
};

//The geometry shader

cbuffer windowSize : register (b1)
{
    float2 WIN_SCALE;
}

float MITER_LIMIT = -1;
float THICKNESS = 10;

float2 screen_space(float4 vertex)
{
    return float2(vertex.xy / vertex.w) * WIN_SCALE;
}

[maxvertexcount(7)]
void GS(lineadj GS_IN input[4], inout TriangleStream<PS_IN> OutputStream)
{
    // get the four vertices passed to the shader:
    float2 p0 = screen_space(input[0].pos); // start of previous segment
        float2 p1 = screen_space(input[1].pos); // end of previous segment, start of current segment
        float2 p2 = screen_space(input[2].pos); // end of current segment, start of next segment
        float2 p3 = screen_space(input[3].pos); // end of next segment

        // perform naive culling
        /*
        float2 area = WIN_SCALE * 1.2;
        if (p1.x < -area.x || p1.x > area.x) return;
        if (p1.y < -area.y || p1.y > area.y) return;
        if (p2.x < -area.x || p2.x > area.x) return;
        if (p2.y < -area.y || p2.y > area.y) return;
        */

        // determine the direction of each of the 3 segments (previous, current, next)
        float2 v0 = normalize(p1 - p0);
        float2 v1 = normalize(p2 - p1);
        float2 v2 = normalize(p3 - p2);

        // determine the normal of each of the 3 segments (previous, current, next)
        float2 n0 = float2(-v0.y, v0.x);
        float2 n1 = float2(-v1.y, v1.x);
        float2 n2 = float2(-v2.y, v2.x);

        // determine miter lines by averaging the normals of the 2 segments
        float2 miter_a = normalize(n0 + n1);    // miter at start of current segment
        float2 miter_b = normalize(n1 + n2);    // miter at end of current segment

        // determine the length of the miter by projecting it onto normal and then inverse it
        float length_a = THICKNESS / dot(miter_a, n1);
    float length_b = THICKNESS / dot(miter_b, n1);

    PS_IN output = (PS_IN)0;
    if (dot(v0, v1) < -MITER_LIMIT) {
        miter_a = n1;
        length_a = THICKNESS;

        // close the gap
        if (dot(v0, n1) > 0) {
            output.col = input[1].col;
            output.pos = float4((p1 + THICKNESS * n0) / WIN_SCALE, 0.0, 1.0);
            OutputStream.Append(output);

            output.col = input[1].col;
            output.pos = float4((p1 + THICKNESS * n1) / WIN_SCALE, 0.0, 1.0);
            OutputStream.Append(output);

            output.col = input[1].col;
            output.pos = float4(p1 / WIN_SCALE, 0.0, 1.0);
            OutputStream.Append(output);
        }
        else {
            output.col = input[1].col;
            output.pos = float4((p1 - THICKNESS * n1) / WIN_SCALE, 0.0, 1.0);
            OutputStream.Append(output);

            output.col = input[1].col;
            output.pos = float4((p1 - THICKNESS * n0) / WIN_SCALE, 0.0, 1.0);
            OutputStream.Append(output);

            output.col = input[1].col;
            output.pos = float4(p1 / WIN_SCALE, 0.0, 1.0);
            OutputStream.Append(output);
        }
    }

    if (dot(v1, v2) < -MITER_LIMIT) {
        miter_b = n1;
        length_b = THICKNESS;
    }

    // generate the triangle strip
    output.col = input[1].col;
    output.pos = float4((p1 + length_a * miter_a) / WIN_SCALE, 0.0, 1.0);
    OutputStream.Append(output);

    output.col = input[1].col;
    output.pos = float4((p1 - length_a * miter_a) / WIN_SCALE, 0.0, 1.0);
    OutputStream.Append(output);

    output.col = input[2].col;
    output.pos = float4((p2 + length_b * miter_b) / WIN_SCALE, 0.0, 1.0);
    OutputStream.Append(output);

    output.col = input[2].col;
    output.pos = float4((p2 - length_b * miter_b) / WIN_SCALE, 0.0, 1.0);
    OutputStream.Append(output);
};

//The pixel shader
float4 PS(PS_IN input) : SV_Target
{
    return input.col;
};


technique10 Render
{
    pass P0
    {
        SetVertexShader(CompileShader(vs_4_0, VS()));
        SetGeometryShader(CompileShader(gs_4_0, GS()));
        SetPixelShader(CompileShader(ps_4_0, PS()));
    }
}

如果我禁用几何着色器,我得到:

如果我启用几何着色器,我会得到:

如您所见,粗线突然出现在其他所有内容的前面。

这可以修复吗?我应该使用不同的方法吗?几何着色器真的是解决方案吗?


编辑 1: 我发现它并没有忽略 Z-Buffer,而是用尽可能接近的深度值填充它(调试时为黑色)。为什么会这样?

我认为几何缓冲区是适合您的情况的快速解决方案。

您的错误发生是因为您的几何缓冲区将所有 z 值设置为 0.0,因此任何深度信息都将被丢弃。输出的三角形都在屏幕 space 中的同一深度平面上。要解决这个问题,他们应该继承原始线条的深度值。

请记住,这可能会导致交集问题,例如如果你的线靠近地面,膨胀的线会穿透地面并且看起来比其他线更细。