平滑階梯函數(Smoothstep),屬於S型函數(Sigmoid function),是一種常用的繪圖函數,在繪圖領域通常將最小極限設為0,最大極限設為1,其函數及圖形如下:
平滑階梯函數的數學模型: S(x) \begin{cases} 0 & x\leq 0 \ 3x^{2}-2x^{3} & x\geq 0, x\leq 1 \ 1 & x\geq 1 \end{cases}
平滑函數的曲線圖形: y=3x^2-2x^3, x is from 0 to 1
接續前回我們找尋到了球範圍內的所有點資訊,我們將透過改變這些點群來獲得一個平滑的曲面,首先在 Editor 模組建立一個平滑階梯函數 smoothstep 。
Editor.prototype.smoothstep = function (x) {
// 3x^2 - 2x^3
if (x <= 0) {
return 0;
} else if (x > 0 && x < 1) {
return 3 * (x * x) - 2 * (x * x * x);
} else if (x >= 1) {
return 1;
}
};
回到 mouseDown 將先前的繪製點功能部分刪除,改為計算各點到球心的距離並且與半徑進行正規化,而為了讓越接近球心的數值越大,因此需要用1減去正規化的數值以獲得反轉的正規化數值。
var distance = intersected.point.distanceTo(selected.vector3);
var normalize = 1 - (distance / radius); // 0 ~ 1
將正規化數值 normalize 代入今天的主角 smoothstep 函數獲得一個解。
var solution = self.smoothstep(normalize);
根據之前學到的點頂位移的計算方式,將法向量 乘上 平滑函數的解 與 強度值後,加到要改變的網格點以改變位置。
var intensity = 1.5;
//...
var newVector = normal.clone().multiplyScalar(solution).multiplyScalar(intensity);
selected.vector3.add(newVector);
geometry.vertices[selected.ID].copy(selected.vector3);
接著使用先前學到的方法修正所有面的法向量,以及拓樸的中心點位置資訊。
for (j = 0; j < selected.faceIDs.length; j++) {
var id = selected.faceIDs[j];
var cb = new THREE.Vector3();
var ab = new THREE.Vector3();
var vA = topology.vertex[topology.face[id].vertexIDs[0]].vector3;
var vB = topology.vertex[topology.face[id].vertexIDs[1]].vector3;
var vC = topology.vertex[topology.face[id].vertexIDs[2]].vector3;
cb.subVectors(vC, vB);
ab.subVectors(vA, vB);
cb.cross(ab);
geometry.faces[id].normal.copy(cb.normalize());
}
//修正中心點位置
topology.computeCenter(selected.ID);
最後不要忘記更新所有幾何資訊,完成的效果如下圖。
geometry.normalsNeedUpdate = true;
geometry.verticesNeedUpdate = true;
geometry.elementsNeedUpdate = true;
網格模型平滑變形效果
https://github.com/QQBoxy/threecad/tree/master/models
https://qqboxy.github.io/threecad/public/example21.html
https://github.com/QQBoxy/threecad/blob/master/client/example21/Editor.js
本次展示了平滑曲面變形的效果,到這個階段已經能看到些許專業繪圖軟體的影子,不過卻是在Web-based搭建的應用程式,大家有沒有覺得建立一套3D的CAD軟體其實並沒有想像中的那樣困難,繼續跟著筆者探索CAD的奧秘吧!