面的法向量,除了用來表示該面的正反面以外,也是用來表示面的確切方向的資訊,正確的法向量可以使光源正確的模擬出照射效果並合理的渲染出來。
前回我們將某一面往法向量方向舉升,造成該網格周圍的面被改變,這時候這些面的法向量其實是會改變的,因此需要將這些面的法向量進行修正,並且運用拓樸關係找到相鄰的面有哪些。
首先宣告一個 faceIDs 用來存放被改變的面。
var faceIDs = [];
接下來可以透過上回跑三個頂點的迴圈,順便利用 點拓墣關係 來找尋與點相鄰的面,這裡偷懶使用ES6的展開運算子(Spread Operator)來進行陣列的合併。
faceIDs.push(...topology.vertex[vid].faceIDs);
最後過濾掉有重複的面,簡單用filter查找就行了。
faceIDs = faceIDs.filter(function (face, index, arr) {
return arr.indexOf(face) === index;
});
有了這些面以後,接下來要重新計算法向量,首先在迴圈內建立兩個向量cb、ca表示某片網格的兩個邊,並且取得三個點座標資訊。
for (var i = 0; i < faceIDs.length;i++) {
var id = faceIDs[i];
var cb = new THREE.Vector3();
var ab = new THREE.Vector3();
var vA = geometry.vertices[geometry.faces[id].a];
var vB = geometry.vertices[geometry.faces[id].b];
var vC = geometry.vertices[geometry.faces[id].c];
// Do Something
}
接下來要將B、C兩點以及A、B兩點各連成一個向量,然後透過高中學過的外積求得垂直的法向量。
cb.subVectors(vC, vB);
ab.subVectors(vA, vB);
cb.cross(ab);
然後將新的法向量正規化就可以用來更新原本每個面的法向量。
geometry.faces[id].normal.copy(cb.normalize());
最後告訴Three.js法向量需要更新就可以了,結果如圖。
geometry.normalsNeedUpdate = true;
修正法向量後變形
https://github.com/QQBoxy/threecad/blob/master/client/example13/Editor.js
眼尖的你可能發現了,計算法向量時其實有順時針、逆時針的差別,這會導致法向量的方向完全相反,但其實STL三角網格檔案有一個不成文的規定,就是vertex座標在儲存的時候大家都會順著相同的方向儲存,所以一般來說不必太擔心網格翻轉的問題,但如果真的不幸遇到了就必須再透過網格翻轉的演算法進行修復喔。