Unity3D 利用代码进行uv贴图

大家好,这里是只蠢新,今天为大家分享一下如何在Unity中通过uv重新对物体贴图,我们以最简单的Cube为例(如有谬误,欢迎勘正)。

起因是我在课程设计中想要用unity实现Minecraft游戏,在搜集素材后发现对于一些特殊的立方体(如土带草方块,下面是泥土、顶面是草地的方块)需要用代码控制进行uv贴图(当然是仅有材质可获得的情况)。

对于三维模型,除了顶点位置坐标外,还有一个很重要的叫做UV坐标,UV坐标是贴图映射到三维模型的重要依据,因此我们可以通过修改纹理坐标来实现Cube不同面的不同贴图。

由于纹理坐标与顶点坐标之间是通过三角面片间接联系起来的,首先要了解Cube的三角网格信息,这里直接通过Unity将Cube的mesh三角网格信息输出,如下图所示: 输出的每一行都代表了一个三角片(顺时针),每个数字即其顶点编号,共4*6=24个顶点(共六个面,每个面有4个顶点,每个面顶点单独计算),每两行(每两个三角片)确定一个平面。

接着输出mesh的顶点信息,各个顶点信息排序是按三角网格编号顺序进行的(第一个输出的顶点信息对于三角网格编号1的顶点,第二个对于编号2,以此类推),如下图所示: 我们的素材图则是这样子的↓ 红色的数字是根据上面获得的三角网格与顶点信息,给对应点标上的三角网格顶点编号,我们只要按编号访问其对应的uv,修改纹理坐标的值即可完成贴图。

纹理坐标是通过三角面片与顶点坐标一一对应的,例如三角网格编号5,对应顶点(0.5, 0.5, -0.5),即z轴负向侧的面的右上角顶点,在素材图中对应顶面图的右上角(Unity对Cube的缺省uv展开为Z轴正向面→Y轴正向面→Z轴负向面→Y轴负向面→X轴负向面→X轴正向面,三角网格编号5所在的面为Cube Y轴正向面,即顶面),即图中标为5的点,坐标为(0.666,0.333)

(uv坐标区间通常为0至1,即左下角顶点为(0,0),右上角顶点为(1,1),其中间点坐标由比例缩放可得)

贴图结果如下图所示: 这样我们就可以通过代码来对cube进行贴图控制了,当然三角网格的编号、顶点设置都是可以自己修改的。 我们还可以通过类似方法修改uv来实现只渲染某个面,或者实时变换各个面的颜色/图像等等。

MapTexture代码实现
Void Map () 
{
    MeshFilter filter = GetComponent<MeshFilter>();
    Mesh mesh = filter.mesh;
    if (filter != null)
        mesh = filter.mesh;

    if (mesh == null || mesh.uv.Length != 24)
    {
        Debug.Log("Attach To Cube");
        return;
    }

    var uvs = mesh.uv;

    //Z轴正向侧(背面)
    uvs[0] = new Vector2(0.0f, 0.0f);
    uvs[1] = new Vector2(0.333f, 0.0f);
    uvs[2] = new Vector2(0.0f, 0.333f);
    uvs[3] = new Vector2(0.333f, 0.333f);

    //顶面
    uvs[8] = new Vector2(0.334f, 0.0f);
    uvs[9] = new Vector2(0.666f, 0.0f);
    uvs[4] = new Vector2(0.334f, 0.333f);
    uvs[5] = new Vector2(0.666f, 0.333f);

    //Z轴负向侧(正面)
    uvs[10] = new Vector2(0.667f, 0.0f);
    uvs[11] = new Vector2(1.0f, 0.0f);
    uvs[6] = new Vector2(0.667f, 0.333f);
    uvs[7] = new Vector2(1.0f, 0.333f);

    //底面
    uvs[12] = new Vector2(0.0f, 0.334f);
    uvs[13] = new Vector2(0.0f, 0.666f);
    uvs[15] = new Vector2(0.333f, 0.334f);
    uvs[14] = new Vector2(0.333f, 0.666f);

    //左面
    uvs[16] = new Vector2(0.334f, 0.334f);
    uvs[18] = new Vector2(0.666f, 0.666f);
    uvs[19] = new Vector2(0.666f, 0.334f);
    uvs[17] = new Vector2(0.334f, 0.666f);

    //右面       
    uvs[20] = new Vector2(0.667f, 0.334f);
    uvs[22] = new Vector2(1.00f, 0.666f);
    uvs[23] = new Vector2(1.00f, 0.334f);
    uvs[21] = new Vector2(0.667f, 0.666f);

    mesh.uv = uvs;
}

后记

emmmm,其实在测试中是存在一些问题的,主要是将该脚本用于大量物体时会出现问题。

我把地图规模设置为200*200,下面是一些测试的情况:

1.每个Cube都附有Texture脚本:

Batches和SetPass calls都在一万左右,游戏很卡,总而言之相当可怕……

2.每个Cube都不带有脚本:

数值上约为带脚本的1/7,CPU情况稍微好转(毕竟仍是有四万个方块呢……)

3.每个Cube都设置为Static,且带有脚本:

虽然每个方块都附带有脚本,但是情况没有1那么恐怖,与情况2比较接近。

最后我将场景中的光源关闭,发现无论是否附带代码,Batches和SetPass calls数值上两种情况大致接近,且保持一个较低的值。

推测是由于我们改动了贴图的uv导致,unity在计算时,把原本的一个uv shell当做了6个uv shell,当接受光照时,每个uv shell都会进行一次Draw call,又加上一些其他渲染规则的干扰,导致最终情况1数值是情况2的7倍左右。而当我们对物体勾选Static后,由于Lightmap Static的作用,先对每个方块进行烘焙生产一个光照图,覆盖在方块上建立光照效果,只调用了一次Draw call,故和情况2大致相同。

但在测试100*100规模时,发现3种情况数据相差不大(都在100左右),最关键的是只有一个Cube的时候,有无代码是完全一样的,也就是说那种神奇的情况只有在万级物体存在的时候出现,或许这是unity对如此大负荷工作的一种抗议吧(傲娇的Unity酱)

总之目前建议是不要在场景中大量使用,仅对有需要的地方增加uv修改就可以了。游戏中确认不会移动变化的物体建议勾选static,另外静态批次虽然可以提高性能但是以耗费内存为代价,使用前需谨慎。最后一点就是千篇一律的Unity优化建议:尽量不要使用实时光照。

那么这次我们就到此结束,哦,顺便在研究查阅资料的过程中看到了一些很有趣的东西,shader编程,目前打算学习一下这方面内容,那么期待下次再见啦。

胡溢钧

河海大学常州校区学生,一只很蠢很蠢的新人。

Changzhou,Jiangsu,China

Subscribe to CG-HHU

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!