几何着色器输出可能用最接近的值填充 Z-Buffer? (DirectX)
Geometry shader output filling Z-Buffer with closest value possible? (DirectX)
我需要在我的 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 缓冲区(深度缓冲区),我是否错过了一些非常重要的事情?
我的完整着色器代码:
//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,而是用尽可能接近的深度值填充它(调试时为黑色)。为什么会这样?
我认为几何缓冲区是适合您的情况的快速解决方案。
您的错误发生是因为您的几何缓冲区将所有 z 值设置为 0.0,因此任何深度信息都将被丢弃。输出的三角形都在屏幕 space 中的同一深度平面上。要解决这个问题,他们应该继承原始线条的深度值。
请记住,这可能会导致交集问题,例如如果你的线靠近地面,膨胀的线会穿透地面并且看起来比其他线更细。
我需要在我的 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 缓冲区(深度缓冲区),我是否错过了一些非常重要的事情?
我的完整着色器代码:
//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,而是用尽可能接近的深度值填充它(调试时为黑色)。为什么会这样?
我认为几何缓冲区是适合您的情况的快速解决方案。
您的错误发生是因为您的几何缓冲区将所有 z 值设置为 0.0,因此任何深度信息都将被丢弃。输出的三角形都在屏幕 space 中的同一深度平面上。要解决这个问题,他们应该继承原始线条的深度值。
请记住,这可能会导致交集问题,例如如果你的线靠近地面,膨胀的线会穿透地面并且看起来比其他线更细。