SceneKit Rigged 角色动画提高性能
SceneKit Rigged Character Animation increase performance
我有 *.DAE
个角色文件,每个角色有 45-70 根骨头,
我想在屏幕上显示大约 100 个动画角色。
然而,当我有大约 60 个角色时,动画需要大约 13 毫秒的更新循环,这是非常昂贵的,并且几乎没有余地进行其他任务。
我正在将动画 "CAAnimationGroup" 设置到网格 SCNNode
当我想交换动画时,我将删除以前的动画 fadeOut
设置为 0.2,并添加新的动画 FadeIn
也设置为 0.2。 -> 不好吗?我应该暂停以前的动画并播放新的动画吗?还是更糟?
是否有更好的方法来为 SceneKit
中的角色设置动画,也许使用 GPU 或其他方式?
请让我开始朝着正确的方向前进,以减少更新循环中的动画开销。
更新
通过 Bug radar 联系 Apple 后,我通过电子邮件收到了这个问题:
This issue is being worked on to be fixed in a future update, we will
let you know as soon as we have a beta build you can test and verify
this issue.
Thank you for your patience.
所以让我们拭目以待,看看 Apple 的工程师会在多大程度上增强它:)。
如果您的顶点的影响少于 4 个,SceneKit 会在 GPU 上执行骨骼动画。来自文档,转载 below:
仅当此几何源中的 componentsPerVector 计数等于或小于 4 时,SceneKit 才会在 GPU 上执行骨骼动画。较大的矢量会导致基于 CPU 的动画并显着降低渲染性能。
我使用下面的代码来检测动画是否在 GPU 上完成:
- (void)checkGPUSkinningForInScene:(SCNScene*)character
forNodes:(NSArray*)skinnedNodes {
for (NSString* nodeName in skinnedNodes) {
SCNNode* skinnedNode =
[character.rootNode childNodeWithName:nodeName recursively:YES];
SCNSkinner* skinner = skinnedNode.skinner;
NSLog(@"******** Skinner for node %@ is %@ with skeleton: %@",
skinnedNode.name, skinner, skinner.skeleton);
if (skinner) {
SCNGeometrySource* boneIndices = skinner.boneIndices;
SCNGeometrySource* boneWeights = skinner.boneWeights;
NSInteger influences = boneWeights.componentsPerVector;
if (influences <= 4) {
NSLog(@" This node %@ with %lu influences is skinned on the GPU",
skinnedNode.name, influences);
} else {
NSLog(@" This node %@ with %lu influences is skinned on the CPU",
skinnedNode.name, influences);
}
}
}
}
您传递 SCNScene
和附有 SCNSkinner
的节点名称以检查动画是在 GPU 上还是在 CPU 上完成。
但是,关于 GPU 上的动画还有一条隐藏信息,即如果您的骨架有超过 60 根骨骼,则它不会在 GPU 上执行。知道这一点的技巧是通过附加无效的着色器修改器条目作为 explained in this post.
来打印默认的顶点着色器。
顶点着色器包含以下蒙皮相关代码:
#ifdef USE_SKINNING
uniform vec4 u_skinningJointMatrices[60];
....
#ifdef USE_SKINNING
{
vec3 pos = vec3(0.);
#ifdef USE_NORMAL
vec3 nrm = vec3(0.);
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
vec3 tgt = vec3(0.);
#endif
for (int i = 0; i < MAX_BONE_INFLUENCES; ++i) {
#if MAX_BONE_INFLUENCES == 1
float weight = 1.0;
#else
float weight = a_skinningWeights[i];
#endif
int idx = int(a_skinningJoints[i]) * 3;
mat4 jointMatrix = mat4(u_skinningJointMatrices[idx], u_skinningJointMatrices[idx+1], u_skinningJointMatrices[idx+2], vec4(0., 0., 0., 1.));
pos += (_geometry.position * jointMatrix).xyz * weight;
#ifdef USE_NORMAL
nrm += _geometry.normal * mat3(jointMatrix) * weight;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
tgt += _geometry.tangent.xyz * mat3(jointMatrix) * weight;
#endif
}
_geometry.position.xyz = pos;
这清楚地表明您的骨架应限制在 60 根骨头。
如果您的所有角色都具有相同的骨架,那么我建议您使用上述提示检查动画是否在 CPU 或 GPU 上执行。否则,您可能必须将您的角色骨架固定为少于 60 根骨骼并且每个顶点的影响不超过 4 个。
我有 *.DAE
个角色文件,每个角色有 45-70 根骨头,
我想在屏幕上显示大约 100 个动画角色。
然而,当我有大约 60 个角色时,动画需要大约 13 毫秒的更新循环,这是非常昂贵的,并且几乎没有余地进行其他任务。
我正在将动画 "CAAnimationGroup" 设置到网格 SCNNode
当我想交换动画时,我将删除以前的动画 fadeOut
设置为 0.2,并添加新的动画 FadeIn
也设置为 0.2。 -> 不好吗?我应该暂停以前的动画并播放新的动画吗?还是更糟?
是否有更好的方法来为 SceneKit
中的角色设置动画,也许使用 GPU 或其他方式?
请让我开始朝着正确的方向前进,以减少更新循环中的动画开销。
更新 通过 Bug radar 联系 Apple 后,我通过电子邮件收到了这个问题:
This issue is being worked on to be fixed in a future update, we will let you know as soon as we have a beta build you can test and verify this issue.
Thank you for your patience.
所以让我们拭目以待,看看 Apple 的工程师会在多大程度上增强它:)。
如果您的顶点的影响少于 4 个,SceneKit 会在 GPU 上执行骨骼动画。来自文档,转载 below:
仅当此几何源中的 componentsPerVector 计数等于或小于 4 时,SceneKit 才会在 GPU 上执行骨骼动画。较大的矢量会导致基于 CPU 的动画并显着降低渲染性能。
我使用下面的代码来检测动画是否在 GPU 上完成:
- (void)checkGPUSkinningForInScene:(SCNScene*)character
forNodes:(NSArray*)skinnedNodes {
for (NSString* nodeName in skinnedNodes) {
SCNNode* skinnedNode =
[character.rootNode childNodeWithName:nodeName recursively:YES];
SCNSkinner* skinner = skinnedNode.skinner;
NSLog(@"******** Skinner for node %@ is %@ with skeleton: %@",
skinnedNode.name, skinner, skinner.skeleton);
if (skinner) {
SCNGeometrySource* boneIndices = skinner.boneIndices;
SCNGeometrySource* boneWeights = skinner.boneWeights;
NSInteger influences = boneWeights.componentsPerVector;
if (influences <= 4) {
NSLog(@" This node %@ with %lu influences is skinned on the GPU",
skinnedNode.name, influences);
} else {
NSLog(@" This node %@ with %lu influences is skinned on the CPU",
skinnedNode.name, influences);
}
}
}
}
您传递 SCNScene
和附有 SCNSkinner
的节点名称以检查动画是在 GPU 上还是在 CPU 上完成。
但是,关于 GPU 上的动画还有一条隐藏信息,即如果您的骨架有超过 60 根骨骼,则它不会在 GPU 上执行。知道这一点的技巧是通过附加无效的着色器修改器条目作为 explained in this post.
来打印默认的顶点着色器。顶点着色器包含以下蒙皮相关代码:
#ifdef USE_SKINNING
uniform vec4 u_skinningJointMatrices[60];
....
#ifdef USE_SKINNING
{
vec3 pos = vec3(0.);
#ifdef USE_NORMAL
vec3 nrm = vec3(0.);
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
vec3 tgt = vec3(0.);
#endif
for (int i = 0; i < MAX_BONE_INFLUENCES; ++i) {
#if MAX_BONE_INFLUENCES == 1
float weight = 1.0;
#else
float weight = a_skinningWeights[i];
#endif
int idx = int(a_skinningJoints[i]) * 3;
mat4 jointMatrix = mat4(u_skinningJointMatrices[idx], u_skinningJointMatrices[idx+1], u_skinningJointMatrices[idx+2], vec4(0., 0., 0., 1.));
pos += (_geometry.position * jointMatrix).xyz * weight;
#ifdef USE_NORMAL
nrm += _geometry.normal * mat3(jointMatrix) * weight;
#endif
#if defined(USE_TANGENT) || defined(USE_BITANGENT)
tgt += _geometry.tangent.xyz * mat3(jointMatrix) * weight;
#endif
}
_geometry.position.xyz = pos;
这清楚地表明您的骨架应限制在 60 根骨头。
如果您的所有角色都具有相同的骨架,那么我建议您使用上述提示检查动画是否在 CPU 或 GPU 上执行。否则,您可能必须将您的角色骨架固定为少于 60 根骨骼并且每个顶点的影响不超过 4 个。