2D Freeform的兩個方式在計算開銷上要比Simple大一些。其中Cartesian算法比較簡單,Directional的方法要基於Cartesian,因此本篇先討論這個算法。node
2D Freeform的兩個算法是能夠在網上找到參考資料的,它們基於一篇論文[http://runevision.com/thesis]的章節6.3。算法
咱們依然沿用以前的那個場景,代碼框架稍微有一些變化。框架
[ExecuteInEditMode] public class SampleNode : MonoBehaviour { public BlendNode[] Nodes; private Vector3 mLastPosition; void Start () { Nodes = FindObjectsOfType<BlendNode>(); } void Update () { var pos = transform.position; if(mLastPosition == pos) return; mLastPosition = pos; UpdateWeights(); } [ContextMenu("Update Weights")] void UpdateWeights() { for (int i = 0; i < Nodes.Length; ++i) Nodes[i].Weight = 0; CalcWeightsFreeformCartesian(); NormalizeWeights(); } void CalcWeightsFreeformCartesian() { } void NormalizeWeights() { float totalWeight = 0; foreach (var node in Nodes) { totalWeight += node.Weight; } if(totalWeight == 0) return; foreach (var node in Nodes) { node.Weight = node.Weight / totalWeight; } } }
這個算法的思考方向是,想象爲任意一個節點生成一個影響力圖,那麼該影響力必然從這個節點的位置向周圍各個方向逐漸遞減。
對任一個方向而言,影響力在這個方向上遇到的第一個其它節點時會降到零。動畫
使用1維來理解這件事,看下圖。
有兩個節點pi和pj,對於採樣點在位置p,那麼個pi的影響值應該是多少呢?
咱們能夠看點p在直線pipj上的投影,這個投影點到pi的距離L0與pi到pj的距離L1,恰好能用來計算這個概念。
使I=1-L0/L1
,那麼能夠知足當p接近pi時,I趨近於1,當p接近pj時,I趨近於0。spa
根據向量點乘的特性,能夠很容易的計算出來投影的長度。
$$ L0 = cos \theta |\vec {p_i p}| = {\vec {p_i p} \cdot \vec {p_i p_j} \over |{\vec {p_i p_j}|}} $$
因此
$$ I_{ij} = 1 - {L0 \over |\vec{p_i p_j}|} = 1 - {\vec{p_i p} \cdot \vec{p_i p_j} \over |{\vec{p_i p_j}|^2}} $$
每一個節點的最終影響力取它到全部其它節點的影響力中的最小值,至此咱們獲得了論文中的公式(6.6):
$$ h_i(p) = \min_{j=1}^n (1 - {\vec{p_i p} \cdot \vec{p_i p_j} \over |{\vec{p_i p_j}|^2}}) $$
最後,對全部節點的影響力進行歸一化,就能獲得它們各自的權重。code
void CalcWeightsFreeformCartesian() { var posS = mInputPosition; // 爲每個動畫節點計算影響值 foreach (var node0 in Nodes) { var influence = float.MaxValue; var pos0 = node0.transform.position; // 計算當前節點針對每個其它節點的影響值,並取最小的一個 for (int i = 0; i < Nodes.Length; ++i) { var node1 = Nodes[i]; if (node0 == node1) continue; var pos1 = node1.transform.position; var vec0S = posS - pos0; var vec01 = pos1 - pos0; // 影響值至關於0S在01上的投影的歸一化 var h = Mathf.Clamp01(1 - Vector3.Dot(vec0S, vec01) / vec01.sqrMagnitude); // 保留影響值中最小的 influence = Mathf.Min(influence, h); } node0.Weight = influence; } }
預覽影響力圖的功能在文章中沒有說起,但包含在完整代碼中。
點此下載完整代碼orm