5

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

正确渲染 不正确的渲染

左边的图像呈现了正确的渲染(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;
}
4

3 回答 3

8

你的错误在于:

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 的信息,从而使硬件剪辑器失谐,将其从 3D 剪辑器降级为 2D 剪辑器。

于 2014-05-26T03:19:28.580 回答
2

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

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

当他们离开视锥体时ndc0会发生什么?ndc1查看图像,似乎 XY 分量的符号发生了变化(在它们在剪辑空间中变换之后!):这意味着 W 坐标与正常坐标相反,不是吗?

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

于 2012-10-06T21:13:49.710 回答
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();
}
于 2013-10-14T04:58:18.503 回答