Godot着色器中如何确定灯光相对于TileMap瓦片的位置?
问题
我给TileMap里的特定瓦片材质分配了Godot CanvasItem着色器,需要在着色器里获取灯光相对于瓦片底部的位置。目前我通过以下代码计算瓦片右下角坐标:
varying vec2 bottom_right_corner; void vertex() { vec2 world_vertex_position = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy; bottom_right_corner = floor(world_vertex_position / 32.0 + 1.0) * 32.0; } void light() { // How to compare bottom_right_corner.y with LIGHT_POSITION.y ? }
我认为需要对坐标进行变换才能与LIGHT_POSITION做比较,但不确定具体变换方式,请问该如何实现这种比较?
解决方法
在Godot的CanvasItem着色器中,LIGHT_POSITION和你计算的bottom_right_corner处于不同坐标空间,必须统一到同一空间才能直接对比。
坐标空间差异说明
bottom_right_corner是通过MODEL_MATRIX转换得到的世界空间坐标。LIGHT_POSITION在light()函数中默认是裁剪空间坐标,需要转换为世界空间才能和瓦片坐标匹配。
实现方案一:将灯光位置转世界空间
在顶点着色器中计算逆投影视图模型矩阵,传递到光函数中完成坐标转换:
varying vec2 bottom_right_corner; varying mat4 inv_projection_view_model; void vertex() { vec2 world_vertex_position = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy; bottom_right_corner = floor(world_vertex_position / 32.0 + 1.0) * 32.0; // 计算逆矩阵,用于裁剪空间到世界空间的转换 inv_projection_view_model = inverse(PROJECTION_MATRIX * VIEW_MATRIX * MODEL_MATRIX); } void light() { // 将裁剪空间的灯光位置转换为世界空间 vec4 light_world_pos = inv_projection_view_model * vec4(LIGHT_POSITION, 0.0, 1.0); vec2 light_world_xy = light_world_pos.xy; // 现在可以直接对比y坐标 float relative_y = bottom_right_corner.y - light_world_xy.y; // 根据relative_y的值实现灯光相对位置的逻辑,比如判断灯光是否在瓦片底部上方/下方 }
实现方案二:将瓦片坐标转裁剪空间
如果不需要世界空间坐标,也可以把瓦片右下角坐标转换到裁剪空间,直接和LIGHT_POSITION对比:
varying vec2 bottom_right_corner_clip; void vertex() { vec2 world_vertex_position = (MODEL_MATRIX * vec4(VERTEX, 0.0, 1.0)).xy; vec2 bottom_right_corner_world = floor(world_vertex_position / 32.0 + 1.0) * 32.0; // 转换为裁剪空间坐标 bottom_right_corner_clip = (PROJECTION_MATRIX * VIEW_MATRIX * vec4(bottom_right_corner_world, 0.0, 1.0)).xy; } void light() { // 直接对比裁剪空间的y坐标 float relative_y = bottom_right_corner_clip.y - LIGHT_POSITION.y; }
注意事项
- 确认代码中的瓦片尺寸
32.0和TileMap的瓦片大小完全一致,避免坐标计算误差。 - 若使用Godot 4.x,矩阵变量名有所调整:
PROJECTION_MATRIX改为PROJECTION,VIEW_MATRIX改为VIEW,需对应修改代码。
内容的提问来源于stack exchange,提问作者Antoine Aubry
相关产品推荐
相关产品推荐

