网格及材质合并

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

最近弄 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 回帖 • 117 关注

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • 持续集成

    持续集成(Continuous Integration)是一种软件开发实践,即团队开发成员经常集成他们的工作,通过每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽早地发现集成错误。

    15 引用 • 7 回帖 • 3 关注
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 182 关注
  • Jenkins

    Jenkins 是一套开源的持续集成工具。它提供了非常丰富的插件,让构建、部署、自动化集成项目变得简单易用。

    54 引用 • 37 回帖
  • Netty

    Netty 是一个基于 NIO 的客户端-服务器编程框架,使用 Netty 可以让你快速、简单地开发出一个可维护、高性能的网络应用,例如实现了某种协议的客户、服务端应用。

    49 引用 • 33 回帖 • 44 关注
  • Caddy

    Caddy 是一款默认自动启用 HTTPS 的 HTTP/2 Web 服务器。

    10 引用 • 54 回帖 • 184 关注
  • HHKB

    HHKB 是富士通的 Happy Hacking 系列电容键盘。电容键盘即无接点静电电容式键盘(Capacitive Keyboard)。

    5 引用 • 74 回帖 • 523 关注
  • OpenStack

    OpenStack 是一个云操作系统,通过数据中心可控制大型的计算、存储、网络等资源池。所有的管理通过前端界面管理员就可以完成,同样也可以通过 Web 接口让最终用户部署资源。

    10 引用 • 5 关注
  • Solo

    Solo 是一款小而美的开源博客系统,专为程序员设计。Solo 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    1444 引用 • 10083 回帖 • 499 关注
  • 锤子科技

    锤子科技(Smartisan)成立于 2012 年 5 月,是一家制造移动互联网终端设备的公司,公司的使命是用完美主义的工匠精神,打造用户体验一流的数码消费类产品(智能手机为主),改善人们的生活质量。

    4 引用 • 31 回帖 • 3 关注
  • FFmpeg

    FFmpeg 是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。

    23 引用 • 32 回帖 • 5 关注
  • etcd

    etcd 是一个分布式、高可用的 key-value 数据存储,专门用于在分布式系统中保存关键数据。

    6 引用 • 26 回帖 • 545 关注
  • Pipe

    Pipe 是一款小而美的开源博客平台。Pipe 有着非常活跃的社区,可将文章作为帖子推送到社区,来自社区的回帖将作为博客评论进行联动(具体细节请浏览 B3log 构思 - 分布式社区网络)。

    这是一种全新的网络社区体验,让热爱记录和分享的你不再感到孤单!

    134 引用 • 1128 回帖 • 110 关注
  • 禅道

    禅道是一款国产的开源项目管理软件,她的核心管理思想基于敏捷方法 scrum,内置了产品管理和项目管理,同时又根据国内研发现状补充了测试管理、计划管理、发布管理、文档管理、事务管理等功能,在一个软件中就可以将软件研发中的需求、任务、bug、用例、计划、发布等要素有序的跟踪管理起来,完整地覆盖了项目管理的核心流程。

    10 引用 • 15 回帖 • 2 关注
  • CodeMirror
    2 引用 • 17 回帖 • 176 关注
  • Lute

    Lute 是一款结构化的 Markdown 引擎,支持 Go 和 JavaScript。

    29 引用 • 202 回帖 • 37 关注
  • 倾城之链
    23 引用 • 66 回帖 • 173 关注
  • OpenResty

    OpenResty 是一个基于 NGINX 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

    17 引用 • 49 关注
  • Hibernate

    Hibernate 是一个开放源代码的对象关系映射框架,它对 JDBC 进行了非常轻量级的对象封装,使得 Java 程序员可以随心所欲的使用对象编程思维来操纵数据库。

    39 引用 • 103 回帖 • 727 关注
  • 服务器

    服务器,也称伺服器,是提供计算服务的设备。由于服务器需要响应服务请求,并进行处理,因此一般来说服务器应具备承担服务并且保障服务的能力。

    125 引用 • 585 回帖
  • PHP

    PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。

    167 引用 • 408 回帖 • 484 关注
  • 开源中国

    开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。

    7 引用 • 86 回帖 • 1 关注
  • 周末

    星期六到星期天晚,实行五天工作制后,指每周的最后两天。再过几年可能就是三天了。

    14 引用 • 297 回帖
  • B3log

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

    1062 引用 • 3455 回帖 • 147 关注
  • 机器学习

    机器学习(Machine Learning)是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。

    77 引用 • 37 回帖 • 1 关注
  • GAE

    Google App Engine(GAE)是 Google 管理的数据中心中用于 WEB 应用程序的开发和托管的平台。2008 年 4 月 发布第一个测试版本。目前支持 Python、Java 和 Go 开发部署。全球已有数十万的开发者在其上开发了众多的应用。

    14 引用 • 42 回帖 • 831 关注
  • OneDrive
    2 引用
  • 钉钉

    钉钉,专为中国企业打造的免费沟通协同多端平台, 阿里巴巴出品。

    15 引用 • 67 回帖 • 259 关注