Godot Shader学习笔记 - 后处理 - 深度图
2024-02-28
尚未完成,漏洞连篇。
后处理是个麻烦活。在戈多里,最方便的办法是添加一个camera,在这个camera下,添加一个MeshInstance, 添加QuadMesh,然后为这个mesh附加shader即可。
上述过程可能有些“Hacky”。
我尝试自己按照戈多的官方文档提供的最佳方案进行后处理:https://docs.godotengine.org/en/stable/tutorials/shaders/advanced_postprocessing.html
获取深度
这种方案可以获取深度缓冲区(也就是对于每个像素,你都能获取到对应的像素点)。
shader_type spatial;
render_mode unshaded;
uniform sampler2D depth_texture : hint_depth_texture; // 深度纹理
uniform sampler2D screen_texture : hint_screen_texture; // 屏幕纹理
void vertex() {
POSITION = vec4(VERTEX, 1.0); // 向POSITION写入数据,这样能禁用Godot的变换。
}
void fragment() {
float depth = texture(depth_texture, SCREEN_UV).x; // 获取顶点深度
vec3 ndc = vec3(SCREEN_UV * 2.0 - 1.0, depth); // 获取CLIP_SPACE内点的位置
vec4 view = INV_PROJECTION_MATRIX * vec4(ndc, 1.0); // 获取CAMERA_SPACE内点位置(齐次坐标)
view.xyz = view.xyz / view.w; // 归一化:将齐次坐标转换为笛卡尔坐标
float linear_depth = -view.z; // 此时的深度是线性的。(但不是[0-1]区间内,不能直接显示)
vec4 world = INV_VIEW_MATRIX * INV_PROJECTION_MATRIX * vec4(ndc, 1.0); // 世界空间的坐标
vec3 world_position = world.xyz / world.w; // 归一化,转为笛卡尔坐标
// 深度范围
float near = 0.1; // 最近深度
float far = 100.0; // 最远深度
// 将深度从它原本的范围,缩放到[0, 1]
float depth01 = clamp((linear_depth - near) / (far - near), 0.0, 1.0);
ALBEDO = vec3(depth01);
}
Godot会在内部处理物体在三维空间内的变换。Godot会处理Model Space, World Space, View Space, Clip Space, Screen Space的变换。这就是为什么你放置一个cube后,能在编辑器里看到这个方块的原因。戈多首先获得这个方块的模型数据,然后把方块的顶点的坐标由局部坐标(相对模型中心的坐标)变换为世界坐标(相对世界原点的坐标),然后变换为视图坐标(相对相机的坐标)。再然后会通过视图变换变为裁剪空间内的坐标,最后将裁剪空间内的坐标转换成屏幕坐标。
`POSITION`是一个可在vertex()里修改的vec4量。修改POSITION的值,会禁用引擎内部上述的模型视图变换。这样一来,你塞在场景内那个充当后处理工具的平面就会时刻贴在你的相机上。它相对世界的位置已经不再可用(?是这样吗?)。
有了上述的代码,我们就很容易把相机面前的物体的距离,用黑白图的形式显示在我们面前了:
不够,直接拿深度来判断似乎有些不妥。我们还得想办法搞到法线才行。