美文网首页
[Unity/UI]使用更多的顶点信息与图集uv映射

[Unity/UI]使用更多的顶点信息与图集uv映射

作者: 江枫枫Maple | 来源:发表于2019-12-12 11:37 被阅读0次

      好久没有写新的文章了,最近一直沉迷FF14(绝神兵好难啊(:з」∠))。最近踩了一个新的坑,怕过几天自己又忘记了所以要赶紧写下来提醒自己~。
      我们在制作UI功能的时候,有时候会需要一些额外的效果,为了性能考虑,有的时候我们会使用shader进行UI效果的优化。P.S.这里贴一篇大佬的优化文章,这个我看了好久,里面的代码有点奇怪,但是思路真的很有用!!! Unity手游开发札记——使用Shader进行UGUI的优化
      然后我们有的时候要使用一些额外的贴图进行采样,然后采样要使用到uv值,当我们使用单张的图片是没有问题的,但是一旦我们使用了图集,这个时候UI中获取到的uv值就不是标准的[0-1]范围的uv值了,而是基于图集中的图片位置计算出的uv值。这也是Unity的UI究竟为什么可以合批的原因。
      但是我们要对其他贴图(这张贴图没有被打进图集,当然打进图集也可以,但是要做一些额外操作,比如把这张图片的uv值也存在顶点信息中)采样时需要一个标准的[0-1]范围的uv,这个时候我们就要借助C#端进行图集的映射了。
      通过DataUtility类中的GetOuterUV函数,我们可以获取到图片在图集中的uv值,然后借助BaseMeshEffect中的ModifyMesh函数,我们可以拿到传给shader之前的顶点数据。然后我们就可以给shader传递更多我们自己定义的参数啦。

    public override void ModifyMesh(VertexHelper vh)
    {
        if (!IsActive())
            return;
        if (img==null)
        {
            img = GetComponent<Image>();
        }
        if(img.sprite==null)
            return;
        outerUv = DataUtility.GetOuterUV(img.sprite);
        UIVertex vert = new UIVertex();
        for (int i = 0; i < vh.currentVertCount; i++)
        {
            vh.PopulateUIVertex(ref vert, i);
            vert.uv1 = Caluv1(vert.uv0);
            vh.SetUIVertex(vert, i);
        }
    }
    private Vector2 Caluv1(Vector2 uv0)
    {
        uv0.x = uv0.x - outerUv.x > 0 ? 1 : 0;
        uv0.y = uv0.y - outerUv.y > 0 ? 1 : 0;
        return uv0;
    }

      这里我直接使用了顶点的uv0数据,因为uv数据理论上与我使用GetOuterUV函数获取到的uv值是相同的,所以我只计算了左下角坐标,直接把uv1的值设置到了[0-1]范围。
      然后还要在shader中添加对应的uv1值接收,要在a2v结构中添加TEXCOORD1属性,当然v2f结构中也要有对应的存储结构(下面的代码是直接从UI-Defalut中copy来的,只有//add的两行是我加的QAQ):

            struct appdata_t
            {
                float4 vertex   : POSITION;
                float4 color    : COLOR;
                float2 texcoord : TEXCOORD0;
                float2 texcoord1 : TEXCOORD1;//add
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f
            {
                float4 vertex   : SV_POSITION;
                fixed4 color    : COLOR;
                float2 texcoord  : TEXCOORD0;
                float4 worldPosition : TEXCOORD1;
                float2 texcoord1  : TEXCOORD2;//add
                UNITY_VERTEX_OUTPUT_STEREO
            };

            v2f vert(appdata_t v)
            {
                v2f OUT;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
                OUT.worldPosition = v.vertex;
                OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);

                OUT.texcoord = v.texcoord;
                OUT.texcoord1 = v.texcoord1;
                OUT.color = v.color * _Color;
                return OUT;
            }

      当我以为这样就大功告成的时候,我发现自己高兴的实在是太早了,因为在测试的时候不管怎么样,我的texcoord1都像是没办法从C#端传入shader一样。而且当我直接输出uv1的值的时候,发现uv1的值还会因为物体的X坐标不同而变化。。。。经过两个多小时的百度,我终于找到了这篇文章Unity3D UGUI 源码学习 BaseMeshEffect
      原来一切的罪魁祸首竟然是canvas,因为canvans不允许我们使用除了uv0以外更多的顶点信息,所以我们填充的uv1信息也就完全没有作用了。

canvans设置额外信息
      设置完canvans之后,我的uv1值终于可以顺利传入shader啦!不多说了,FF14启动!
      补充:

      因为我之前的理解是Image的范围就是顶点的绘制范围,那么对应的uv也应该是处于[0-1]的范围内。但是后来在测试时发现并不是所有图片的uv值都在[0-1]的范围内。
      当我们使用的图片周围有一些空白像素时,Unity内部会帮我们做一个优化,直接舍弃掉多余的像素。


举个栗子

      就拿上一张图来说,这张图的大小是256*256,但是图片的周围有很多无用的像素,没有填充任何信息,这个时候我们在ModifyMesh函数中获取到的顶点uv值就不是一个[0-1]范围内的值了,而是一个Unity优化过的uv值。
      就在我好奇为什么uv不是[0-1]范围时还可以绘制出正常的图像时,我打开了线框模式...才发现原来在绘制顶点的时候根本就不是根据Image范围来绘制顶点的,而是像文字一样,只绘制有效像素范围:


Image与线框
      图中白色范围是Image组件范围,黑色范围是实际顶点所绘制的线框,上面的图片周围的无用像素已经被unity优化掉了,如果这种图片我们还用上面的算法把uv映射到[0-1]的范围,那么在使用这套uv采样的过程中就会出现错误的结果!(但是uv0是正确的,我暂时做了一个开关可以判断是否使用uv1,如果后面有更好的解决方法也会在这里补充)

相关文章

网友评论

      本文标题:[Unity/UI]使用更多的顶点信息与图集uv映射

      本文链接:https://www.haomeiwen.com/subject/ylizgctx.html