Unity Canvas图像着色器的对象到剪辑

时间:2018-10-07 03:21:33

标签: unity3d glsl hlsl fragment-shader unity-ui

TL; DR

将着色器应用于Unity3D画布中的UI.Image对象时,UNITY_MATRIX_MVP似乎是相对于Canvas渲染器的,而不是相对于Image对象本身的绘制。

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


概述

作为着色器的预处理步骤,我正在计算屏幕上出现的图像四边形的x_minx_maxy_miny_max这个样机:

enter image description here

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

我发现这适用于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个对象的哪个导致以下结果:

enter image description here

  • 随着x_min的增加,红色通道也会增加。
  • 随着y_min的增加,绿色通道也会增加。

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

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

enter image description here

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

这似乎暗示UnityObjectToClipPos以及扩展所依赖的UNITY_MATRIX_MVP矩阵与Canvas Parent对象有关,而不是与Parent内部的Image对象有关。


问题

我是否以UNITY_MATRIX_MVP是相对于画布对象而不是要绘制的Image对象为假设进行更正?如果是这样,还有另一种方法来获取专门用于Image对象的MVP矩阵吗?另外,还有其他方法可以在着色器中计算点在图像局部空间中的Clip-space位置吗?

  • 我正在使用Unity 2017.1.2f1
  • DisableBatching着色器标志已启用

0 个答案:

没有答案