如何获取世界坐标中给定像素的颜色?

How to get the color of a given pixel in world coordinates?

我正在使用 Godot 的片段着色器。我可以这样获取当前像素的世界坐标:

vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;

假设我将 pixel_world_pos.y 增加了 10,我想知道那个像素是什么颜色。我该如何解决?

我知道信息存储在 UV 中,但我使用的是世界坐标,而不是 UV 坐标...

让我们仔细看看这一行:

vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;

在这里,您选择 VERTEX,它会在 space 的视野中。在 w 坐标上使用 1.0 将其扩展为 vec4。然后用 CAMERA_MATRIX.

进行改造

CAMERA_MATRIXvec4 从视图 space 带到世界 space(即 CAMERA_MATRIX 是一个逆视图矩阵)。然后你从中取出 vec3,丢弃 w.


好吧,假设你接下来修改y坐标:

vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
pixel_world_pos.y = pixel_world_pos.y + 10.0;

并且您想知道视图 space 的位置。嗯……我们做我们做的,但反过来。

我们做的最后一件事是丢弃 w。我们需要在后面加上w

vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
pixel_world_pos.y = pixel_world_pos.y + 10.0;
vec4 tmp = vec4(pixel_world_pos, 1.0);

假设 CAMERA_MATRIX 是 3D 仿射变换。

您之前做的事情是用 CAMERA_MATRIX 转换向量。要撤消它,我们需要使用 CAMERA_MATRIX 的逆(a.k.a。视图矩阵)来转换向量。 Godot 将其提供为 INV_CAMERA_MATRIX。所以我们用那个转换:

vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
pixel_world_pos.y = pixel_world_pos.y + 10.0;
vec4 tmp = INV_CAMERA_MATRIX * vec4(pixel_world_pos, 1.0);

你做的第一件事就是增加向量。所以让我们现在丢弃 w

vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
pixel_world_pos.y = pixel_world_pos.y + 10.0;
vec3 pixel_view_pos = (INV_CAMERA_MATRIX * vec4(pixel_world_pos, 1.0)).xyz;

好了。


哦,等等你要读取像素。所以我们需要紫外线。下一步是使用 PROJECTION_MATRIX.

vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
pixel_world_pos.y = pixel_world_pos.y + 10.0;
mat4 WORLD_TO_CLIP_MATRIX = PROJECTION_MATRIX * INV_CAMERA_MATRIX;

在这里,我将视图矩阵 (INV_CAMERA_MATRIX) 与投影矩阵 (PROJECTION_MATRIX) 组合在一起,因此我们可以直接从世界 space 转换为剪辑 space .即 INV_CAMERA_MATRIX 将我们从世界 space 带到视图 space,而 PROJECTION_MATRIX 将我们从视图 space 带到剪辑 space。

在我们更进一步之前,我们需要处理一个事实,即 PROJECTION_MATRIX 进行投影变换(惊喜!),这不是仿射变换。所以我们不应该添加和删除 w,相反,我们这样做:

vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
pixel_world_pos.y = pixel_world_pos.y + 10.0;
mat4 WORLD_TO_CLIP_MATRIX = PROJECTION_MATRIX * INV_CAMERA_MATRIX;
vec4 pixel_clip_pos = WORLD_TO_CLIP_MATRIX * vec4(pixel_world_pos, 1.0);
vec3 pixel_nds_pos = pixel_clip_pos.xyz / pixel_clip_pos.w;

这里dns代表归一化设备space。我们现在可以丢弃 z

vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
pixel_world_pos.y = pixel_world_pos.y + 10.0;
mat4 WORLD_TO_CLIP_MATRIX = PROJECTION_MATRIX * INV_CAMERA_MATRIX;
vec4 pixel_clip_pos = WORLD_TO_CLIP_MATRIX * vec4(pixel_world_pos, 1.0);
vec2 uv = (pixel_clip_pos.xyz / pixel_clip_pos.w).xy * 0.5 + vec2(0.5, 0.5);

然而,事实证明,如果我们简单地执行 (pixel_clip_pos.xyz / pixel_clip_pos.w).xy,我们将获得屏幕中心的原点坐标。所以我们需要缩放和偏移它们,使它们从左下角的零变为右上角的一。这就是屏​​幕 uv 的工作原理。

顺便说一下,如果你想计算深度,我们丢弃的 z 会很有用。


我们完成了:

shader_type spatial;
render_mode unshaded;

void fragment()
{
    vec3 pixel_world_pos = (CAMERA_MATRIX * vec4(VERTEX, 1.0)).xyz;
    pixel_world_pos.y = pixel_world_pos.y + 10.0;
    mat4 WORLD_TO_CLIP_MATRIX = PROJECTION_MATRIX * INV_CAMERA_MATRIX;
    vec4 pixel_clip_pos = WORLD_TO_CLIP_MATRIX * vec4(pixel_world_pos, 1.0);
    vec2 uv = (pixel_clip_pos.xyz / pixel_clip_pos.w).xy * 0.5 + vec2(0.5, 0.5);
    ALBEDO = texture(SCREEN_TEXTURE, uv).rgb;
}

顺便说一下,您需要注意您计算的 uv 可能会超出 SCREEN_TEXTURE 的边界。