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

时间:2015-02-10 17:25:34

标签: c++ directx shader hlsl

我在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-Buffer(Depth-Buffer),我是否错过了一些非常重要的东西?

我的完整着色器代码:

//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()));
    }
}

如果我禁用几何着色器,我会得到:
Without Geometry Shader
如果我启用了几何着色器,我得到:
With Geometry Shader
正如你所看到的,粗线突然出现在其他一切的前面。

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


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

1 个答案:

答案 0 :(得分:1)

我认为几何缓冲区对于您的情况来说是一个很好的快速解决方案。

您的错误发生是因为几何缓冲区将所有z值设置为0.0,因此会删除任何深度信息。输出的三角形都在屏幕空间中的相同深度平面上。要解决这个问题,他们应该从原始行继承深度值。

请注意,这可能会导致交叉问题,例如:如果你的线路靠近地面,膨胀的线路会穿透地面,看起来比其他线条更薄。