首页 > 解决方案 > 如何获取世界坐标中给定像素的颜色?

问题描述

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

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

假设我将 pixel_world_pos.y 增加 10,并且我想知道该像素是什么颜色。我怎么能弄清楚呢?

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

标签: shaderfragment-shadergodot

解决方案


让我们仔细看看这条线:

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

在这里,您VERTEX将在视图空间中拍摄。将其扩大到坐标上的vec4with 。然后用 变换它。1.0wCAMERA_MATRIX

从视图空间到世界空间(即CAMERA_MATRIX逆视图矩阵)。然后你从中取出一个,丢弃.vec4CAMERA_MATRIXvec3w


好吧,让我们说接下来你y像这样修改坐标:

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

您想知道它在视图空间中的位置。嗯……我们做我们所做的,但反过来。

我们做的最后一件事是丢弃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(也称为视图矩阵)的逆向量来转换向量。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因此我们可以直接从世界空间转换到剪辑空间。那就是INV_CAMERA_MATRIX把我们从世界空间带到视图空间,PROJECTION_MATRIX把我们从视图空间带到剪辑空间。

在我们更进一步之前,我们需要处理一个事实,即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代表标准化的设备空间。我们现在可以丢弃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,我们将获得与屏幕中心原点的坐标。所以我们需要对它们进行缩放和偏移,使它们从左下角的零变为右上角的一。这就是屏​​幕紫外线的工作原理。

顺便说一句,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.


推荐阅读