如何在Unity3D中产生任意形状的物体?

Unity3D是一款渲染功能非常强大、交互性能优越的游戏引擎,著名的《纪念碑谷》(图1)就是采用该引擎开发的。

图1. 游戏《纪念碑谷》

用过Unity3D的读者知道,Unity中只提供了正方体、球形、圆柱体等基础形状的预设体,那么如何在Unity3D中产生任意形状的物体呢?下面就我个人的经验,以产生一个箭头为例,提供可能的几个思路。

一、物体拼接

这是最简单的方法,因为一个箭头可以由细长圆柱 + 圆锥拼接而成。

我们需要先在Unity3D中新建一个空的物体,命名为“Arrow”(图2)。

图2. 新建空的游戏物体

然后我们创建一个圆柱体(3D Object -> Cylinder),但是这里没有圆锥体,怎么办?这就用到了Unity3D的Asset Store了,在商店中搜索“ConeCollider”,可以找到相应的免费资源(图3)。

图3. 商店中的“ConeCollider”

点击下载,下载完成后导入,可以找到相应的模型ConeCollider(图4),把它拉进Hierarchy中即可。

图4. 建立圆锥体

接下来将圆柱和圆锥都移动到“Arrow”的空物体处,成为其子物体,同时调整圆柱、圆锥的大小和位置直至合适,这样一个箭头就产生了(图5)。同时,可以将Arrow拉倒下方文件夹中,成为一个Prefab(预设体),这样后面再产生箭头就可以由该预设体产生了。

图5. 拼接完成的箭头

二、动态产生网格拓扑

第一种方法虽然非常简单方便,但是只适用于产生一些简单性状的物体,同时不利于物体网格的压缩。这里介绍另一种非常实用的方法——在Unity3D中动态产生相应性状的网格拓扑,这种方法理论上可以产生任意你想要的性状。

讨论具体的方法之前,需要提到Unity3D中物体形状的网格拓扑。在Unity3D中,所有物体都是由一个个三角面组成,也许你会问那怎么会有圆柱、球形等形状?聪明的读者已经从图4中的圆锥得到答案,只要划分足够细,正多边形可以近似为圆形,古代的“割圆术”即是这个原理。图6给出了Unity3D中球的网格拓扑(这里用到了渲染网格的UCLA Wireframe Shader,同样可以在商店中免费下载使用)。

图6. Unity3D中球的网格拓扑

可以看到,Unity3D中的物体都是由这样的三角形网格组成的,构成网格的信息包括两部分:顶点(vertices)和三角拓扑信息(即每一个三角形由哪些点组成,triangles)。那么只需要通过脚本更改物体网格的顶点与拓扑,就可以得到相应的形状了,这就是第二种产生任意形状物体的方法——动态产生网格拓扑。

举个简单的例子,比如我们要生成一个正方形,我们可以把它分解成三角形(可以分解成2个三角形、4个三角形……这里以两个三角形为例,图7)。

图7. 正方形示意图

顶点坐标都有了,那么接下来是三角拓扑信息。左下角三角形组成的顶点为0,2,1(逆时针顺序,Unity3D中通常采用单面渲染逆,即时针可见,顺时针不可见),右下角三角形组成的顶点为1,2,3。这样triangles就是一个整数型数组{0,2,1,1,2,3}。将上述顶点和拓扑替换掉原来物体的网格信息,再重新计算法线方向就可以得到新的网格了。

相应的代码如下:

// 生成正方形的4个顶点

Vector3[] vertices = new Vector3[4];

vertices[0] = new Vector3(0, 0, 0);

vertices[1] = new Vector3(0, 0, 1);

vertices[2] = new Vector3(0, 1, 0);

        vertices[3] = new Vector3(0, 1, 1);

// 生成正方形的三角拓扑信息

        int[] triangle = new int[6] {0,2,1,1,2,3 };

// 获取物体的网格

        Mesh mesh = GetComponent<MeshFilter>().mesh;

// 清除原有网格

mesh.Clear();

// 赋予网格新的顶点

mesh.vertices = vertices;

// 赋予网格新的拓扑信息

        mesh.triangles = triangle;

// 网格重计算法线

        mesh.RecalculateNormals();

将相应的脚本绑定在场景中的游戏物体上,运行结果见图8。

图8. Unity3D运行产生的正方形

回到我们之前的例子——箭头,那么你需要动脑筋梳理出箭头的顶点坐标和拓扑信息应该是怎么样的了。图9给出了作者采用的一种方案,以12边形近似为圆。

图9. 12边形近似的箭头

相应代码如下:

using System;

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

public class CreateArrow : MonoBehaviour {

    public float ra = 0.3f,// 圆柱的半径

    ha = 3,// 圆柱的高度

    rb = 1,// 圆锥的半径

    hb = 2;// 圆锥的高度

    public int n = 12;// 正多边形的边数

    // 初始化函数

    void Start () {

        Vector3[] vertices = new Vector3[3 * n + 1];// 模型的顶点

        int[] triangle = new int[18 * n - 12];// 模型的三角形拓扑

        for (int i = 0; i < n; i++)

        {

            // 圆柱的底面顶点

            vertices[i] = new Vector3((float)(ra * Math.Cos(2 * Math.PI * i / n)), (float)(ra * Math.Sin(2 * Math.PI * i / n)), 0);

            // 圆柱的顶面顶点

            vertices[n + i] = new Vector3((float)(ra * Math.Cos(2 * Math.PI * i / n)), (float)(ra * Math.Sin(2 * Math.PI * i / n)), ha);

            // 圆锥的底面顶点

            vertices[2 * n + i] = new Vector3((float)(rb * Math.Cos(2 * Math.PI * i / n)), (float)(rb * Math.Sin(2 * Math.PI * i / n)), ha);

        }

        // 圆锥的顶点

        vertices[3 * n] = new Vector3(0, 0, ha + hb);

        // 生成三角拓扑信息

        for (int i = 0; i < n - 2; i++)

        {

            triangle[3 * i] = 0;

            triangle[3 * i + 2] = i + 1;

            triangle[3 * i + 1] = i + 2;

            triangle[3 * (n - 2) + 6 * n + 3 * i] = 2 * n;

            triangle[3 * (n - 2) + 6 * n + 3 * i + 2] = 2 * n + i + 1;

            triangle[3 * (n - 2) + 6 * n + 3 * i + 1] = 2 * n + i + 2;

        }

        for (int i = 0; i < n - 1; i++)

        {

            triangle[3 * (n - 2) + 6 * i] = i;

            triangle[3 * (n - 2) + 6 * i + 2] = n + i;

            triangle[3 * (n - 2) + 6 * i + 1] = n + i + 1;

            triangle[3 * (n - 2) + 6 * i + 3] = n + i + 1;

            triangle[3 * (n - 2) + 6 * i + 5] = i + 1;

            triangle[3 * (n - 2) + 6 * i + 4] = i;

            triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * i] = 2 * n + i;

            triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * i + 2] = 3 * n;

            triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * i + 1] = 2 * n + i + 1;

        }

        triangle[3 * (n - 2) + 6 * (n - 1)] = n - 1;

        triangle[3 * (n - 2) + 6 * (n - 1) + 2] = n + n - 1;

        triangle[3 * (n - 2) + 6 * (n - 1) + 1] = n;

        triangle[3 * (n - 2) + 6 * (n - 1) + 3] = n;

        triangle[3 * (n - 2) + 6 * (n - 1) + 5] = 0;

        triangle[3 * (n - 2) + 6 * (n - 1) + 4] = n - 1;

        triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * (n - 1)] = 2 * n + n - 1;

        triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * (n - 1) + 2] = 3 * n;

        triangle[3 * (n - 2) + 6 * n + 3 * (n - 2) + 3 * (n - 1) + 1] = 2 * n;

        // 获取物体的网格

        Mesh mesh = GetComponent<MeshFilter>().mesh;

        // 清除原有网格

        mesh.Clear();

        // 赋予网格新的顶点

        mesh.vertices = vertices;

        // 赋予网格新的拓扑信息

              mesh.triangles = triangle;

        // 网格重计算法线

                mesh.RecalculateNormals();

    }

}

将上述代码中的n改成128后,即以128边形近似为圆,结果如图10。

图10. 12边形近似的箭头

三、生成fbx模型文件

第二种方法的好处是方便灵活,但是模型的可复制性不强,比如需要很多个箭头,每次都要重复计算各个箭头的顶点坐标,程序的计算量一下子就上去了。那么,有没有什么办法可以让自己设计的网格模型成为可重复使用的模型呢?当然有!那就是生成fbx模型文件,也就是第一种方法用到的模型ConeCollider这样的文件。

生成fbx文件的方法很多,可以借助其他的建模软件(如3d Max, MAYA等),也可以自己使用Unity3D编写fbx文件。下面介绍利用Unity3D生成fbx文件的方法,需要用到一个dll外部库——WRP_FBXExporter.dll(百度搜索下载即可)。

将WRP_FBXExporter.dll移动到Assets文件夹下,引用中就会自动添加可以使用。相比第二种方法,只需要添加几行代码即可导出模型的fbx文件。

        // 建立游戏物体的数组,当前只有一个

        GameObject[] gameObjects = new GameObject[1];

        // 将场景中的物体作为数组元素,“CreateFBX”是场景中游戏物体名称

        gameObjects[0] = GameObject.Find("CreateFBX");

        // 调用FBXExporter.ExportFBX,生成对应的FBX文件

        FBXExporter.ExportFBX("", "Arraw" + n, gameObjects, true);

生成结果见图11,可以看到已经在Assets下生成了Arraw12.fbx。

图11. 生成相应的fbx文件

对于FBXExporter.ExportFBX () 函数的参数详解见图12,分别对应fbx文件存放文件、fbx文件名、转化的游戏物体数组等选项。

图12. FBXExporter.ExportFBX () 参数详解

参考资料

[1] https://assetstore.unity.com/?q=ConeCollider&orderBy=0(ConeCollider下载)

[2] https://assetstore.unity.com/packages/vfx/shaders/directx-11/ucla-wireframe-shader-21897(UCLA Wireframe Shader下载)

[3] https://blog.csdn.net/dong2016hong/article/details/54847235(WRP_FBXExporter.dll下载)

谢谢您的阅读与支持!

文章内容原创,如需转载或内容合作,请联系作者:wangrongxin168@163.com

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容

  • 素描知识大汇总 素描是造型艺术的基础。作为造型艺术形式之一的素描,它是通过形体结构、比例、位置、运动、线条、明暗调...
    感叹号美术阅读 3,945评论 0 19
  • 更新:【面试题含答案】http://bbs.9ria.com/thread-288394-1-1.html 高频问...
    好怕怕阅读 4,613评论 3 52
  • 远远的望着 扑面的冷风压垮了最后一次笑脸 徘徊的双眸 止不住的盯着高挂的圆月 直到 西边的一抹残霞 在我的头顶呼唤...
    与你余我阅读 193评论 0 0
  • 晚上暴走运河回来已经9点半多了,下地铁后往住处走。这时我有两个选择,一是穿过一条小路,路过一个小广场,但灯光灰暗,...
    Cherish5240阅读 573评论 0 0
  • 一、 阡陌红尘多错过, 天赐情缘空蹉跎; 前世今生因果定, 孰是孰非难评说。 二、 君住大东北, 我生俏江南; 回...
    远方孤雁阅读 133评论 0 4