为什么对于同一个投影点,采样得到的遮挡深度与计算得到的深度相差很大?
我尝试用如下方式为一个二维顶视图游戏实现三维点光照:
- 我有高度图多边形和纹理,会被渲染到不同的渲染层,并通过高度图画布捕获。
- 接着我从光源的视角渲染3D场景,使用高度图视口纹理作为地形高度图,将深度写入像素颜色值。
- 接着我渲染全屏的后处理覆盖层,在其中计算2D输入顶点的3D位置,将其投影到3D点光源相机裁剪空间,并使用与渲染3D相机视口时相同的公式来计算深度。
- 接着对从3D视口获得的深度纹理进行采样,并与计算得到的深度进行比较以实现阴影和光照映射。思路是如果差异大于eps,则该点被遮挡;未遮挡的位置应绘制点光源纹理叠加。
问题在于我在任何地方都会得到这个差值,即使高度图的高度为0,且前方没有遮挡物。造成深度值发散的原因可能是什么?

点光源深度着色器:
shader_type spatial;
render_mode unshaded;
uniform sampler2D heightmap : source_color, filter_linear, repeat_disable;
uniform float height_scale : hint_range(0.0, 2000.0) = 120.0;
uniform mat4 flashlight_projection;
varying vec3 v_world_position;
void vertex() {
VERTEX.y = height_scale * texture(heightmap, UV).r;
v_world_position = VERTEX;
}
void fragment() {
vec4 clip_space = flashlight_projection * vec4(v_world_position, 1.0);
float depth_01 = -(clip_space.z / clip_space.w) * 0.5 + 0.5;
ALBEDO = vec3(depth_01);
}
后处理覆盖着色器:
shader_type canvas_item;
render_mode unshaded;
uniform sampler2D flashlight_depth : filter_nearest;
uniform mat4 flashlight_projection;
uniform vec2 camera_position;
uniform vec2 zoom;
uniform vec2 viewport_size;
vec2 get_global_from_UV(vec2 uv) {
return camera_position + (uv - 0.5) * viewport_size / zoom;
}
void fragment() {
vec2 position = get_global_from_UV(SCREEN_UV);
vec2 vector_from_camera = position - camera_position;
vec4 clip_space = flashlight_projection * vec4(vector_from_camera.x, 0.0, vector_from_camera.y, 1.0);
if (clip_space.w <= 1e-5) COLOR.a = 0.0;
vec2 depth_map_norm_uv = clip_space.xy / clip_space.w;
vec2 flashlight_depth_texture_uv = depth_map_norm_uv * 0.5 + 0.5;
flashlight_depth_texture_uv.y = 1.0 - flashlight_depth_texture_uv.y;
if (any(lessThan(flashlight_depth_texture_uv, vec2(0.0))) ||
any(greaterThan(flashlight_depth_texture_uv, vec2(1.0))))
COLOR.a = 0.0;
float occluder_distance = texture(flashlight_depth, flashlight_depth_texture_uv).r;
float ground_point_distance_from_flashlight = -(clip_space.z / clip_space.w) * 0.5 + 0.5;
float distance_error = clamp(abs(occluder_distance - ground_point_distance_from_flashlight), 0.0, 1.0);
COLOR.rgb = vec3(distance_error);
}
Godot 4.6 MRE项目源码在此。该示例在编辑器中直接运行,无需启动场景。
解决方案
Godot论坛上的大神帮我解决了这个问题。Godot论坛问题链接。
问题出在把浮点深度编码为无符号字节的红色通道上。
你需要把深度图缓冲区改为浮点格式,以便存储实际正确的数值。否则它只会将数值钳制在0-1范围内,并将每个通道映射为8位整数。为此,请为视口启用2D HDR。
为了调试预览,我只是把你着色器中的最后一行改为了:
COLOR.rgb = vec3(step( 0.0001, distance_error));
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。