网格及材质合并

本贴最后更新于 2294 天前,其中的信息可能已经时移世易

最近弄 unity 地图导出 pmd,发现好多网格合并代码用不了,我无法理解那些合并代码里的网格合并到底是什么意思.我说说我理解的网格合并
一般一个地图场景,有很多个 gameobject,每个有网格的 gameobject 上都有一个 meshfilter 组件和一个 meshrenderer 组件
而对 meshrenderer 组件来说,他可能具有一系列材质来对应渲染 meshfilter 里的每个 submesh,而每个材质都有自己的 shader 参数等.但是使用同一材质的,这些 shader 参数都一样
而对于每个 meshfilter 组件来说,他拥有顶点缓冲区,索引缓冲区,uv,color,normal,以及每个 submesh 对应了哪些顶点,以及这个 meshfilter 所在的 gameobject 的坐标(世界变换矩阵)
所以要合并,应该是先要统计这些信息,然后把所有信息存储到一个在原点,无旋转,无缩放的 gameobject 上,把所有网格信息,为其添加 meshfilter 组件放到 mesh 里,把所有材质放到 meshrenderer 里的 materials 里
而这个主要麻烦,就是网格和材质的对应关系,以及网格顶点信息的重新计算(要把原来 meshfilter 里的顶点算出世界坐标然后放到这里).这整个流程只要心里有数了,就可以写代码来实现了.

最后放一个组件代码,用于合并一系列的 gameobject 到一个 gameobject 里,主要是合并网格信息,材质等
这个组件没有合并同材质的 submesh,事实上,使用了同一材质的不同网格,可以合并到一个 submesh 里,这个点可以继续优化,但是现在是没处理的,我只是想把地图场景保存为 pmd 的,所以这部分忽略了
如果有不对的请指教

在一个 Editor 类里添加一个类
[CustomEditor(typeof(PMDSave))]
public class PMDSaveInspector : Editor
{
public override void OnInspectorGUI()
{
base.OnInspectorGUI();
if (GUILayout.Button("Combine"))
{
var obj = (PMDSave)target;
obj.CombineMapMesh();
}
}
}

然后在 Asset 目录下新增此组件
然后在地图的根节点处,挂入此脚本组件,点组件 Inspector 栏下的 CombineMesh 按钮,这个单网格就可以替代全部地图下的所有 gameobejct 了。

public class PMDSave : MonoBehaviour {
void CombineMapMesh()
{
Transform GenMapRoot = new GameObject("GenMapRoot").transform;
GenMapRoot.position = Vector3.zero;
GenMapRoot.localScale = Vector3.one;
GenMapRoot.rotation = Quaternion.identity;
MeshFilter mf = GenMapRoot.gameObject.AddComponent();
MeshRenderer mr = GenMapRoot.gameObject.AddComponent();
MeshFilter[] mfs = GetComponentsInChildren();
MeshRenderer[] mrs = GetComponentsInChildren();
//统计材质
List mat = new List();
for (int i = 0; i < mrs.Length; i++)
{
if (!mrs[i].enabled)
continue;
mat.AddRange(mrs[i].sharedMaterials);
}
mr.sharedMaterials = mat.ToArray();
int subMeshIndex = 0;
List vertex = new List();
List uv = new List();
List normal = new List();
List co = new List();
Dictionary<int, int[]> subMeshIndices = new Dictionary<int, int[]>();
//统计顶点.
for (int i = 0; i < mfs.Length; i++)
{
//隐藏了的不要
if (!mrs[i].enabled)
continue;
//推入顶点
int vertexIndex = vertex.Count;//记录推入之前的索引起始
for (int j = 0; j < mfs[i].sharedMesh.vertexCount; j++)
vertex.Add(mfs[i].transform.localToWorldMatrix * mfs[i].sharedMesh.vertices[j]);

	  //推入颜色,UV,法线
	  int uv_len = mfs[i].sharedMesh.uv.Length;
	  for (int j = 0; j < uv_len; j++)
		  uv.Add(mfs[i].sharedMesh.uv[j]);
	  int normal_len = mfs[i].sharedMesh.normals.Length;
	  for (int j = 0; j < normal_len; j++)
		  normal.Add(mfs[i].sharedMesh.normals[j]);
	  int color_len = mfs[i].sharedMesh.colors.Length;
	  for (int j = 0; j < color_len; j++)
		  co.Add(mfs[i].sharedMesh.colors[j]);
	  for (int j = 0; j < mfs[i].sharedMesh.subMeshCount; j++)
	  {
		  int[] indices = mfs[i].sharedMesh.GetIndices(j);
		  //把这些索引,都加上全局索引下标
		  for (int k = 0; k < indices.Length; k++)
			  indices[k] += vertexIndex;
		  subMeshIndices.Add(subMeshIndex, indices);
		  subMeshIndex++;
	  }
  }
  mf.sharedMesh = new Mesh();
  mf.sharedMesh.SetVertices(vertex);
  mf.sharedMesh.SetColors(co);
  mf.sharedMesh.uv = uv.ToArray();
  mf.sharedMesh.SetNormals(normal);
  mf.sharedMesh.subMeshCount = subMeshIndices.Count;
  vertexCnt = vertex.Count;
  foreach (var each in subMeshIndices)
	  mf.sharedMesh.SetIndices(each.Value, MeshTopology.Triangles, each.Key);

}
}

之前做这个 PMDSave 主要是来保存和官方 PMD 骨骼结构调整一致的流星.net 人物角色的,因为想让.net 流星角色也跳极乐净土,后来朋友想把场景也弄进去,就让我导出下场景
其实导出角色的时候,要导出骨骼结构,以及部分的腿部 IK 设置,以及蒙皮数据,所以导出角色,是比导出地图更麻烦的。
而一个 pmd 貌似只能保存一个 meshfilter 里的顶点和材质,所以后来就找代码合并网格和材质到一个物件上,找了半天都是些不能用的,我就想不通到底哪里理解不一样
于是自己折腾了一个,过几天朋友就会发出一个流星蝴蝶剑.net 角色跳舞蹈的视频。其实转换骨骼结构为官方 mmd 的标准骨架后,舞蹈动作都可以通用了,想想一群.net 的角色一起跳舞
就觉得好滑稽。

最后合并网格是用来减少 drawcall 的,相同材质的网格合并能减少 drawcall,所以在这个组件基础上,只要把材质相同的 submesh 合并到一个 submesh 里,drawcall 也就会被优化掉
不过合并后,成为一个整体,是无法控制地图上的任何元素的,因为都在一起了,要显示就一起显示,要隐藏就一起隐藏

  • Unity

    Unity 是由 Unity Technologies 开发的一个让开发者可以轻松创建诸如 2D、3D 多平台的综合型游戏开发工具,是一个全面整合的专业游戏引擎。

    25 引用 • 7 回帖 • 245 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • AngularJS

    AngularJS 诞生于 2009 年,由 Misko Hevery 等人创建,后为 Google 所收购。是一款优秀的前端 JS 框架,已经被用于 Google 的多款产品当中。AngularJS 有着诸多特性,最为核心的是:MVC、模块化、自动化双向数据绑定、语义化标签、依赖注入等。2.0 版本后已经改名为 Angular。

    12 引用 • 50 回帖 • 423 关注
  • V2EX

    V2EX 是创意工作者们的社区。这里目前汇聚了超过 400,000 名主要来自互联网行业、游戏行业和媒体行业的创意工作者。V2EX 希望能够成为创意工作者们的生活和事业的一部分。

    17 引用 • 236 回帖 • 418 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 9 关注
  • JWT

    JWT(JSON Web Token)是一种用于双方之间传递信息的简洁的、安全的表述性声明规范。JWT 作为一个开放的标准(RFC 7519),定义了一种简洁的,自包含的方法用于通信双方之间以 JSON 的形式安全的传递信息。

    20 引用 • 15 回帖 • 20 关注
  • FlowUs

    FlowUs.息流 个人及团队的新一代生产力工具。

    让复杂的信息管理更轻松、自由、充满创意。

    1 引用
  • JVM

    JVM(Java Virtual Machine)Java 虚拟机是一个微型操作系统,有自己的硬件构架体系,还有相应的指令系统。能够识别 Java 独特的 .class 文件(字节码),能够将这些文件中的信息读取出来,使得 Java 程序只需要生成 Java 虚拟机上的字节码后就能在不同操作系统平台上进行运行。

    180 引用 • 120 回帖 • 2 关注
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    129 引用 • 793 回帖
  • JRebel

    JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。

    26 引用 • 78 回帖 • 623 关注
  • Vim

    Vim 是类 UNIX 系统文本编辑器 Vi 的加强版本,加入了更多特性来帮助编辑源代码。Vim 的部分增强功能包括文件比较(vimdiff)、语法高亮、全面的帮助系统、本地脚本(Vimscript)和便于选择的可视化模式。

    27 引用 • 66 回帖
  • SOHO

    为成为自由职业者在家办公而努力吧!

    7 引用 • 55 回帖 • 92 关注
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 41 关注
  • 旅游

    希望你我能在旅途中找到人生的下一站。

    85 引用 • 895 回帖 • 1 关注
  • SQLServer

    SQL Server 是由 [微软] 开发和推广的关系数据库管理系统(DBMS),它最初是由 微软、Sybase 和 Ashton-Tate 三家公司共同开发的,并于 1988 年推出了第一个 OS/2 版本。

    19 引用 • 31 回帖 • 2 关注
  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1083 引用 • 3461 回帖 • 286 关注
  • Vditor

    Vditor 是一款浏览器端的 Markdown 编辑器,支持所见即所得、即时渲染(类似 Typora)和分屏预览模式。它使用 TypeScript 实现,支持原生 JavaScript、Vue、React 和 Angular。

    313 引用 • 1667 回帖 • 1 关注
  • OnlyOffice
    4 引用 • 23 关注
  • 尊园地产

    昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。

    1 引用 • 22 回帖 • 684 关注
  • Scala

    Scala 是一门多范式的编程语言,集成面向对象编程和函数式编程的各种特性。

    13 引用 • 11 回帖 • 108 关注
  • 链书

    链书(Chainbook)是 B3log 开源社区提供的区块链纸质书交易平台,通过 B3T 实现共享激励与价值链。可将你的闲置书籍上架到链书,我们共同构建这个全新的交易平台,让闲置书籍继续发挥它的价值。

    链书社

    链书目前已经下线,也许以后还有计划重制上线。

    14 引用 • 257 回帖 • 2 关注
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    333 引用 • 323 回帖 • 65 关注
  • sts
    2 引用 • 2 回帖 • 148 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖 • 2 关注
  • RIP

    愿逝者安息!

    8 引用 • 92 回帖 • 290 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 399 关注
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    492 引用 • 1383 回帖 • 375 关注
  • 百度

    百度(Nasdaq:BIDU)是全球最大的中文搜索引擎、最大的中文网站。2000 年 1 月由李彦宏创立于北京中关村,致力于向人们提供“简单,可依赖”的信息获取方式。“百度”二字源于中国宋朝词人辛弃疾的《青玉案·元夕》词句“众里寻他千百度”,象征着百度对中文信息检索技术的执著追求。

    63 引用 • 785 回帖 • 251 关注
  • WebSocket

    WebSocket 是 HTML5 中定义的一种新协议,它实现了浏览器与服务器之间的全双工通信(full-duplex)。

    48 引用 • 206 回帖 • 398 关注