Cocos Creator 中 _worldMatrix 到底是什麼(中)
- 2019 年 10 月 25 日
- 筆記
Cocos Creator 中 _worldMatrix 到底是什麼(中)
1. 中篇摘要
在上篇中主要做了三件事
- 簡單表述了矩陣的基本知識,以及需要涉及到的三角函數知識
- 推導了圖形變換中 位移 、旋轉、縮放 對應的變換矩陣。
- cocos creator 中矩陣存儲方式
在本篇中我們將運用推導的變換矩陣,一一驗證程式碼中更新節點變換矩陣的程式碼背後的邏輯。遊戲場景中的節點都成樹形的父子關係。當前節點 worldMatrix是通過父級節點對應的矩陣獲取,所以當前場景中只有一個節點時,當前節點的 worldMatrix 應該與localMatrix相同(未測試)。所以這裡就通過分析 updateLocalMatrix 來了解。
cocos creator 中圖形變換採用標記臟數據方式告知渲染流需要更新變換矩陣。採用位運算存儲當前需要更新的變換資訊。updateLocalMatrix 方法程式碼行數比較長,也為了方便分析,所以會將程式碼塊拆分。如想整體瀏覽此程式碼塊,請下載v2.1.3版本的cocos creator。對應的程式碼在安裝文件 resourcesenginecocos2dcoreCCNode.js
2. updateLocalMatrix函數整體邏輯
從如下程式碼就可以論證我上邊說明的變換過程,判斷臟值,有更新無返回。判斷是否有圖形變換,有更新無返回。這裡有必要說明下位運算是如何做到存儲多值的。
設某選擇題存在ABC三個選項,正確選項為AC。我們設ABC的權值分別是 A=001 B=010 C=100 ,AC的權值 AC=A|B = 101。此時若用戶選擇 B,程式只需要將 AC & B > 0 就知道是否正確。程式碼中就剛好利用這一點,在節點變換過程中,不斷改變存在變換 localMatDirty 的值。
// author:herbert 公眾號:小院不小 // 原文鏈接 https://www.cnblogs.com/yfrs/p/ccmatrix2.html _updateLocalMatrix() { let dirtyFlag = this._localMatDirty; if (!dirtyFlag) return; let t = this._matrix; if (dirtyFlag & (LocalDirtyFlag.RT | LocalDirtyFlag.SKEW)) { // 旋轉 縮放 傾斜 } t.m12 = this._position.x; t.m13 = this._position.y; this._localMatDirty = 0; this._worldMatDirty = true; }
程式碼中(LocalDirtyFlag.RT | LocalDirtyFlag.SKEW)
LocalDirtyFlag.RT=LocalDirtyFlag.POSITION | LocalDirtyFlag.SCALE | LocalDirtyFlag.ROTATION 所以程式碼中if 判斷是當前變換中是否存在 位置 、縮放 、旋轉、傾斜。雖然判斷了位置,並沒有放到if程式碼塊中設置,也許還有其他什麼變換導致位置變換。最後就是還原標記,以及告訴渲染流得更新worldMatrix了。
3. 旋轉 縮放 傾斜程式碼邏輯
矩陣的乘法是不滿足交換律,即 AB <> BA 所以體現在程式碼中就是判斷旋轉和傾斜還在設置縮放的原因。複合變換的順序會影響最終節點的位置。此部分程式碼邏輯如下
// author:herbert 公眾號:小院不小 if (dirtyFlag & (LocalDirtyFlag.RT | LocalDirtyFlag.SKEW)) { let rotation = -this._eulerAngles.z; let hasSkew = this._skewX || this._skewY; let sx = this._scale.x, sy = this._scale.y; if (rotation || hasSkew) { // 旋轉 傾斜 } else { t.m00 = sx; t.m01 = 0; t.m04 = 0; t.m05 = sy; } }
程式碼中,沒有旋轉或傾斜資訊,就只剩下縮放。那麼當前矩陣就是縮放矩陣,只需要把矩陣對角線上的值依次設置成sx sy。let rotation = -this._eulerAngles.z;
取負值是因為,rotation 順時針為正,然後歐拉角中,逆時針為正。取z軸旋轉角,是因為2d中旋轉軸就是z軸。
4. 存在旋轉傾斜程式碼邏輯
從程式碼中可知,如果存在複合變換。cocos creator 變換順序為,旋轉->縮放->傾斜->位移。這裡有兩個需要注意地方
- 傳入的旋轉角度需要轉換成弧度。不經常用math.cos math.sin 可能不知道這些函數的參數是弧度值
- Math.sin(Math.PI/6)不等於0.5是因為浮點數的原因
// author:herbert 公眾號:小院不小 wx:464884492 // 原文鏈接 https://www.cnblogs.com/yfrs/p/ccmatrix2.html if (rotation || hasSkew) { let a = 1, b = 0, c = 0, d = 1; // rotation if (rotation) { let rotationRadians = rotation * ONE_DEGREE; c = Math.sin(rotationRadians); d = Math.cos(rotationRadians); a = d; b = -c; } // scale t.m00 = a *= sx; t.m01 = b *= sx; t.m04 = c *= sy; t.m05 = d *= sy; // skew if (hasSkew) { // 傾斜 } }
程式碼中將旋轉矩陣分塊,只提取左上角的四項,得出具體的分塊矩陣A為。A此時就應該等於選擇矩陣,即 a=cos(b) b=sin(b) c=-sin(b) d=cos(b).從上篇中我們推導旋轉矩陣是逆時針旋轉推倒。然後程式碼中rotation為了符合使用習慣是順時針的。所有對應的旋轉矩陣應該乘以-1;
由於cos是偶函數,sin是奇函數,將-1帶入矩陣得到
a=cos(b) b=-sin(b) c=sin(b) d=cos(b);接下來處理縮放,將縮放矩陣右乘(cocos 中複合變換矩陣,是左乘還是右乘,沒有明確的地方說明。此處是通過程式碼反推可能有誤)變化後的矩陣,如下圖所示
根據矩陣乘法規則(行乘列)可知
a=asx b=bsx c=csy d= dsy
5. 傾斜程式碼邏輯
傾斜其實是兩個變換,X軸傾斜和Y軸傾斜。在上篇推導中,得到對應變換矩陣。同上邊一樣也只取左上角的的分塊矩陣A.其中
// author:herbert 公眾號:小院不小 wx:464884492 if (hasSkew) { let a = t.m00, b = t.m01, c = t.m04, d = t.m05; let skx = Math.tan(this._skewX * ONE_DEGREE); let sky = Math.tan(this._skewY * ONE_DEGREE); if (skx === Infinity) skx = 99999999; if (sky === Infinity) sky = 99999999; t.m00 = a + c * sky; t.m01 = b + d * sky; t.m04 = c + a * skx; t.m05 = d + b * skx; }
由於 tan(90)趨近無窮大,當計算值為Infinity skx 和 sky 分別做一個值限定。接下來看程式碼對應的矩陣變換。首先先從y到x的順序,將Asky和Askx相乘得到一個複合矩陣。再左乘當前變換矩陣p,為了和前邊對照,依然採用a b c d
矩陣乘法滿足結合率,先將右邊的兩個矩陣相乘
所以通過矩陣乘法規則得到新的值
m00=a+c*sky m04=c+a*skx
m01=d+b*sky m05=d+b*skx
6. 總結
中篇相對於上篇,中間間隔了一個多月的時間,原創實屬不易。這期間一直在惡補圖形學和矩陣相關知識。最初分析版本2.0.10,當時程式碼中存在rotationX rotationY 旋轉,當rotationX 和rotationY 不相等時,一直卡在那段程式碼的分析過程中。後來還去官方論壇提問 https://forum.cocos.com/t/topic/84680/4 ,沒有得到滿意的結果,也沒多少人回復。後來各種查資料,才發現官方將那段程式碼移除了,採用歐拉角的方式。所有我將本地版本升級成v2.1.3版本。到這個版本後,又卡在了 let rotation = –this._eulerAngles.z這個負號問題。越分析越,感覺欠缺的越多,歐拉角,四元數。同時感覺可能寫不出中篇了,不過我還是找了一個感覺是對的推導將中篇完成了。當然其中我覺得不清楚的地方還有
- 在2.0.10 版本中Y軸旋轉和X軸選中問題,為啥可以根據Z軸旋轉的結果一分為二
- 在2.1.3 版本中旋轉取歐拉角z負號問題,旋轉矩陣 b c 值相互取反問題
- 複合矩陣變換左乘右乘問題,在本文中我通過程式碼推導應該是左乘
- 傾斜變換Asky和Askx兩個居中相乘順序問題,本文通過y右乘x得到程式碼結果
歡迎感興趣的朋友關注我的微信訂閱號"小院不小",或者點擊下方的二維碼關注。我將多年開發中遇到的難點,以及一些有意思的功能,體會都會一一發布到我的訂閱號中。需要本文demo可以在公眾號中回復matrix
維護了一個Coscos Creator 的遊戲開發群,歡迎喜歡聊技術的朋友加入
閑來無事,採用cocos creator開發了一個小遊戲【坦克俠】,感興趣的朋友一個可以來玩玩