首页 > 其他分享 >Unity用户手册-Mesh合批

Unity用户手册-Mesh合批

时间:2022-11-29 21:41:12浏览次数:47  
标签:obj 渲染 对象 模型 mesh Unity Mesh 用户手册


Mesh合批

把很多静止的模型,标记为Batching Static,原本需要把模型一个个送到GPU渲染,Unity会把相同材质相同纹理相同Shader的模型合批成一个大的模型,送到GPU进行渲染,这样就减少DrawCall。可以调用Mesh.CombineMeshes方法,创建CombineInstance[]数组,进行合批。

 

Static Batching

要求:必须使用相同的材质material,相同的纹理Texture,然后在编辑器里设置为static batching的。

特点:静态批是无法运动的。

所以一般制作流程上,对于场景这些静态的物体都采用静态批,美术会根据场景的规模,将相邻的一片物件的贴图合并到一张或几张1024或512的大图上,这样这些物件可以使用同一个material,就可以静态批在一起,大幅节省DrawCall。 静态批的时候Unity3d会在运行时生成一个合并的大模型,并且为这个模型指定一张共同的贴图,所以这个批在一起的数量是有限的,如果批在一起的定点数过多,它就会自动分成两个批,一个静态批的上限为65535,这是因为Unity默认使用16位Mesh Index Buffer。

 

Dynamic batching

Dynamic batching的原理也很简单,在进行场景绘制之前将所有的共享同一材质的模型的顶点信息变换到世界空间中,然后通过一次Draw call绘制多个模型,达到合批的目的。模型顶点变换的操作是由CPU完成的,所以这会带来一些CPU的性能消耗。并且计算的模型顶点数量不宜太多,否则CPU串行计算耗费的时间太长会造成场景渲染卡顿,所以Dynamic batching只能处理一些小模型。

要求如下:

  • 目前Unity限制能进行Dynamic batching的模型最高能有900个顶点属性。这里注意不是900个顶点,而是900个顶点属性。如果我们在Shader中使用了Vertex Position,Normal and single UV,那么能够进行Dynamic batching的模型最多只能够有300个顶点。如果我们在Shader中使用了Vertex Position、Normal、UV0、UV1 and Tangent那么顶点的数量就减少到180个。
  • 批在一起的所有的模型应用同样的缩放值
  • 使用相同的材质
  • 相同的一张lightmap
  • 不能使用多pass的shader
  • 不能接收阴影

 

DrawCall

DrawCall是CPU通过底层图像编程接口发出的渲染命令,GPU读取渲染命令执行渲染操作。

 

过多的DrawCall影响绘制的原因:

主要是每次绘制时,CPU通过底层图像编程接口发出渲染命令DrawCall,而每个DrawCall需要很多准备工作,检测渲染状态、提交渲染数据、提交渲染状态,而GPU本身可以很快处理完渲染任务。DrawCall过多,CPU负载过多,而GPU性能闲置。

 

渲染状态

渲染状态定义场景中的网格是怎样被渲染出来的。例如使用哪个顶点着色器、哪个片元着色器、光源属性、材质等。如果没有更改渲染状态,所有的网格将使用同一种渲染状态。

 

Mesh.CombineMeshes方法

  • 遍历当前对象和子对象的MeshFilter组件,创建CombineInstance[]数组。
  • 获取filter.sharedMesh赋值给combine[i].mesh。
  • obj.transform.worldToLocalMatrix * filter.transform.localToWorldMatrix得到当前对象和子对象在本地空间的矩阵,赋值给combine[i].transform。
  • 遍历子对象所有的MeshRenderer组件,获取render.sharedMaterial存储在一个List中,然后赋值给当前对象的MeshRenderer的sharedMaterials。
  • 调用mesh.CombineMeshes(combine),合并Mesh。
  • 同时,还可以调用Unwrapping.GenerateSecondaryUVSet(meshFilter.sharedMesh);合并第二套UV。
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class CombineMesh
{
private const string MESH_PATH = "Assets/GameData/";

[MenuItem("Examples/CombineMesh")]
static void Combine()
{
GameObject obj = Selection.activeGameObject;
if (obj.GetComponent<MeshRenderer>() == null)
{
obj.AddComponent<MeshRenderer>();
}
if (obj.GetComponent<MeshFilter>() == null)
{
obj.AddComponent<MeshFilter>();
}

List<Material> material = new List<Material>();
Matrix4x4 matrix = obj.transform.worldToLocalMatrix;
MeshFilter[] filters = obj.GetComponentsInChildren<MeshFilter>();
int filterLength = filters.Length;
CombineInstance[] combine = new CombineInstance[filterLength];
for (int i = 0; i < filterLength; i++)
{
MeshFilter filter = filters[i];
MeshRenderer render = filter.GetComponent<MeshRenderer>();
if (render == null)
{
continue;
}
if (render.sharedMaterial != null && !material.Contains(render.sharedMaterial))
{
material.Add(render.sharedMaterial);
}
combine[i].mesh = filter.sharedMesh;
//对坐标系施加变换的方法是 当前对象和子对象在世界空间中的矩阵 左乘 当前对象从世界空间转换为本地空间的变换矩阵
//得到当前对象和子对象在本地空间的矩阵。
combine[i].transform = matrix * filter.transform.localToWorldMatrix;
render.enabled = false;
}

MeshFilter meshFilter = obj.GetComponent<MeshFilter>();
Mesh mesh = new Mesh();
mesh.name = "Combine";
//合并Mesh
mesh.CombineMeshes(combine);
meshFilter.sharedMesh = mesh;
//合并第二套UV
Unwrapping.GenerateSecondaryUVSet(meshFilter.sharedMesh);
MeshRenderer renderer = obj.GetComponent<MeshRenderer>();
renderer.sharedMaterials = material.ToArray();
renderer.enabled = true;

MeshCollider collider = new MeshCollider();
if (collider != null)
{
collider.sharedMesh = mesh;
}
string tempPath = MESH_PATH + obj.name + "_mesh.asset";
AssetDatabase.CreateAsset(meshFilter.sharedMesh, tempPath);
PrefabUtility.DisconnectPrefabInstance(obj);
}

}

 

对坐标系施加变换的方法

矩阵变换,当前对象和子对象在世界空间中的矩阵 左乘 当前对象从世界空间转换为本地空间的变换矩阵,得到当前对象和子对象在本地空间的矩阵。

 

标签:obj,渲染,对象,模型,mesh,Unity,Mesh,用户手册
From: https://blog.51cto.com/u_6871414/5897036

相关文章

  • Unity用户手册-Unity与Android、iOS互相调用
        C#是以Assembly(汇编集)为一个基本单位组织代码的,dll就是一个assemble,dll之间有加载依赖顺序。dll是windows平台上的动态库,而so是linux平台上的动态库,最后.a是IOS......
  • 【Unity插件】NGUI核心组件之UIAtlas
    NGUI:UIAtlasUIAtlas是一个容器,他包含了许多sprite的坐标信息。如果你对这个概念不是很熟悉,你可以这样理解:与使用很多小的贴图来渲染UI相比,使用一张包含了所有小贴图的大......
  • Unity游戏的GC(garbage collection)优化
     Unity版本:5.5引言游戏运行时使用内存来存储数据,当这些数据不再被使用时,存储这些数据的内存被释放以便于之后这些内存可以被复用。垃圾(Garbage)是存储无用数据的内存的术语......
  • Unity--Cinemachine官方实例详解
    1.2DCamera搭建一个快速场景,MainCamera选择Orthographic。在Cinemachine下有Create2DCamera,在生成的相机中设置follow,同时注意body的设置,如下图所示在虚拟相机中还需要......
  • Unity Animator -- Apply Root Motion
    Animator.ApplyRootMotion这个属性是用来控制物体在播放骨骼动画的时候是否应用骨骼根节点的运动参数。一、当没有骨骼根节点的情况时,比如只是一个Cube立方体,如果勾选了Appl......
  • 【详细解析版】Unity UGUI Mask组件实现原理
    MaskingisimplementedusingthestencilbufferoftheGPU.即Mask是利用了GPU的模板缓冲来实现的,关于模板,打个简单的比方,就像一个面具,可以挡住一部分“脸”的显示一样。......
  • Unity-利用SkinnedMeshRenderer和Mesh的BindPose实现骨骼动画
    SkinnedMeshRenderer蒙皮网格渲染器。蒙皮是指将Mesh中的顶点附着(绑定)在骨骼之上,而且每个顶点可以被多个骨骼所控制。骨骼是皮肤网格内的不可见对象,它们影响动画过程中网格......
  • Unity判断对象是否在视野内
    判断对象是否在视野内,有两种方式:第一种:不设置固定的目标,使用LayerMask,设置寻找对象的Layer,使用Physics.OverlapSphere方法,以给定的位置为圆心,按照设定距离投射一个球体,返回......
  • Unity--Physics.OverlapSphere的参数LayerMask和GameObject的layer
    Layer介绍:Unity中是用int32来表示32个Layer层。int32表示二进制一共有32位(0—31)在Unity中每个GameObject都有Layer属性,默认的Layer都是Default。在Unity中可编辑的Layer共......
  • Unity-Animator Override Controller
    AnimatorOverrideController是一种资产类型,允许您扩展现有的AnimatorController,替换使用的特定动画,但保留原始结构,参数和逻辑。允许您创建相同基本状态机的多个变体,但每......