5

ジオメトリシェーダーを使用して、(画面スペースで)任意の幅の広い線をレンダリングしようとしています。最初はすべて問題ないように見えますが、特定のビュー位置では、線が正しくレンダリングされません。

正しいレンダリング 正しくないレンダリング

左側の画像は正しいレンダリングを示しています(正のX、Y、Z軸に3本の線、幅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

2

今日、私は自分で答えを見つけました。よくわかりませんが、これで問題は解決しました。

線の頂点がシーンに定義された射影行列の近平面を超えると問題が発生します (私の場合、3 つの線のすべての終了頂点)。解決策は、ビュー フラスタム内の線の頂点を手動でクリップすることです (この方法では、頂点がニア プレーンを超えることはありません!)。

それらが視錐台の外にあるとき、それらはどう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 に答える