3D模型人物换装系统

2023-11-11 19:20
文章标签 系统 模型 3d 人物 换装

本文主要是介绍3D模型人物换装系统,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

3D模型人物换装系统

  • 介绍
  • 遇到的问题
  • 问题修复
  • 具体实现换装
    • 1.准备所有模型部位和模型骨骼
      • 部位准备
      • 材质准备
      • 模型根骨骼准备
      • 创建文件夹将上述模型拖成预制体
      • 创建一个动画状态机给他们附上待机动画
    • 2.脚本驱动
      • Mesh合并代码 UCombineSkinnedMgr.cs
      • 创建Mesh以及实例化对象的代码 UCharacterController.cs
      • 测试换装调用 UCharacterManager.cs
    • 3.测试
  • 总结

介绍

本文使用2018.4.4和2020.3.26进行的测试

人物换装系统

人物换装系统下

人物换装对模型的还是有一定要求的,首先换装是要有多套模型的,通用做法是将人物的身体(包含头),衣服、裤子、鞋子、头发分别作为一个部位,然后将这几个部位动态合批成一个人物模型。有的人身体和头也是分开的部位这个看做模型那边的情况具体定。还有的不要身体直接分成衣服、裤子、双手、双脚、头(包含头发),我这里介绍的也是这种方法这样也能避免很多蒙皮问题。

遇到的问题

之前我们在做换装人物合批的时候也遇到过几个影响比较大的问题这里我拿出来说一下。

  1. 蒙皮外露穿模情况:这个也比较好理解其实就是人物在做动作的时候可能把人身体的一部分露出来了,比如说肩膀衣服露肉了这种穿模情况。
  2. 人物合批材质也需要合批那么身上的贴图合批会乱(这种情况我会在下面代码讲一下这里不详细说了)。

问题修复

针对蒙皮穿模的这种情况其实有很多办法

  1. 协商模型制作去掉能够被衣服挡住的蒙皮(但是这里要考虑所有的衣服,但凡有一个衣服是需要露这块的蒙皮那么就不能使用这个方法)
  2. 针对不同的衣服使用不同程度的蒙皮,比如A衣服要用露胳膊但是B衣服不露胳膊,那么做一个是删除胳膊的身体蒙皮A1和一个是不删除胳膊的身体蒙皮B1,这样A-A1绑定 B-B1绑定,需要穿A衣服的时候绑定A1身体,需要穿B衣服的时候绑定B1身体。
  3. 不要身体,身体直接和衣服裤子绑定在一起做,去掉身体部位(这个也是本文的做法)。

具体实现换装

1.准备所有模型部位和模型骨骼

部位准备

如下所示把其中一个赋值好材质的模型预制体拖上来,红框是每个需要合批的部位。
在这里插入图片描述

选中每一个部位然后Ctrl + D拷贝一份
在这里插入图片描述

将上述拷贝的部位改名如下
在这里插入图片描述
全部拷贝出来如下所示,这样的对象是包含根节点信息的
在这里插入图片描述
改名如下
眉毛和脸模型都是一样的就不需要合批处理只用一个即可
在这里插入图片描述

材质准备

模型材质赋值到上述对象上
在这里插入图片描述
在这里插入图片描述

模型根骨骼准备

使用刚才上面的那个白膜拖拽到场景上
在这里插入图片描述
解除预制体绑定先
在这里插入图片描述

删除下面其他的Mesh模型
在这里插入图片描述
在这里插入图片描述

修改名称如下
在这里插入图片描述

这里我只是用了一个模型来讲,换装肯定有多个模型,其他模型也是这样处理,第一个模型名字我后缀都是1,后面的模型依次后缀是2、3、4…

创建文件夹将上述模型拖成预制体

创建文件如下,并且将上面的拷贝的对象分别放到对应文件夹下做预制体
在这里插入图片描述

其他所有的需要换装的模型都这样处理放到对应文件夹下
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

上图中脸和眉毛只有一个是因为都是一样的只保留了一份

创建一个动画状态机给他们附上待机动画

创建一个动画控制器并双击打开
在这里插入图片描述
将待机动画拖拽上来
在这里插入图片描述
将之前的所有模型根节点中的动画控制器换成刚才我们生成的动画控制器
在这里插入图片描述
在这里插入图片描述

2.脚本驱动

Mesh合并代码 UCombineSkinnedMgr.cs

using UnityEngine;
using System.Collections.Generic;public class UCombineSkinnedMgr
{/// <summary>/// Combine SkinnedMeshRenderers together and share one skeleton./// Merge materials will reduce the drawcalls, but it will increase the size of memory. /// </summary>/// <param name="skeleton">combine meshes to this skeleton(a gameobject)</param>/// <param name="meshes">meshes need to be merged</param>/// <param name="combine">merge materials or not</param>public void CombineObject(GameObject skeleton, SkinnedMeshRenderer[] meshes){// Fetch all bones of the skeletonList<Transform> transforms = new List<Transform>();transforms.AddRange(skeleton.GetComponentsInChildren<Transform>(true));List<Material> materials = new List<Material>();//the list of materialsList<CombineInstance> combineInstances = new List<CombineInstance>();//the list of meshesList<Transform> bones = new List<Transform>();//the list of bones// Collect information from meshes//获取所有for (int i = 0; i < meshes.Length; i++){SkinnedMeshRenderer smr = meshes[i];materials.AddRange(smr.materials); // Collect materials// Collect meshesfor (int sub = 0; sub < smr.sharedMesh.subMeshCount; sub++){CombineInstance ci = new CombineInstance();ci.mesh = smr.sharedMesh;ci.subMeshIndex = sub;combineInstances.Add(ci);}// Collect bonesfor (int j = 0; j < smr.bones.Length; j++){int tBase = 0;for (tBase = 0; tBase < transforms.Count; tBase++){if (smr.bones[j].name.Equals(transforms[tBase].name)){bones.Add(transforms[tBase]);break;}}}}// Create a new SkinnedMeshRendererSkinnedMeshRenderer oldSKinned = skeleton.GetComponent<SkinnedMeshRenderer>();if (oldSKinned != null){GameObject.DestroyImmediate(oldSKinned);}SkinnedMeshRenderer r = skeleton.AddComponent<SkinnedMeshRenderer>();r.sharedMesh = new Mesh();r.sharedMesh.CombineMeshes(combineInstances.ToArray(), false, false);// Combine meshesr.bones = bones.ToArray();// Use new bonesr.materials = materials.ToArray();}
}

创建Mesh以及实例化对象的代码 UCharacterController.cs

using UnityEngine;public class UCharacterController
{/// <summary>/// GameObject reference/// </summary>public GameObject Instance = null;/// <summary>/// 换装总组装数量/// </summary>public int m_MeshCount = 9;public string Role_Skeleton;public string Role_Body;public string Role_Cloak;public string Role_Face;public string Role_Hair;public string Role_Hand;public string Role_Leg;public string Role_MainWeapon;public string Role_Retina;public string Role_SubWeapon;/// <summary>/// 创建对象/// </summary>/// <param name="job"></param>/// <param name="skeleton"></param>/// <param name="body"></param>/// <param name="cloak"></param>/// <param name="face"></param>/// <param name="hair"></param>/// <param name="hand"></param>/// <param name="leg"></param>/// <param name="mainweapon"></param>/// <param name="retina"></param>/// <param name="subweapon"></param>public UCharacterController(string job, string skeleton, string body, string cloak, string face, string hair, string hand, string leg, string mainweapon, string retina, string subweapon){Object res = Resources.Load("RoleMesh/" + job + "/" + job + "/" + skeleton);this.Instance = GameObject.Instantiate(res) as GameObject;this.Role_Skeleton = skeleton;this.Role_Body = body;this.Role_Cloak = cloak;this.Role_Face = face;this.Role_Hair = hair;this.Role_Hand = hand;this.Role_Leg = leg;this.Role_MainWeapon = mainweapon;this.Role_Retina = retina;this.Role_SubWeapon = subweapon;string[] equipments = new string[m_MeshCount];equipments[0] = "Body/" + Role_Body;equipments[1] = "Cloak/" + Role_Cloak;equipments[2] = "Face/" + Role_Face;equipments[3] = "Hair/" + Role_Hair;equipments[4] = "Hand/" + Role_Hand;equipments[5] = "Leg/" + Role_Leg;equipments[6] = "Mainweapon/" + Role_MainWeapon;equipments[7] = "Retina/" + Role_Retina;equipments[8] = "Subweapon/" + Role_SubWeapon;SkinnedMeshRenderer[] meshes = new SkinnedMeshRenderer[m_MeshCount];GameObject[] objects = new GameObject[m_MeshCount];for (int i = 0; i < equipments.Length; i++){res = Resources.Load("RoleMesh/" + job + "/" + equipments[i]);objects[i] = GameObject.Instantiate(res) as GameObject;meshes[i] = objects[i].GetComponentInChildren<SkinnedMeshRenderer>();}UCharacterManager.Instance.CombineSkinnedMgr.CombineObject(Instance, meshes);for (int i = 0; i < objects.Length; i++){GameObject.DestroyImmediate(objects[i].gameObject);}}public void Delete(){GameObject.Destroy(Instance);}/// <summary>/// 部位换装/// </summary>/// <param name="path">路径</param>/// <param name="index">切换部位</param>/// <param name="equipment"></param>/// <param name="combine"></param>public void ChangeEquipments(string path, int index, int equipmentId){switch (index){case 0:Role_Body = "Body" + equipmentId;break;case 1:Role_Cloak = "Cloak" + equipmentId;break;case 2:Delete();this.Instance = GameObject.Instantiate(Resources.Load("RoleMesh/" + path + "/" + path + "/" + path + equipmentId)) as GameObject;Role_Hair = "Hair" + equipmentId;break;case 3:Role_Hand = "Hand" + equipmentId;break;case 4:Role_Leg = "Leg" + equipmentId;break;case 5:Role_MainWeapon = "Mainweapon" + equipmentId;Role_SubWeapon = "Subweapon" + equipmentId;break;}string[] equipments = new string[m_MeshCount];equipments[0] = "Body/" + Role_Body;equipments[1] = "Cloak/" + Role_Cloak;equipments[2] = "Face/" + Role_Face;equipments[3] = "Hair/" + Role_Hair;equipments[4] = "Hand/" + Role_Hand;equipments[5] = "Leg/" + Role_Leg;equipments[6] = "Mainweapon/" + Role_MainWeapon;equipments[7] = "Retina/" + Role_Retina;equipments[8] = "Subweapon/" + Role_SubWeapon;Object res = null;SkinnedMeshRenderer[] meshes = new SkinnedMeshRenderer[m_MeshCount];GameObject[] objects = new GameObject[m_MeshCount];for (int i = 0; i < equipments.Length; i++){res = Resources.Load("RoleMesh/" + path + "/" + equipments[i]);objects[i] = GameObject.Instantiate(res) as GameObject;meshes[i] = objects[i].GetComponentInChildren<SkinnedMeshRenderer>();}UCharacterManager.Instance.CombineSkinnedMgr.CombineObject(Instance, meshes);for (int i = 0; i < objects.Length; i++){GameObject.DestroyImmediate(objects[i].gameObject);}}}

测试换装调用 UCharacterManager.cs

using UnityEngine;
using System.Collections.Generic;/// <summary>
/// 换装管理器
/// </summary>
public class UCharacterManager : MonoBehaviour
{public static UCharacterManager Instance;private UCombineSkinnedMgr skinnedMgr = null;public UCombineSkinnedMgr CombineSkinnedMgr { get { return skinnedMgr; } }private int characterIndex = 0;private Dictionary<int, UCharacterController> characterDic = new Dictionary<int, UCharacterController>();public UCharacterManager(){skinnedMgr = new UCombineSkinnedMgr();}private void Awake(){Instance = this;}public UCharacterController mine;private void Start(){mine = Generatecharacter("Axceler", "Axceler1", "Body5", "Cloak5", "Face", "Hair1", "Hand5", "Leg5", "Mainweapon5", "Retina", "Subweapon5");}private void Update(){if (Input.GetKeyDown(KeyCode.Space)){ChangeRole();}//衣服if (Input.GetKeyDown(KeyCode.Q)){mine.ChangeEquipments("Axceler", 0, Random.Range(1, 8));}//飘带if (Input.GetKeyDown(KeyCode.W)){mine.ChangeEquipments("Axceler", 1, Random.Range(1, 7));}//头发if (Input.GetKeyDown(KeyCode.E)){mine.ChangeEquipments("Axceler", 2, Random.Range(1, 8));}//手套if (Input.GetKeyDown(KeyCode.A)){mine.ChangeEquipments("Axceler", 3, Random.Range(1, 8));}//腿饰if (Input.GetKeyDown(KeyCode.S)){mine.ChangeEquipments("Axceler", 4, Random.Range(1, 8));}//武器if (Input.GetKeyDown(KeyCode.D)){mine.ChangeEquipments("Axceler", 5, Random.Range(1, 8));}}public void ChangeRole(){if (mine != null){mine.Delete();}int a = Random.Range(1, 8);int b = Random.Range(1, 8);int c = Random.Range(1, 7);int d = Random.Range(1, 8);int e = Random.Range(1, 8);int f = Random.Range(1, 8);int g = Random.Range(1, 8);mine = Generatecharacter("Axceler", "Axceler" + a, "Body" + b, "Cloak" + c, "Face", "Hair" + a, "Hand" + d, "Leg" + e, "Mainweapon" + f, "Retina", "Subweapon" + g);}#region 创建人物模型骨骼public UCharacterController Generatecharacter(string job, string skeleton, string body, string cloak, string face, string hair, string hand, string leg, string mainweapon, string retina, string subweapon){UCharacterController instance = new UCharacterController(job, skeleton, body, cloak, face, hair, hand, leg, mainweapon, retina, subweapon);characterDic.Add(characterIndex, instance);characterIndex++;return instance;}#endregion
}

3.测试

随便创建一个场景然后在随便一个对象上面挂载UCharacterManager.cs脚本运行测试在这里插入图片描述

总结

这个方案是将所有的Mesh合并成一个Mesh,材质球是叠加的并没有合批材质
在这里插入图片描述

注意:如果要想将模型完全合批需要将所有图都设置可读可写,这里放到另一篇文章去讲,这里先不讲合批材质的方法在这里插入图片描述
本文资源
如果文章对你有帮助的话留一个免费的关注和点赞吧

这篇关于3D模型人物换装系统的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/392291

相关文章

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

基于人工智能的图像分类系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 图像分类是计算机视觉中的一个重要任务,目标是自动识别图像中的对象类别。通过卷积神经网络(CNN)等深度学习技术,我们可以构建高效的图像分类系统,广泛应用于自动驾驶、医疗影像诊断、监控分析等领域。本文将介绍如何构建一个基于人工智能的图像分类系统,包括环境

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

【区块链 + 人才服务】可信教育区块链治理系统 | FISCO BCOS应用案例

伴随着区块链技术的不断完善,其在教育信息化中的应用也在持续发展。利用区块链数据共识、不可篡改的特性, 将与教育相关的数据要素在区块链上进行存证确权,在确保数据可信的前提下,促进教育的公平、透明、开放,为教育教学质量提升赋能,实现教育数据的安全共享、高等教育体系的智慧治理。 可信教育区块链治理系统的顶层治理架构由教育部、高校、企业、学生等多方角色共同参与建设、维护,支撑教育资源共享、教学质量评估、