使用延迟的屏幕空间贴花系统进行边界检查问题

时间:2019-01-15 15:01:28

标签: python glsl coordinate-transformation

我在一篇名为“使用递延屏幕空间贴花的其他材质上绘制东西”,链接:http://martindevans.me/game-development/2015/02/27/Drawing-Stuff-On-Other-Stuff-With-Deferred-Screenspace-Decals/之后,尝试使用OpenGL实现递延屏幕空间贴花系统。

在场景的顶部绘制了一个红色阴影的立方体,该立方体与将depthmask设置为false的墙保持一致。 图片链接(无限制的多维数据集):https://gyazo.com/8487947bd4afb08d8d0431551057ad6f

墙的深度缓冲区和一些顶点着色器输出用于在立方体的尺寸范围内计算墙的对象空间位置。边界检查可确保立方体的每个像素在对象的空间位置之外墙被丢弃了。

问题在于边界无法正常工作,立方体完全消失。

潜在故障

我已经通过在lighitingpass中可视化深度缓冲区来检查深度缓冲区是否正常工作,并且似乎工作正常。 depthbuffer存储在gbuffer的颜色附件中,其floatsize为GL_RGB32F。 图像链接(Ligtingpass深度缓冲墙的可视化远距离墙):https://gyazo.com/69920a532ca27aa9f57478cb57e0c84c

贴图着色器代码

VertexShader

// Vertex positions    
vec4 InputPosition = vec4(aPos, 1);   

// Viewspace Position    
PositionVS = view* model* InputPosition;    

// Clipspace Position    
PositionCS = projection*PositionVS;

gl_Position = PositionCS;    

FragmentShader

// Position on the screen    
vec2 screenPos = PositionCS.xy / PositionCS.w;

// Convert into a texture coordinate   
vec2 texCoord = vec2((1 + screenPos.x) / 2 + (0.5 / resolution.x), (1 - 
screenPos.y) / 2 + (0.5 / resolution.y));

// Sampled value from depth buffer   
vec4 sampledDepth = texture(gDepth, texCoord);

// View-direction   
vec3 viewRay = PositionVS.xyz * (farClip / -PositionVS.z);

// Wallposition in view-space   
vec3 viewPosition = viewRay*sampledDepth.z;

// Transformation from view-space to world-space   
vec3 WorldPos = (invView*vec4(viewPosition, 1)).xyz;

// Transformation from world-space to object-space   
vec3 objectPosition = (invModel*vec4(WorldPos, 1)).xyz;

// Bounds check, discard pixels outside the wall in object-space    
if (abs(objectPosition.x) > 0.5) discard;    
else if (abs(objectPosition.y) > 0.5) discard;    
else if (abs(objectPosition.z) > 0.5) discard;    

// Color to Gbuffer    
gAlbedoSpec = vec4(1, 0, 0, 1);

代码说明

invView和invModel分别是视图对象和模型对象的逆。矩阵逆计算在CPU中完成,并作为统一形式发送到fragmenthader。 farClip是到相机远平面的距离(此处设置为3000)。 gDepth是Gbuffer的深度纹理。

问题

与立方体匹配的那部分墙应涂成红色阴影,如下图所示。

图像链接(带边界的立方体):https://gyazo.com/ab6d0db2483a969db932d2480a5acd08

我的猜测是问题在于视图空间位置如何转换为对象空间位置,但我无法弄清楚!

1 个答案:

答案 0 :(得分:0)

您在混淆粉笔和奶酪。 PositionCS是剪辑空间位置,可以通过Perspective divide转换为规范化的设备空间位置:

vec2 ndcPos = PositionCS.xyz / PositionCS.w;

sampledDepth是深度值(默认为[0,1]范围),可以通过读取“红色”颜色通道(.r.x)获得从深度缓冲区纹理开始。可以通过depth*2.0-1.0将深度转换为归一化的设备空间Z坐标:

vec2 texCoord = ndcPos.xy * 0.5 + 0.5; 
   // (+ 0.5/resolution.xy;) is not necessary if texture filter is GL_NEAREST

float sampledDepth = texture(gDepth, texCoord).x;

float sampleNdcZ = sampledDepth * 2.0 - 1.0;

在透视投影和规范化的设备空间中,具有相同x和y坐标的所有点都在同一条射线上,该射线始于视图位置。

这意味着,如果深度缓冲区gDepth是用与ndcPosPositionCS)相同的视图矩阵和投影矩阵生成的,则可以用相应的ndcPos.z代替NDC Z坐标形成深度缓冲区(sampleNdcZ),并且该点仍在同一视线上。
ndcPos.zsampleNdcZ是同一参考系统中的可比较值。

vec3 ndcSample = vec3(ndcPos.xy, sampleNdcZ);

可以通过反投影矩阵和透视图划分将该坐标转换为视图空间坐标。
如果将同一视线上的NDC点转换到视线空间,则XY坐标将不同。请注意,由于(* 1/.w),变换不是线性的。另请参见OpenGL - Mouse coordinates to Space coordinates

uniform mat4 invProj; // = inverse(projection)
vec4 hViewPos     = invProj * vec4(ndcSample, 1.0);
vec3 viewPosition = hViewPos.xyz / hViewPos.w;

这可以通过逆视图矩阵进一步转换为世界空间,并通过逆模型矩阵转换为对象空间:

vec3 WorldPos       = (invView * vec4(viewPosition, 1.0)).xyz;
vec3 objectPosition = (invModel * vec4(WorldPos, 1.0)).xyz;