首页 > 解决方案 > 使用延迟屏幕空间贴花系统的边界检查问题

问题描述

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

一个红色阴影立方体被绘制在场景的顶部,该立方体与深度掩码设置为 false 的墙相一致。图片链接(无界立方体):https ://gyazo.com/8487947bd4afb08d8d0431551057ad6f

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

问题是边界没有正常工作,立方体完全消失了。

潜在故障

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

贴花着色器代码

顶点着色器

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

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

// Clipspace Position    
PositionCS = projection*PositionVS;

gl_Position = PositionCS;    

片段着色器

// 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 中完成,并作为统一发送到片段着色器。farClip 是到相机远平面的距离(此处设置为 3000)。gDepth 是 Gbuffer 的深度纹理。

问题

与立方体一致的墙壁部分应该是红色的,如下图所示,显然不是。

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

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

标签: pythonglslcoordinate-transformation

解决方案


你混淆了粉笔和奶酪。PositionCS是剪辑空间位置,可以通过透视除法转换为标准化设备空间位置:

vec2 ndcPos = PositionCS.xyz / PositionCS.w;

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

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相同的视图矩阵和投影矩阵生成的,则您可以用深度缓冲区 ( ) 中的相应 NDC z 坐标替换,并且该点仍然在同一视图射线上。并且是同一参考系统中的可比较值。ndcPosPositionCSndcPos.zsampleNdcZ
ndcPos.zsampleNdcZ

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

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

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;


推荐阅读