如何将global_invocation_id转换为裁剪空间坐标?(深度转世界坐标)
正确的GID到裁剪空间转换方案
结论:第三种方案是正确的,也是图形编程中像素索引转UV坐标的标准做法。
方案分析
1. 初步实现(存在偏移问题)
let uv = vec2f(gid.xy) / screen_size; let clip_space = vec2(uv.x, 1.0 - uv.y) * 2.0 - 1.0;
该方案将像素索引直接作为坐标除以屏幕尺寸,得到的是像素左下角的UV坐标,而非像素中心。对于右下角像素(screen_width-1, screen_height-1),计算出的UV是((w-1)/w, (h-1)/h),转换到裁剪空间后无法匹配对应坐标系的右下角位置,且采样深度时会和深度缓冲区存储的像素中心深度值不匹配,导致坐标偏移。
2. 第二种方案(边缘错误+非中心对齐)
let uv = vec2f(gid.xy) / (screen_size - vec2f(1.0));
虽然该方案能让右下角像素的UV达到(1,1),但计算的是像素右下角的坐标,同样不是像素中心,会导致深度采样偏差。此外当屏幕尺寸为1时,会出现除以0的错误,存在兼容性隐患。
3. 第三种方案(正确的像素中心对齐)
let uv = (vec2f(gid.xy) + vec2f(0.5)) / screen_size; let clip_space = vec2(uv.x, 1.0 - uv.y) * 2.0 - 1.0;
这是图形编程中的标准做法:
gid.xy是像素的索引,每个像素的中心坐标为gid.xy + 0.5- 除以屏幕尺寸后得到像素中心的UV坐标,与深度缓冲区中存储的每个texel对应的深度值完全匹配
- 转换到裁剪空间后,左上角像素中心对应裁剪空间的
(-1, 1),右下角像素中心对应(1, -1)(符合WebGPU的NDC坐标系规则),确保坐标转换的准确性
完整的深度转世界坐标代码
let depth = textureLoad(depth_buffer, gid.xy, 0); let uv = (vec2f(gid.xy) + vec2f(0.5)) / screen_size; let clip_space = vec2(uv.x, 1.0 - uv.y) * 2.0 - 1.0; let world_space_hc = inverse_view_proj_matrix * vec4(clip_space, depth, 1.0); let world_space = world_space_hc.xyz / world_space_hc.w;
内容的提问来源于stack exchange,提问作者Lukas Kalbertodt
相关产品推荐
相关产品推荐

