当前位置: 首页 > news >正文

Unity 网格模型及优化

一个模型中可以包含很多网格,一个模型可以由多个网格组成。在Unity3D中一个网格可以由多个子网格(Sub-Mesh)组成。

在渲染引擎的时候,每个子网格都要匹配一个材质球来做渲染,实际上一个子网格本身就是一个个普通的模型,它由很多个三角形构成,也需要材质球支持达成渲染。

为什么要有子网格?

美术人员在制作3D模型时,为什么不打网格编成一个,而要制作成多个子网格?

  1. 模型中的一部分网格用一种材质球来表现效果,另一部分用另一种。因为一个网格只能对应一个材质球所以要拆开。
  2. 模型中的莫部分贴图在众多模型中共同时使用的频率比较高,为了不重复制作以减少重复劳动,原本可以作为一个整体的模型会将公共材质部分单独拆分出来让一部分模型使用同一个材质球。
  3. 制作动画时,由于动画过于复杂,如果使用同一个模型去表现,则骨骼的数量就会成倍的增加,为了更好的表现动画,也为了能降低骨骼的使用数量,要拆分出来一部分模型,让它们单独成为模型动画的一部分。

从以上看多个子网格比单个网格要灵活的多,但它也有缺点,由于每个子网格都有材质球,导致子网格越多,drawcall 也越多。

子网格功能虽然强大,但也需要注意性能开销。 

动态合并3D模型

我们知道每个网格模型都需要有一个材质球支持,导致每个模型都会产生一个drawcall,过多的drawcall,CPU会忙于发送状态数据给GPU,GPU大多数时间处于等待状态,这将导致画面帧率下降及强烈的画面卡顿。

实际项目中通常会遇到这样的问题:场景中会有很多物体,人物、建筑、路标、景观、树木、石头、碎块、花草等等,有的物体可能不只有一个材质球,相同物体共用一个,当然不同的物体也可能使用相同的材质球,这些物体如果不做任何优化处理,就会产生很多drawcall。

合并3D模型的主要目的就是减少drawcall,通过减少材质球的提交次数来完成优化,简单的说就是大拥有相同材质球的模型合并成一个模型和一个材质球,从而减少向GPU提交的drawcall数量,让GPU并行处理数据时更快更顺畅。

在Unity中分为动态合批和静态合批两种,使用它们的前提条件是模型物体必须具有相同的材质球,除了这个必要条件外还有其他条件。

动态批处理

开启动态批处理时,Unity会将场景中的某些物体自动批处理成为同一个drawcall,需要满足:

  1. 顶点数目要少于900个顶点网格。如果你的着色器使用顶点坐标、法线和单独的UV,那么顶点个数就要在300以内,如果着色器使用顶点坐标、法线、UV0、UV1和切线,则顶点要在180个内的网格。
  2. 两个物体的缩放单位要相同,比如A(1,1,1) B(1,1,2)就没法合批了
  3. 使用相同材质球
  4. 多管线(Pipeline)着色器会中断动态合批,支持多个灯光的前置渲染,它们增加了多个渲染通道,因此无法批处理。旧系统中的延迟渲染路径(灯光前置通道)关闭了动态批处理,因为它需要绘制物体两次。多有多个Pass的着色器增加了渲染管道,不会被动态批处理。

从上面看动态批处理的条件非常苛刻,真实的项目里很多模型时不符合动态批处理要求的。另外动态批处理要消耗CPU将所有物体的顶点转换到世界空间,合并网格也会带来开销。一味的减小drawcall并不是万能的,开销取决于很多因素,drawcall的开销主要是由图形API的调用造成的,如果节省的开销小于准备工作的开销,就得不偿失。例如,一个主机设备或流行的API(Apple Metal这样的),drawcall的开销通常很低,那么对于它来说动态批处理就没有什么优势了。

静态批处理

静态批处理允许引擎离线的情况下进行模型合并的批处理,减少drawcall。无论模型有多大,只要使用同一个材质球都会被静态批处理优化。它通常比动态批处理更有用(因为不需要实时转换顶点来消耗CPU),缺点是消耗了更多的内存。

Unity中需要对物体设置静态标记,也就是不能移动,缩放,旋转的物体。

如果一些物体之前共用一个模型,那么unity会复制这些物体的模型用来合并,所以使内存增大。个人觉得使用内存换CPU是值得的,然而如果用100GB的内存换千分之一的CPU效率那就不值得了。

静态批处理的具体流程是,将所有静态标记的物体放入世界空间->通过材质球分类标准将它们分别合并->构建成一个大的顶点集合和索引缓存。从而减少drawcall。

从技术上来说,静态批处理并没有节省3D API drawcall的数量,但它能减少因3D API之间的状态改变导致的消耗。大多数平台上,批处理限制在64000个顶点和64000个索引内(OpenGLES上为48000个,MacOS上为32000个),所以如果批处理过多,需要取消一些静态标记。

自己编写合并3D模型程序

Unity3D中:

  • Mesh类有一个CombineMeshes的接口,提供了合并入口。
  • MeshFilter类,是承载数据类。
  • MeshRenderer类,是绘制网格的类

在使用之前我们还需要了解

MeshFilter和MeshRenderer中的mesh与shareMesh、material及shareMaterial的区别

mesh和material都是实例型的变量,对mesh和material执行任何操作都是额外复制一份后重新赋值,即使只是get操作,同样也会执行复制操作。而就是说,对mesh和material进行操作后,就会变成另一个实例。sharedMesh和shareMaterial与前面的两个变量不同,它们是共享型的。多个模型可以共同指定一个sharedMesh和sharedMaterial,当年修改了shareMesh或sharedMaterial里面的参数,指向同一个sharedMaterial和sharedMesh的多个模型就会同时改变效果。

materials和sharedMaterialsd的区别

materials是实例型,sharedMaterials是共享型,两个都是数组。material和sharedMaterial只针对主网格。而就是说material等于materials[0],sharedMaterial等于sharedMaterial[0]。

网格、MeshFilter、MeshRenderer的关系

网格时数据资源,它可以有自己的资源文件,通常时.FBX文件。网格里存储了顶点、UV、顶点颜色、三角形、切线、法线、骨骼、骨骼权重等提供渲染所必要的数据。

MeshFilter是一个承载网格数据的类,网格被实例化后存储在MeshFilter类中。MeshFilter包含两种类型,即实例型(mesh)和共享型变量(sharedMesh)。

MeshRenderer具有渲染功能,它会提取MeshFilter中的网格数据,结合自身的materials或sharedMaterials进行渲染。

Mesh.CombineInstance合并数据实例类

合并时需要为每个将要合并的网格创建一个CombineInstance实例,并往里放入mesh、subMesh的索引,lightmap的缩放和偏移,realtimeLightmap的缩放和偏移,以及世界坐标矩阵。它承载了所有需要需要合并的数据。        

现在我们掌握了关于合并网格的理论知识我们来实现以下,这里我从Unity 商店下载了一个模型,我将角色手中的 剑和盾牌进行了合并,并生成了一个新的GameObject 效果如下:

using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;public class CombineTest : MonoBehaviour
{public List<MeshFilter> combineMeshFilers = new List<MeshFilter>();// Start is called before the first frame updatevoid Start(){CreateCombineModel();}private void CreateCombineModel(){// 提取网格所需的资源List<Material> materials = new List<Material>();CombineInstance[] combines = new CombineInstance[combineMeshFilers.Count];for (int i = 0; i < combineMeshFilers.Count; i++){CombineInstance tmpInstance = new CombineInstance();MeshFilter tmpFilter = combineMeshFilers[i];tmpInstance.mesh = tmpFilter.sharedMesh;tmpInstance.transform = tmpFilter.transform.localToWorldMatrix;combines[i] = tmpInstance;MeshRenderer renderer = tmpFilter.transform.GetComponent<MeshRenderer>();for (int j = 0; j < renderer.sharedMaterials.Length; j++){if (!materials.Contains(renderer.sharedMaterials[j]))materials.Add(renderer.sharedMaterials[j]);}}// 将所有网格合并成一个新的网格Mesh mesh = new Mesh();MeshFilter combineFilter = transform.AddComponent<MeshFilter>();MeshRenderer combineRenderer = transform.AddComponent<MeshRenderer>();mesh.CombineMeshes(combines);combineFilter.mesh = mesh;combineRenderer.materials = materials.ToArray();}
}

相关文章:

Unity 网格模型及优化

一个模型中可以包含很多网格&#xff0c;一个模型可以由多个网格组成。在Unity3D中一个网格可以由多个子网格&#xff08;Sub-Mesh)组成。 在渲染引擎的时候&#xff0c;每个子网格都要匹配一个材质球来做渲染&#xff0c;实际上一个子网格本身就是一个个普通的模型&#xff0…...

离线 快速搭建 docker docker-compose k8s 环境

所需资源 sealos_5.0.1_linux_arm64.tar.gzkubernetes.tar等docker-compose-linux-aarch64 离线安装sealos&#xff0c;用于安装k8sdocker 首先安装sealos工具 tar zxvf sealos_5.0.1_linux_arm64.tar.gz sealos && chmod x sealos && mv sealos /usr/bin*…...

Excel根据条件动态索引单元格范围

假如我是一个老板&#xff0c;下面有数不胜数的员工&#xff0c;我要检查他们每周的工作产出&#xff0c;列一个排行榜&#xff0c;提高员工积极性&#xff0c;毕竟多劳多得嘛。 每天去手动统计&#xff0c;未免显得不太聪明&#xff0c;我们可以利用公式来解决这个问题。 我们…...

【计算机网络五】HTTP协议!网站运行的奥秘!

目录 HTTP协议 1.HTTP是什么&#xff1f; 2.Fiddler抓包查看HTTP协议格式 3.HTTP请求 4.HTTP响应 HTTP协议 1.HTTP是什么&#xff1f; HTTP ( 全称为 " 超文本传输协议 ") 诞生与 1991 年 . 目前已经发展为最主流使用的一种应用层协议 . HTTP 的前几个版本…...

开源模型应用落地-qwen模型小试-Qwen2.5-7B-Instruct-tool usage入门-Qwen-Agent深入学习(四)

一、前言 Qwen-Agent 是一个利用开源语言模型Qwen的工具使用、规划和记忆功能的框架。其模块化设计允许开发人员创建具有特定功能的定制代理,为各种应用程序提供了坚实的基础。同时,开发者可以利用 Qwen-Agent 的原子组件构建智能代理,以理解和响应用户查询。 本篇将换一种方…...

stream学习

Stream流 定义 Steam流&#xff0c;用于操作集合或者数组中的数据&#xff0c;大量结合了Lamda表达式的语法风格&#xff0c;代码简洁。 重点&#xff1a; 流只能收集一次 ​ 获取Stream流 Stream流要与数据源建立连接。 1.list ​ 直接调用steam()即可 // list List<Stri…...

【数据结构】实验二 单链表的基本操作

一、实验目的 掌握线性表的链式存储结构的表示和实现方法。 掌握链表基本操作的算法实现&#xff0c;以及对相应算法的性能分析。 二、实验内容 1&#xff09;根据输入的一系列整数&#xff0c;以0标志结束&#xff0c;用头插法建立单链表&#xff0c;并输出单链表中各元素…...

SQL 分组查询中的非聚合列要求及实例解析

在 SQL 查询中&#xff0c;当我们对数据进行分组时&#xff0c;通常会用到 GROUP BY 子句。SQL 标准要求&#xff1a;所有非聚合列&#xff08;即没有使用聚合函数的列&#xff09;都必须出现在 GROUP BY 子句中&#xff0c;或者是聚合函数的结果。这篇文章通过一个实例来说明这…...

Unity中实现战斗帧同步的高级技术

一、帧同步的基本原理 帧同步&#xff08;Frame Synchronization&#xff09;在网络游戏中指的是在每一帧上保证所有玩家所看到的游戏状态一致&#xff0c;而不是每个玩家单独计算自己的状态。实现帧同步通常需要每个客户端仅发送用户输入到服务器&#xff0c;并由服务器进行全…...

Qt 正则表达式提取文件中的 USB 设备 ID

Qt 正则表达式提取文件中的 USB 设备 ID flyfish 文档内容 Bus: 001 Device: 001 Description: 1d6b:0002 Linux Foundation 2.0 root hub Bus: 002 Device: 003 Description: 0e0f:0002 , Inc. USB Hub Bus: 002 Device: 002 Description: 0e0f:0003 , Inc. Mouse Bus: 002…...

使用 Python 和 OpenCV 实现摄像头人脸检测并截图

概述 在现代应用中&#xff0c;人脸检测是一项非常重要的技术&#xff0c;广泛应用于安全监控、身份验证等领域。本文将详细介绍如何使用 Python 和 OpenCV 库实现摄像头人脸检测并截图&#xff0c;并通过具体的代码示例来展示整个过程。 环境准备 在开始编写代码之前&#…...

【二叉搜素树】——LeetCode二叉树问题集锦:6个实用题目和解题思路

文章目录 计算布尔二叉树的值求根节点到叶节点的数字之和二叉树剪枝验证二叉搜索树二叉搜索树中第K小的元素二叉树的所有路径 计算布尔二叉树的值 解题思路&#xff1a; 这是一个二叉树的布尔评估问题。树的每个节点包含一个值&#xff0c;其中叶子节点值为 0 或 1&#xff0…...

【计算机视觉】FusionGAN

1. FusionGAN论文阅读 abreheret/FusionGAN: Pytorch implementation of "Generating a Fusion Image: One’s Identity and Another’s Shape" 1.1. WHY 在现实世界中,将对象或人物转换为期望的形状是一种常用技术,但现有的图像翻译方法在处理身份和形状时存在…...

问:SQL优化,七条实践总结?

SQL语句优化是数据库性能调优的重要部分&#xff0c;通过合理的优化可以显著提升查询速度和系统性能。文章总结几种常见SQL语句优化方法。 1. 优化Where子句的顺序 原则&#xff1a;表之间的连接条件应写在其他Where条件之前&#xff0c;能够过滤掉最大数量记录的条件应优先写…...

unity单例模式的不同声明(待完善

总结&#xff1a; 这段代码实现了一个泛型单例模式&#xff08;Singleton Pattern&#xff09;&#xff0c;用于确保某个类&#xff08;由泛型参数 T 指定&#xff09;在整个应用程序中只有一个实例&#xff0c;并且在第一次访问时才创建该实例。该模式保证了该实例的全局唯一…...

大模型在蓝鲸运维体系应用——蓝鲸运维开发智能助手

本文来自腾讯蓝鲸智云社区用户: CanWay 背景 1、运维转型背景 蓝鲸平台从诞生之初&#xff0c;就一直在不遗余力地推动运维转型&#xff0c;让运维团队可以通过一体化PaaS平台&#xff0c;快速编写脚本&#xff0c;编排流程&#xff0c;开发运维工具&#xff0c;从被动地提供…...

vue2,vue3响应式的理解

vue2的话主要使用的是defineProperty对已有属性添加get,set,从而完成对数据的响应式控制&#xff0c;但每次需要for循环对属性进行遍历 function DefineReactive(target, key, value) {//存在多层嵌套的objectObserver(value);Object.defineReactive(target, key, {get() {retu…...

群控系统服务端开发模式-应用开发-前端退出功能

我们从未登录一直到退出&#xff0c;现在已经登录到操作&#xff0c;现在完成退出。退出有两种情况下会退出&#xff1a;第一种情况下是手动点击退出按钮&#xff0c;第二种情况下是登录过期时间到了自动退出的。 一、手动退出 因退出及个人信息页面都在公有页面&#xff0c;所…...

Web入门

Spring 官网&#xff1a;Spring | Home Spring是一个开源的Java企业级应用开发框架。Spring的主要目的是使Java EE&#xff08;Java Platform, Enterprise Edition&#xff09;开发更容易&#xff0c;并且通过提供一系列丰富的库和接口来促进良好编程实践&#xff0c;是…...

基于SpringBoot网上超市的设计与实现录像

基于SpringBoot网上超市的设计与实现录像 SpringBoot网上超市的设计与实现录像...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)

说明&#xff1a; 想象一下&#xff0c;你正在用eNSP搭建一个虚拟的网络世界&#xff0c;里面有虚拟的路由器、交换机、电脑&#xff08;PC&#xff09;等等。这些设备都在你的电脑里面“运行”&#xff0c;它们之间可以互相通信&#xff0c;就像一个封闭的小王国。 但是&#…...

<6>-MySQL表的增删查改

目录 一&#xff0c;create&#xff08;创建表&#xff09; 二&#xff0c;retrieve&#xff08;查询表&#xff09; 1&#xff0c;select列 2&#xff0c;where条件 三&#xff0c;update&#xff08;更新表&#xff09; 四&#xff0c;delete&#xff08;删除表&#xf…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

CRMEB 框架中 PHP 上传扩展开发:涵盖本地上传及阿里云 OSS、腾讯云 COS、七牛云

目前已有本地上传、阿里云OSS上传、腾讯云COS上传、七牛云上传扩展 扩展入口文件 文件目录 crmeb\services\upload\Upload.php namespace crmeb\services\upload;use crmeb\basic\BaseManager; use think\facade\Config;/*** Class Upload* package crmeb\services\upload* …...