我正在尝试使用几何着色器渲染任意宽线(在屏幕空间中)。起初看起来一切都很好,但在某些视图位置上,线条渲染不正确:
左侧的图像呈现正确的渲染(正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;
}
答案 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)
今天我自己找到了答案。我完全不明白,但这解决了这个问题。
当线顶点超出为场景定义的投影矩阵的近平面时(在我的情况下,三条线的所有结束顶点),会出现问题。解决方案是手动剪切视锥体内的线顶点(这样顶点不能超出近平面!)。
当ndc0
和ndc1
超出视锥体时会发生什么?看图像,似乎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();
}