Hello. I recently downloaded the UniVRM package, which allows you to import 3D models with the .vrm type. I created a character through VRoid Studio and when imported into Unity it has hair physics.
The script is used for each individual group of hair, each group has 5-6 bones. There are 13 groups of hair on the character.
I created about 60 characters on stage in Unity. But processing each group of hair takes +/- 70 µs (microseconds). Since there are 13 of them, each character take about (70 * 13) = 910 µs of CPU time, which is equal to 0.9 ms.
There are 60 characters in the scene, hair processing for each character takes 0.9 ms, so for each frame hair processing takes (0.9 * 60) = 54 ms. Frame time = 54 ms and because of this I have 17-22 FPS on average.
I found this package on github and found the file that handle hair physics (UniVRM/Assets/VRM/Runtime/SpringBone/VRMSpringBone.cs at master · vrm-c/UniVRM · GitHub)
In the private void UpdateProcess(float deltaTime) function, all calculations that affect FPS take place. It has this code:
This code block takes about 30 µs of CPU time and can be optimized by executing it, for example, once every half a second, and not every frame, since it updates the hair group array data, which can be changed in the Unity interface.
m_colliders.Clear();
if (ColliderGroups != null)
{
foreach (var group in ColliderGroups)
{
if (group != null)
{
foreach (var collider in group.Colliders)
{
m_colliders.Add(new SphereCollider(group.transform, collider));
}
}
}
}
But I could not optimize this block of code in any way, and only threading came to my mind. I tried use foreach in diffenrent threads and waiting end of execution for them in the main thread, but I got this error “UnityException get_lossyScale can only be called from main thread”. I got it error because the code in function verlet.Update(…) uses transform.lossyScale (Unity - Scripting API: Transform.lossyScale), and Unity says that this is only possible in the main thread.
var stiffness = m_stiffnessForce * deltaTime;
var external = m_gravityDir * (m_gravityPower * deltaTime);
foreach (var verlet in m_verlet)
{
verlet.SetRadius(m_hitRadius);
verlet.Update(m_center,
stiffness,
m_dragForce,
external,
m_colliders
);
}
Is it possible to use threading somehow in this case? He’s just needed here.
Unity use only one thread for handling hair physics and my CPU loaded in 25-30% cause i’ve 4 cores CPU. GPU loaded in 15% with 22 FPS