赞
踩
同屏动画太多导致的 CPU 蒙皮计算压力太大。比如 MOBA 类游戏的几十个小兵,或者竞技场周围的吃瓜群众等等,
这些动画一般不需要很好的效果,可以尝试使用多种优化手段来降低效果(也可以保持效果基本不变)、占用内存、消耗GPU 来降低 CPU 的压力。
有两种 GPU 加速模式:
Step1 编辑器每帧运行 AnimationClip,获取转换后的顶点坐标保存到数组中,根据数组长度计算需要的 Texture 最小尺寸,(Texture用来保存顶点数据)。
- private void CalculateTextureSize(ref int textureSize, int totalCount)
- {
- textureSize = 2;
- while (textureSize * textureSize < totalCount)
- {
- textureSize = textureSize << 1;
- }
- }
然后把顶点数据 xyz 当成 rgb,保存到 Texture 中,保存成引擎可以支持的任意资源格式都可以。
Step2 运行时,设置我们想要播放动画的速度,Update 驱动帧 CurrentFrame,计算顶点的开始 Index,设置给 Shader,
- //因为每一帧都要记录所有定点的位置,所以顶点的开始Index就是totalVerts * currentFrame
- currentPixelIndex = totalVerts * currentFrame;
Step3 Shader 渲染部分,
SV_VertexID,我们用他来区分每一个顶点,加上我们上一步计算的出来的 PixelIndex,就是我们 Vertex 的坐标,如何转成 UV 呢?
根据我们顶点保存的规则,来计算,很简单,如下
- inline float4 getUV(float startIndex)
- {
- float y = (int)(startIndex / _SkinningTexSize);
- float u = (startIndex - y * _SkinningTexSize) / _SkinningTexSize;
- float v = y / _SkinningTexSize;
- return float4(u, v, 0, 0);
- }
获取 UV 之后,根据 UV 坐标取我们计算好缓存起来的 vertex 坐标输出到片段着色器中。
- VertexOutput vert(VertexInput input, uint vid : SV_VertexID)
- {
- VertexOutput output;
- UNITY_SETUP_INSTANCE_ID(input);
- float startPixelIndex = _StartPixelIndex + vid;
- float4 uv = getUV(startPixelIndex);
- float4 vertex = tex2Dlod(_SkinningTex, uv);
- output.vertex = UnityObjectToClipPos(vertex);
- output.uv = input.uv;
- return output;
- }
这个方案:
Step1 类似方案A,记录每一帧的关节 matrix,使用三个 rgb 来保存 matrix 的三行。
- meshTexturePixels[pixelIndex] = new Color(matrix.m00, matrix.m01, matrix.m02, matrix.m03);
- pixelIndex++;
- meshTexturePixels[pixelIndex] = new Color(matrix.m10, matrix.m11, matrix.m12, matrix.m13);
- pixelIndex++;
- meshTexturePixels[pixelIndex] = new Color(matrix.m20, matrix.m21, matrix.m22, matrix.m23);
- pixelIndex++;
最后保存到 Texture 里,不一样的一点是,需要保存一个新的 Mesh 文件,因为骨骼的顺序必须和我们保存的 Texture 对应上。
Step2
perFramePixelsCount,上文我们讲了需要三个像素。totalJoints,是我们Mesh的总关节数。同样的,Update 驱动帧 CurrentFrame。
currentPixelIndex = pixelsStartIndex + totalJoints * currentFrame * perFramePixelsCount;
Step3 Shader 渲染部分,
根据设置的 StartPixelIndex,取Matrix,然后计算顶点、法线等
- inline float4x4 getMatrix(float startIndex)
- {
- float4 row0 = tex2Dlod(_SkinningTex, getUV(startIndex));
- float4 row1 = tex2Dlod(_SkinningTex, getUV(startIndex + 1));
- float4 row2 = tex2Dlod(_SkinningTex, getUV(startIndex + 2));
- return float4x4(row0, row1, row2, float4(0, 0, 0, 1));
- }
-
- VertexOutput vert(VertexInput input)
- {
- VertexOutput output;
- UNITY_SETUP_INSTANCE_ID(input);
- float startPixelIndex = _StartPixelIndex;
- float4 index = input.uv1;
- float4 weight = input.uv2;
- float4x4 matrix1 = getMatrix(startPixelIndex + index.x * 3);
- float4x4 matrix2 = getMatrix(startPixelIndex + index.y * 3);
- float4x4 matrix3 = getMatrix(startPixelIndex + index.z * 3);
- float4x4 matrix4 = getMatrix(startPixelIndex + index.w * 3);
- float4 vertex = mul(matrix1, input.vertex) * weight.x;
- vertex = vertex + mul(matrix2, input.vertex) * weight.y;
- vertex = vertex + mul(matrix3, input.vertex) * weight.z;
- vertex = vertex + mul(matrix4, input.vertex) * weight.w;
- float4 normal = mul(matrix1, input.normal) * weight.x;
- normal = normal + mul(matrix2, input.normal) * weight.y;
- normal = normal + mul(matrix3, input.normal) * weight.z;
- normal = normal + mul(matrix4, input.normal) * weight.w;
- output.vertex = UnityObjectToClipPos(vertex);
- output.normal = UnityObjectToWorldNormal(normal);
- output.uv = input.uv;
- return output;
- }
这个方案:
处理大规模角色,同时不要求动画效果很精细,内存够用的情况,可以采用方案A。
处理逻辑压力大、CPU消耗多并且渲染压力小的情况,可以选择方案B。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。