52.动态效果-顶点动画-顶点动画的注意事项-批处理
52.1 知识点
知识回顾
在之前的顶点动画相关课程中,我们提到过需要在渲染标签中添加 "DisableBatching"="True"
来让该 Shader 渲染的对象不进行批处理。这样做的目的是确保基于模型空间的计算能够正确进行,且不会影响最终的渲染结果。
为什么批处理会影响顶点动画
Unity 中默认有静态批处理和动态批处理。批处理的主要作用是将多个对象合并,作为一个 DrawCall 进行处理。批处理会对顶点动画产生影响,原因在于不同的对象拥有不同的变换矩阵(位置、旋转、缩放),而批处理会将这些对象的变换矩阵统一处理。
举个例子:
- 物体 A:位于世界空间位置 (0, 0, 0),无旋转。
- 物体 B:位于世界空间位置 (5, 0, 0),无旋转。
它们是两个独立的对象,拥有不同的变换矩阵。
不进行批处理时:
每个对象的变换矩阵会单独传递给 Shader,顶点的模型空间位置会根据各自的变换进行正确计算。
进行批处理时:
启用批处理后,Unity 会将对象 A 和对象 B 合并为一个 DrawCall,并使用统一的变换矩阵。例如,在静态批处理中,Unity 会将对象 A 和对象 B 的顶点合并为一个网格,并使用统一的变换进行渲染。此时,批处理后顶点位置是混合的,Shader 中无法区分不同对象的模型空间位置,这可能会导致以下问题:
顶点动画失效: 假设你希望顶点在模型空间的 x 方向上进行sin波动动画。如果对象 A 和对象 B 的模型空间位置被混合,波动动画会变得不可预测。
变换混淆: 对象 A 和对象 B 拥有不同的变换矩阵。如果批处理后使用统一的变换矩阵,Shader 无法区分每个顶点属于哪个对象,导致所有顶点的动画效果混淆。
总结: 批处理会让对象失去独立性,相当于将多个对象之间独立的模型空间坐标系合并为一个坐标系,从而影响顶点的相对位置和变换矩阵等信息,导致顶点动画结果异常。因此,我们通过渲染标签来关闭批处理。
关闭批处理带来的问题
关闭批处理带来的最直接问题是导致 DrawCall 的提升。DrawCall 的增加可能会带来性能问题。如果 DrawCall 的增加并没有带来性能问题,那么可以通过关闭批处理来解决顶点动画问题。如果性能问题很严重,并且必须优化带有顶点动画的 Shader,那么应该如何解决呢?
如何解决问题
开启批处理的同时,可以通过以下方法来解决问题:
1. 顶点颜色
可以利用顶点颜色来存储每个顶点的位置信息或相对位置信息。具体步骤如下:
- 获取当前游戏对象上的网格过滤器组件(MeshFilter)。
- MeshFilter 用于存储和管理游戏对象的网格(Mesh)数据。
- 获取网格的顶点数组,每个顶点是一个三维向量(Vector3),表示模型空间中的一个点的位置。
- 创建一个颜色数组,长度与顶点数组相同,用于存储每个顶点的颜色信息。
- 遍历顶点数组,将顶点的模型空间位置存储到顶点颜色中。
- 在 Shader 中通过颜色属性获取顶点信息。
示例代码:
// MeshFilter 用于存储和管理游戏对象的网格(Mesh)数据。
MeshFilter meshFilter = GetComponent<MeshFilter>();
if (meshFilter != null)
{
// 获取 MeshFilter 组件所关联的网格(Mesh)对象。
Mesh mesh = meshFilter.mesh;
// 获取网格的顶点数组,每个顶点是一个三维向量(Vector3),
// 表示模型空间中的一个点的位置。
Vector3[] vertices = mesh.vertices;
// 创建一个颜色数组,长度与顶点数组相同。
// 这个数组将用于存储每个顶点的颜色信息。
Color[] colors = new Color[vertices.Length];
// 遍历顶点数组中的每个顶点。
for (int i = 0; i < vertices.Length; i++)
{
// 将模型空间位置存储在顶点颜色中
// 创建一个新的颜色对象,将顶点的 x、y、z 坐标作为颜色的 RGB 分量,
// 第四个参数 1 通常表示不透明度为完全不透明。
colors[i] = new Color(vertices[i].x, vertices[i].y, vertices[i].z, 1);
}
// 将计算得到的颜色数组赋值给网格的颜色属性,
// 这样可以在渲染时根据顶点颜色进行着色。
mesh.colors = colors;
}
在 Shader 中,通过 appdata_full.color
获取顶点信息。
2. UV 通道
和上面的顶点颜色方案类似,只是将相关信息存储到 UV 通道中。一般在存储两个值时,使用 mesh.uv2
或 mesh.uv3
,并在顶点着色器中通过 appdata_full.texcoord
获取。
总结
若顶点动画 Shader 由于关闭批处理而带来了性能问题,可以通过以下方案避免顶点动画渲染问题:
- 利用顶点颜色;
- 使用 UV 通道;
- 其他方案。
这些方法能够在确保顶点动画正常的同时,解决由批处理带来的性能问题。
52.2 知识点代码
Lesson52_动态效果_顶点动画_顶点动画的注意事项_批处理.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson52_动态效果_顶点动画_顶点动画的注意事项_批处理 : MonoBehaviour
{
void Start()
{
#region 知识回顾
//我们在之前的顶点动画相关课程中一再强调
//我们需要在渲染标签中添加
//"DisableBatching"="True"
//来让该Shader渲染的对象不进行批处理
//目的是让基于模型空间的计算能够正确进行
//不会影响最终的渲染结果
#endregion
#region 知识点一 为什么批处理会影响顶点动画
//Unity中默认有静态批处理和动态批处理
//批处理的主要作用是
//合并多个对象,将他们作为一个DrawCall进行处理
//之所以批处理会对顶点动画带来影响
//是因为
//不同的对象会拥有不同的变换矩阵(位置、旋转、缩放)
//而批处理后
//他们的变换矩阵会进行统一处理
//举例:
//物体A:位于世界空间位置 (0, 0, 0),无旋转。
//物体B:位于世界空间位置 (5, 0, 0),无旋转。
//他们是两个独立的对象,拥有不同的变换矩阵
//不进行批处理时:
//每个对象的变换矩阵会单独传递给Shader,顶点的模型空间位置会根据各自的变换进行正确计算
//进行批处理时:
//启用批处理后,Unity会将对象A和对象B合并为一个Draw Call,并使用一个统一的变换矩阵
//比如在静态批处理中,Unity会将对象A和对象B的顶点合并为一个网格,并使用统一的变换进行渲染
//批处理后顶点位置是混合的,Shader中无法区分不同对象的模型空间位置
//可能带来的问题有:
//顶点动画失效:
//假设你希望顶点在模型空间的x方向上进行sin波动动画。
//如果对象A和对象B的模型空间位置被混合,波动动画会变得不可预测
//变换混淆:
//对象A和对象B有不同的变换矩阵。
//如果批处理后使用统一的变换矩阵,Shader无法区分每个顶点属于哪个对象,导致所有顶点的动画效果混淆。
//总结:
//批处理会让对象失去独立性
//相当于将多个对象之间独立的模型空间坐标系合并为一个坐标系
//从而影响顶点的相对位置和变换矩阵等信息
//导致顶点动画结果异常
//因此我们通过渲染标签来关闭批处理
#endregion
#region 知识点二 关闭批处理带来的问题
//关闭批处理带来的最直接问题就是导致
//DrawCall的提升
//DrawCall的提升可能会带来性能问题
//如果DrawCall的增加并没有带来性能问题
//那我们可以通过关闭批处理来解决顶点动画问题
//如果带来了性能问题,并且必须优化带有顶点动画的Shader,我们应该如何解决呢?
#endregion
#region 知识点三 如何解决问题
//开启批处理
//1.顶点颜色
// 利用顶点颜色来存储每个顶点的位置信息或相对位置信息
// 我们在C#代码中获取模型网格顶点数据,将数据存储到网格的颜色属性中
// 在Shader中通过颜色属性获取顶点信息
// 获取当前游戏对象上的网格过滤器组件(MeshFilter)。
// MeshFilter 用于存储和管理游戏对象的网格(Mesh)数据。
MeshFilter meshFilter = GetComponent<MeshFilter>();
if (meshFilter != null)
{
// 获取 MeshFilter 组件所关联的网格(Mesh)对象。
Mesh mesh = meshFilter.mesh;
// 获取网格的顶点数组,每个顶点是一个三维向量(Vector3),
// 表示模型空间中的一个点的位置。
Vector3[] vertices = mesh.vertices;
// 创建一个颜色数组,长度与顶点数组相同。
// 这个数组将用于存储每个顶点的颜色信息。
Color[] colors = new Color[vertices.Length];
// 遍历顶点数组中的每个顶点。
for (int i = 0; i < vertices.Length; i++)
{
// 将模型空间位置存储在顶点颜色中
// 创建一个新的颜色对象,将顶点的 x、y、z 坐标作为颜色的 RGB 分量,
// 第四个参数 1 通常表示不透明度为完全不透明。
colors[i] = new Color(vertices[i].x, vertices[i].y, vertices[i].z, 1);
}
// 将计算得到的颜色数组赋值给网格的颜色属性,
// 这样可以在渲染时根据顶点颜色进行着色。
mesh.colors = colors;
}
// 在Shader中直接在appdata_full结构体中点出颜色成员既可以利用它获取到顶点信息
// 在顶点着色器使用appdata_full.color获取
//2.uv通道
// 和上面的顶点颜色方案类似,只是把相关信息存储到uv通道中而已,但是一般在存储两个值时使用
// 只需要将上面的 mesh.colors改成 mesh.uv2 mesh.uv3 这样
// 在顶点着色器使用appdata_full.texcoord获取
//等等
#endregion
#region 总结
//若顶点动画Shader由于关闭批出来带来了性能问题
//我们可以去掉渲染标签"DisableBatching"="True"
//通过
//1.顶点颜色
//2.uv通道
//等方案去避免顶点动画渲染问题
#endregion
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com