几何着色器中的宽线表现奇怪

时间:2012-10-06 08:41:32

标签: opengl glsl geometry-shader

我正在尝试使用几何着色器渲染任意宽线(在屏幕空间中)。起初看起来一切都很好,但在某些视图位置上,线条渲染不正确:

Correct rendering Incorrect rendering

左侧的图像呈现正确的渲染(正X,Y和Z轴上的三条线,2像素宽)。

当相机移动到原点附近(实际上靠近线条)时,线条会像右图像一样呈现。着色器看起来很简单,我不明白我的GPU上发生了什么:

--- Vertex Shader

#version 410 core

// Modelview-projection matrix
uniform mat4 ds_ModelViewProjection;
// Vertex position
in vec4 ds_Position;
// Vertex color
in vec4 ds_Color;

// Processed vertex color
out vec4 ds_VertexColor;

void main()
{    
    gl_Position = ds_ModelViewProjection * ds_Position;

    ds_VertexColor = ds_Color;
}

--- Geometry Shader

    #version 410 core

    // Viewport size, in pixels
uniform vec2 ds_Viewport;
// Line width, in pixels
uniform float ds_LineWidth = 2.0;
// Processed vertex color (from VS, in clip space)
in vec4 ds_VertexColor[2];
// Processed primitive vertex color
out vec4 ds_GeoColor;

layout (lines) in;
layout (triangle_strip, max_vertices = 4) out;

void main()
{
    vec3 ndc0 = gl_in[0].gl_Position.xyz / gl_in[0].gl_Position.w;
    vec3 ndc1 = gl_in[1].gl_Position.xyz / gl_in[1].gl_Position.w;

    vec2 lineScreenForward = normalize(ndc1.xy - ndc0.xy);
    vec2 lineScreenRight = vec2(-lineScreenForward.y, lineScreenForward.x);
    vec2 lineScreenOffset = (vec2(ds_LineWidth) / ds_ViewportSize) * lineScreenRight;

    gl_Position = vec4(ndc0.xy + lineScreenOffset, ndc0.z, 1.0);
    ds_GeoColor = ds_VertexColor[0];
    EmitVertex();

    gl_Position = vec4(ndc0.xy - lineScreenOffset, ndc0.z, 1.0);
    ds_GeoColor = ds_VertexColor[0];
    EmitVertex();

    gl_Position = vec4(ndc1.xy + lineScreenOffset, ndc1.z, 1.0);
    ds_GeoColor = ds_VertexColor[1];
    EmitVertex();

    gl_Position = vec4(ndc1.xy - lineScreenOffset, ndc1.z, 1.0);
    ds_GeoColor = ds_VertexColor[1];
    EmitVertex();

    EndPrimitive();
}

--- Fragment Shader

// Processed primitive vertex color
in vec4 ds_GeoColor;

// The fragment color.
out vec4 ds_FragColor;

void main()
{
        ds_FragColor = ds_GeoColor;
}

3 个答案:

答案 0 :(得分:5)

你的错误在于:

gl_Position = vec4(ndc0.xy + lineScreenOffset, ndc0.z, 1.0 /* WRONG */);

修复它:

vec4 cpos = gl_in[0].gl_Position;
gl_Position = vec4(cpos.xy + lineScreenOffset*cpos.w, cpos.z, cpos.w);

你所做的是:丢失关于W的信息,从而使HW裁剪器失谐,将其从3D裁剪器降级为2D裁剪器。

答案 1 :(得分:2)

今天我自己找到了答案。我完全不明白,但这解决了这个问题。

当线顶点超出为场景定义的投影矩阵的近平面时(在我的情况下,三条线的所有结束顶点),会出现问题。解决方案是手动剪切视锥体内的线顶点(这样顶点不能超出近平面!)。

ndc0ndc1超出视锥体时会发生什么?看图像,似乎XY组件的符号发生了变化(在剪辑空间中变换后!):这意味着W坐标与正常坐标相反,不是吗?

如果没有几何着色器,光栅化器将负责在视锥体之外剪切这些基元,但由于我已经引入了几何着色器,我需要自己计算这些结果。有人可以建议我关于这个问题的一些链接吗?

答案 2 :(得分:0)

我遇到了类似的问题,我试图将顶点的法线绘制为彩色线条。我绘制法线的方式是我将所有顶点绘制为点,然后使用GS将每个顶点展开为一条线。 GS很简单,我发现在整个屏幕上都有随机错误的线条。然后我将这一行添加到GS中(由下面的注释标记),问题得到解决。似乎问题是因为线的一端在截头锥体内,而另一端在外面,所以我最终会在整个屏幕上运行线条。

// Draw normal of a vertex by expanding a vertex into a line
[maxvertexcount(2)]
void GSExpand2( point PointVertex points[ 1 ], inout LineStream< PointInterpolants > stream )
{
    PointInterpolants v;

    float4 pos0 = mul(float4(points[0].pos, 1), g_viewproj);
    pos0 /= pos0.w;

    float4 pos1 = mul(float4(points[0].pos + points[0].normal * 0.1f, 1), g_viewproj);
    pos1 /= pos1.w;

    // seems like I need to manually clip the lines, otherwise I end up with incorrect lines running across the entire screen
    if ( pos0.z < 0 || pos1.z < 0 || pos0.z > 1 || pos1.z > 1 ) 
        return;

    v.color = float3( 0, 0, 1 );
    v.pos = pos0;
    stream.Append( v );

    v.color = float3( 1, 0, 0 );
    v.pos = pos1;
    stream.Append( v );

    stream.RestartStrip();
}