惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
V
Visual Studio Blog
P
Privacy International News Feed
月光博客
月光博客
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
Lohrmann on Cybersecurity
N
News and Events Feed by Topic
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Apple Machine Learning Research
Apple Machine Learning Research
阮一峰的网络日志
阮一峰的网络日志
Webroot Blog
Webroot Blog
T
Threatpost
宝玉的分享
宝玉的分享
The Last Watchdog
The Last Watchdog
小众软件
小众软件
L
LINUX DO - 最新话题
C
Cisco Blogs
T
Troy Hunt's Blog
Schneier on Security
Schneier on Security
酷 壳 – CoolShell
酷 壳 – CoolShell
www.infosecurity-magazine.com
www.infosecurity-magazine.com
雷峰网
雷峰网
G
GRAHAM CLULEY
有赞技术团队
有赞技术团队
Know Your Adversary
Know Your Adversary
博客园 - 叶小钗
罗磊的独立博客
V
V2EX
博客园 - Franky
P
Proofpoint News Feed
SecWiki News
SecWiki News
腾讯CDC
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Jina AI
Jina AI
博客园 - 三生石上(FineUI控件)
S
Secure Thoughts
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Google DeepMind News
Google DeepMind News
Attack and Defense Labs
Attack and Defense Labs
人人都是产品经理
人人都是产品经理
The Cloudflare Blog
PCI Perspectives
PCI Perspectives
V2EX - 技术
V2EX - 技术
Google DeepMind News
Google DeepMind News
Last Week in AI
Last Week in AI
aimingoo的专栏
aimingoo的专栏
Cisco Talos Blog
Cisco Talos Blog
N
News and Events Feed by Topic
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
S
SegmentFault 最新的问题

博客园 - mcwind

Unity开发IOS游戏的优化建议 Unity3d 如何使用程序集(续) Unity 几个Mesh特效的例子 Unity动态载入文本数据的方法归类 Unity动态载入可执行代码的方法 Unity几个有用的游戏运动特效 Unity——Export/Import Package功能和项目管理的研究 三维游戏引擎——Unity 角色模型优化要点续:换肤和导入 三维游戏引擎——Unity 角色模型优化要点 3D游戏引擎——Unity 如何绘制反面 资源:三维CAD二次开发网站 [收藏]你想成为一名游戏策划吗? [收藏]学习:游戏策划入门 如何让你开发的游戏赚钱 如何更改MapleTr登录页面和标题 Some Useful Articles Of TDD (Test Driven Development) On MSDN 第一次试用Live Writer手记 收藏:航空航天院所 倒腾sql2005数据表内容的方法 - mcwind - 博客园
3D游戏引擎——Unity中网格合并示例研究
mcwind · 2011-02-18 · via 博客园 - mcwind

为了实现游戏人物外形的定制,专门研究了Unity示例程序 。对程序中动态载入身体各部位模型并进行组合的代码看了很久才明白。下面做一些备忘。

首先需要了解几个基本对象的结构

一、 SkinedMeshRender:该对象负责网格绘制。主要数据成员包括

var bones : Transform[] 骨骼

var materials : Material[] 材质

var sharedMesh : Mesh 网格

其中Mesh的主要成员是

vertices : Vector3[] 顶点

boneWeights : BoneWeight[] 骨骼权重

boneWeights数组与vertices数组对应,表示对应下标的顶点运动受骨骼影响的权重。BoenWeight结构记录了骨骼在SkinedMeshRender.bones数组中的索引。 

二、网格和材质的对应关系

      一张实际的网格只能施加一个材质。因此,当render所使用的mesh包含多个实际网格(sub mesh),它对每个sub mesh所施加的材质实际上是materials数组中对应下标的材质。

三、合并网格(CombineMeshes)函数的第二个参数是设置是否将多个子网格合并成一张实际的网格。正如前面所述,一个实际的网格只能施加一个材质,所以只有被合并的所有网格原来使用的就是同一个材质(即共享材质)时,将它们真正合并才能正确应用材质。否则,应该将该参数置为false,表示不实际合并这些sub mesh,而是将它们作为被合并后Mesh对象的sub mesh。

四、数组对应问题:网格顶点和骨骼、sub mesh和材质之间的对应都是通过数组下标进行的,所以操作时保证新生成的个数组下标对应关系正确是非常重要的。

这是例子中组合创建模型的主要函数,我将自己理解后的备注添加在里面。 

// Creates a character based on the currentConfiguration recycling a
    // character base, this way the position and animation of the character
    // are not changed.
    // 这个函数实际上并没有将各部分的子网格合并成一张网,而只是将他们合并到
    // 同一个Mesh下作为sub mesh。因为一张网格只能用一个材质,只有所有子网格
    // 都共享同一个材质时,合并成一张网才能保证材质应用正确。
    public GameObject Generate(GameObject root)
    {
        
// The SkinnedMeshRenderers that will make up a character will be
        // combined into one SkinnedMeshRenderers using multiple materials.
        // This will speed up rendering the resulting character.
        List<CombineInstance> combineInstances = new List<CombineInstance>();
        List
<Material> materials = new List<Material>();
        List
<Transform> bones = new List<Transform>();//获得构成骨架的所有Transform
        Transform[] transforms = root.GetComponentsInChildren<Transform>();
        
        
//一次处理构成身体的各部分
        foreach (CharacterElement element in currentConfiguration.Values)
        {
            
//GetSkinnedMeshRenderer()内部Instantiat了一个由该部分肢体Assets构成的
            //GameObject,并返回Unity自动为其创建SinkedMeshRender。
            SkinnedMeshRenderer smr = element.GetSkinnedMeshRenderer();//注意smr.materials中包含的材质数量和顺序与下面的sub mesh是对应的
            materials.AddRange(smr.materials);
            
for (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++)
            {
                CombineInstance ci 
= new CombineInstance();
                ci.mesh 
= smr.sharedMesh;
                ci.subMeshIndex 
= sub;
                combineInstances.Add(ci);
            }
// As the SkinnedMeshRenders are stored in assetbundles that do not
            // contain their bones (those are stored in the characterbase assetbundles)
            // we need to collect references to the bones we are using
            // 网格点与骨骼的对应关系是通过Mesh数据结构中的BoneWeight数组来实现的。该数组
            // 与网格顶点数组对应,记录了每个网格点受骨骼(骨骼记录在SinkedMeshRender的bones
            // 数组中,按下标索引)影响的权重。
            // 而此处,示例程序提供的肢体Assets并不包含骨骼,而是返回骨骼名称。因此,推断
            // GetBoneNames()返回的骨骼名称应该与实际骨骼数组的顺序相同。
            foreach (string bone in element.GetBoneNames())
            {
                foreach (Transform transform 
in transforms)
                {
                    
//通过名字找到实际的骨骼
                    if (transform.name != bone) continue;
                    bones.Add(transform);
                    
break;
                }
            }

            Object.Destroy(smr.gameObject);
        }

// Obtain and configure the SkinnedMeshRenderer attached to
        // the character base.
        // 至此,combineInstances、bones和materials三个数组中的数据对应关系是正确的。
        // 合并时,第二个参数是fals,表示保持子网格不变,只不过将它们统一到一个Mesh里
        // 来管理,这样只需采用一个SkinedMeshRender绘制,效率较高。
        SkinnedMeshRenderer r = root.GetComponent<SkinnedMeshRenderer>();
        r.sharedMesh 
= new Mesh();
        r.sharedMesh.CombineMeshes(combineInstances.ToArray(), 
falsefalse);
        r.bones 
= bones.ToArray();
        r.materials 
= materials.ToArray();
        
        
return root;
    }