我在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()));
}
}
如果我禁用几何着色器,我会得到:
如果我启用了几何着色器,我得到:
正如你所看到的,粗线突然出现在其他一切的前面。
这可以修复吗?我应该使用不同的方法吗?几何着色器真的是解决方案吗?
编辑1:我发现它并没有忽略Z-Buffer,而是用最接近的深度值填充它(调试中为黑色)。为什么会这样?
答案 0 :(得分:1)
我认为几何缓冲区对于您的情况来说是一个很好的快速解决方案。
您的错误发生是因为几何缓冲区将所有z值设置为0.0,因此会删除任何深度信息。输出的三角形都在屏幕空间中的相同深度平面上。要解决这个问题,他们应该从原始行继承深度值。
请注意,这可能会导致交叉问题,例如:如果你的线路靠近地面,膨胀的线路会穿透地面,看起来比其他线条更薄。