shader - 如何获取世界坐标中给定像素的颜色?
问题描述
我正在使用 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
将在视图空间中拍摄。将其扩大到坐标上的vec4
with 。然后用 变换它。1.0
w
CAMERA_MATRIX
从视图空间到世界空间(即CAMERA_MATRIX
逆视图矩阵)。然后你从中取出一个,丢弃.vec4
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;
您想知道它在视图空间中的位置。嗯……我们做我们所做的,但反过来。
我们做的最后一件事是丢弃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
.
推荐阅读
- testing - TestCafe 不在文本输入字段中写入
- c# - 如何从 ASP.NET mvc 中的控制器访问 HttpContext
- alteryx - 拆分 JSON 输入并在 Alteryx 工作流中应用与列名相同的 JSON 字段名称
- c++ - 在语言环境土耳其语 (1254) 计算机中使用希腊语代码页 (1253)
- spring-boot - 带有 Hikari 池的 Spring Boot - 不重用连接
- android - 获得 OnTouch 事件时未调用 OnClick
- python - 使用 200.000 个不同的单词字典嵌入 TensorFlow 功能 API
- libreoffice-calc - 复制 OO / LO 宏对话框的最简单方法是什么?
- swift - Swift 编译器在使用具有关联类型的协议扩展时因分段而失败
- c - 尝试拆分字符串,但子字符串混乱