首页 > 解决方案 > Unity Canvas 图像着色器的 Object-To-Clip

问题描述

TL; 博士

将着色器应用于UI.ImageUnity3D 画布中的对象时,UNITY_MATRIX_MVP似乎与画布渲染器相关,而不是正在绘制的图像对象本身。

有没有一种方法可以计算 Image 对象的局部空间中一个点的剪辑空间坐标,而不仅仅是相对于 Canvas 根?


概述

作为着色器的预处理步骤,我正在计算屏幕上出现在此模型中的图像四边形的、x_min和:x_maxy_miny_max

在此处输入图像描述

UnityObjectToClipPos我的基本方法是使用(仅使用内置转换矩阵)手动计算 4 个角的剪辑空间位置UNITY_MATRIX_MVP,然后从中获取最小/最大值。

我发现这适用于SpriteRendererMeshRenderer组件,但对于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_miny_min可以使用以下片段着色器:

half4 frag () : COLOR
{
    // Min is bounds.xy, max is bounds.zw
    float4 bounds = GetQuadUVBounds();
    return float4(bounds.xy, 0, 1);
}

对于MeshRenderer四边形和SpriteRenderer对象,结果如下:

在此处输入图像描述

测试最大值也会产生预期的结果。

但是,UI.Image在画布中的组件上测试相同的着色器时,它不能按预期工作:

在此处输入图像描述

尽管UI.Image对象(将着色器设置为其材质)在屏幕上移动,但其颜色不会改变。但是,当在编辑器场景视图中移动编辑器相机时,颜色发生变化。

这似乎意味着UnityObjectToClipPos,以及UNITY_MATRIX_MVP它所依赖的矩阵,与 Canvas Parent 对象相关,而不是与父对象内的 Image 对象相关。


问题

我的假设是否正确,UNITY_MATRIX_MVP即相对于画布对象,而不是正在绘制的 Image 对象?如果是这样,是否有另一种方法可以专门为 Image 对象获取 MVP 矩阵?或者,是否有任何其他方法可以在着色器中计算图像局部空间中某个点的剪辑空间位置?

标签: unity3dglslhlslfragment-shaderunity-ui

解决方案


推荐阅读