TL; 博士
将着色器应用于UI.Image
Unity3D 画布中的对象时,UNITY_MATRIX_MVP
似乎与画布渲染器相关,而不是正在绘制的图像对象本身。
有没有一种方法可以计算 Image 对象的局部空间中一个点的剪辑空间坐标,而不仅仅是相对于 Canvas 根?
概述
作为着色器的预处理步骤,我正在计算屏幕上出现在此模型中的图像四边形的、x_min
和:x_max
y_min
y_max
UnityObjectToClipPos
我的基本方法是使用(仅使用内置转换矩阵)手动计算 4 个角的剪辑空间位置UNITY_MATRIX_MVP
,然后从中获取最小/最大值。
我发现这适用于SpriteRenderer
或MeshRenderer
组件,但对于UI.Image
画布中的组件UNITY_MATRIX_MVP
似乎仅相对于画布父级,而不是图像对象本身,这意味着我不能使用它来计算相对于图像的位置。
细节
你可以在这里找到完整的着色器:https ://gist.github.com/JohannesMP/5a74fcc41d77f281a5900021c01f2356
对于给定的对象空间坐标UnityObjectToClipPos
,首先使用齐次剪辑空间坐标,然后将其移动到屏幕 UV 空间中(在屏幕上可见时值范围从 0 到 1):
inline float2 ObjToScreenUV(in float3 obj_pos)
{
float4 clip_pos4d = UnityObjectToClipPos(obj_pos);
float2 clip_pos2d = clip_pos4d.xy / clip_pos4d.w;
#if UNITY_UV_STARTS_AT_TOP
return float2(1 + clip_pos2d.x, 1 - clip_pos2d.y) / 2;
#else
return (1 + clip_pos2d) / 2;
#endif
}
由于在对象空间中定义单位四边形的 4 个顶点是微不足道的(在对象空间 X/Y 轴上仅偏移 0.5),因此屏幕上的最小/最大值可以计算如下:
inline float4 GetQuadUVBounds()
{
// 1. Get relative screen position of 4 quad corners
float2 bl = ObjToScreenUV(float3(-0.5, -0.5, 0));
float2 tl = ObjToScreenUV(float3(-0.5, 0.5, 0));
float2 br = ObjToScreenUV(float3(0.5, -0.5, 0));
float2 tr = ObjToScreenUV(float3(0.5, 0.5, 0));
// 2. Get min/max of x and y
float min_x = min(min(bl.x, tl.x), min(br.x, tr.x));
float max_x = max(max(bl.x, tl.x), max(br.x, tr.x));
float min_y = min(min(bl.y, tl.y), min(br.y, tr.y));
float max_y = max(max(bl.y, tl.y), max(br.y, tr.y));
return float4(min_x, min_y, max_x, max_y);
}
请注意,我目前不打算优化最小/最大逻辑。
为了验证最小/最大值的计算是否正确,可以在绘制对象时将它们用于红色和绿色通道。
例如,要验证它是否有效x_min
,y_min
可以使用以下片段着色器:
half4 frag () : COLOR
{
// Min is bounds.xy, max is bounds.zw
float4 bounds = GetQuadUVBounds();
return float4(bounds.xy, 0, 1);
}
对于MeshRenderer
四边形和SpriteRenderer
对象,结果如下:
- 随着
x_min
增加,红色通道也增加。 - 随着
y_min
增加,绿色通道也增加。
测试最大值也会产生预期的结果。
但是,UI.Image
在画布中的组件上测试相同的着色器时,它不能按预期工作:
尽管UI.Image
对象(将着色器设置为其材质)在屏幕上移动,但其颜色不会改变。但是,当在编辑器场景视图中移动编辑器相机时,颜色会发生变化。
这似乎意味着UnityObjectToClipPos
,以及UNITY_MATRIX_MVP
它所依赖的矩阵,与 Canvas Parent 对象相关,而不是与父对象内的 Image 对象相关。
问题
我的假设是否正确,UNITY_MATRIX_MVP
即相对于画布对象,而不是正在绘制的 Image 对象?如果是这样,是否有另一种方法可以专门为 Image 对象获取 MVP 矩阵?或者,是否有任何其他方法可以在着色器中计算图像局部空间中某个点的剪辑空间位置?
- 我正在使用 Unity 2017.1.2f1
- 着色器
DisableBatching
标志已启用