學習Unity 2019 ECS框架(二)

ECS很是適合用於大規模物件的動畫交互,好比這個流體模擬https://connect.unity.com/p/shi-yong-unityde-ecshe-job-systemshi-xian-liu-ti-mo-ni-xiao-guogit

他在github裏給出了傳統SPH實現(MonoBehaviour)的源碼,和使用ECS架構後的源碼。github

 

先解析下傳統單線程實現,也就是MonoBehaviour。數組

大致思路是在每一個粒子的MonoBehaviour裏,計算本身和其餘粒子在必定密度下受到的力,相互做用力產生的速度與運動方向,再應用到座標位置上。架構

 

private void Start() { InitSPH(); } private void Update() {
  // 計算密度壓力 ComputeDensityPressure();
  // 計算力(含方向) ComputeForces();
  // 計算位置 Integrate();
  // 計算碰撞 ComputeColliders();   // 應用位置 ApplyPosition(); }

 

計算粒子間的流體碰撞共使用到下列參數,SPH包括粒子密度滲透。框架

其中restDensity和smoothingRadius是粒子間超過必定距離上的閾值,則不在計算相互做用力,這也符協力學運動物質趨於穩定的物理學規律。dom

[System.Serializable] private struct SPHParameters { public float particleRadius;    // 粒子半徑
    public float smoothingRadius;   // 平滑半徑
    public float smoothingRadiusSq; // 平衡半徑開方
    public float restDensity;       // 休息密度
    public float gravityMult;       // 重力加速
    public float particleMass;      // 質點 
    public float particleViscosity; // 顆粒粘度
    public float particleDrag;      // 粒子牽引
    #pragma warning restore 0649 }

 

InitSPH()ide

初始化粒子的位置,將位置擺放爲x * y * z,添加必定的位置擾動。oop

// 計算抖動:增長必定隨機性,將隨機值的值域映射到【-1,1】,將抖動縮小到0.1動畫

float jitter = (Random.value * 2f - 1f) * parameters[parameterID].particleRadius * 0.1f;this

粒子位置擺放x z方向都加上了Random.Range(-0.1f, 0.1f)的隨機值,這個隨機值和擾動都不用太大,只是給初始位置增長一點移動,避免運行後的流體只是單純下落。

 

ComputeDensityPressure()

計算相互間的做用裏,因此須要兩個for,進行O(n^2)的遍歷計算

Vector3 rij = particles[j].position - particles[i].position; // 指向j的方向向量,是i粒子對j粒子的做用力

// 若是以前距離小於平滑半徑,則須要進行密度計算
if
(r2 < parameters[particles[i].parameterID].smoothingRadiusSq) { // 質點 * 圓周的一些參數 * pow(平滑半徑,9) * pow(平滑半徑距離 - 實際距離, 3) particles[i].density += parameters[particles[i].parameterID].particleMass * (315.0f / (64.0f * Mathf.PI * Mathf.Pow(parameters[particles[i].parameterID].smoothingRadius, 9.0f))) * Mathf.Pow(parameters[particles[i].parameterID].smoothingRadiusSq - r2, 3.0f); }

計算並存儲粒子受到壓力particles[i].pressure,壓力值與粒子間密度有關。

 

計算做用力與速度,由於是受全部粒子的做用力,是個累加值,速度一樣的道理。下面的代碼稍微簡化了下,不是原代碼。

// 小於平滑閾值,則計算相互做用力(壓力)
if (r < parameters[particles[i].parameterID].smoothingRadius) { // -rij 指向本身 // 計算本身受到的壓力值,計算壓力的粒子距離減去平滑半徑,也便是不受力的距離
    forcePressure += -rij.normalized * particleMass * (particles[i].pressure + particles[j].pressure) / (2.0f * particles[j].density) * (-45.0f / smoothingRadius, 6.0f))) * smoothingRadius - r, 2.0f); forceViscosity += particleViscosity * particleMass * (particles[j].velocity - particles[i].velocity) / particles[j].density * (45.0f / smoothingRadius, 6.0f))) * (smoothingRadius - r); }

 

ComputeColliders()

這個操做我想是計算與地面/牆壁的碰撞,對於其餘碰撞體,都加上SPHCollider標籤,for循環計算particles數組內每一個粒子和GameObject.FindGameObjectsWithTag("SPHCollider")場景內全部SPHCollider標籤的物體進行‘碰撞檢測’,大體實現是:在粒子球體半徑範圍內,經過叉積計算碰撞面的法線方向,而後在地面/牆面的投影計算滲透長度與位置?沒看懂。

 

最後計算一堆點積累加計算各個方向的力,應用到粒子位置上。

 

 

JobSystem & Unity ECS

1. SPHCollider : IComponentData

2. SPHParticle : ISharedComponentData

3. SPHVelocity : IComponentData

先使用ComponentData接口實現數據,這在Unity ECS中被歸爲Component組件,儘管在傳統MVC被認爲是Model,但這裏和Component聯繫更緊密,就像GameObject掛載Component也有一堆Serializable的字段同樣。

 

SPHManager : MonoBehaviour

它構建了整個場景,給牆壁/地面添加Collider,排列粒子位置,感受有點像World的一部分。

private void Start() { // Imoprt //manager = World.Active.GetOrCreateSystem<EntityManager>();
        manager = World.Active.EntityManager; // Setup
 AddColliders(); AddParticles(amount); }

 

SPHSystem : JobComponentSystem

JobHandle OnUpdate(JobHandle inputDeps)每幀調用裏,處理了各個IComponentData & IJobParallelFor,IJobParallelFor只定義了Execute處理每幀當前數據須要作的/執行的操做,屬於行爲,行爲與Component解耦,雖然本來也沒在一塊兒,但若是說MonoBehaviour裏處理了行爲叫作Component的行爲,好吧。

總之按順序new 這些實現了IJobParallelFor的結構體,Unity內部會去註冊這些Execute方法並分佈運行,咱們只須要保證這些接口實現的調用時正確順序就行。

[BurstCompile]
private struct ComputeForces : IJobParallelFor // 行爲


[JobProducerType(typeof(IJobParallelForExtensions.ParallelForJobStruct<>))] public interface IJobParallelFor { // // 摘要: // Implement this method to perform work against a specific iteration index. // // 參數: // index: // The index of the Parallel for loop at which to perform work. void Execute(int index); }

 

 

這樣看來Unity ECS的使用並不會比MonoBehaviour更加複雜,咱們只須要掌握幾個概念ComponentData, World, ComponentSystem,以及實現接口和數據正確,剩下的就交給框架。

相關文章
相關標籤/搜索