如果可以的話,提供切面時讓每個切面的頂點數相同,就可以透過〈掃掠(sweep)〉來建構 3D 物件;然而有時候,或許你想使用不同頂點數的切面,例如,從四個頂點的面變成六個頂點的面,構成漸變的效果。
從 m 個頂點到 n 個頂點之間的變化該怎麼定義呢?不知道!事實就是如此,如果你沒有提供過渡期間頂點的變化過程,資訊就是不充足的,有時使用者的需求就是這麼模糊「就自動幫我漸變」的概念。
那麼就來猜吧!一個方式是,取兩個面頂點數的最小公倍數,然後將重新構造兩個面的頂點數,使它們的數量都是該最小公倍數,這樣就可以掃掠了,因此,先定義出最小公倍數的函式 lcm
:
function gcd(m, n) {
return n == 0 ? m : gcd(n, m % n);
}
function lcm(m, n) {
return m * n / gcd(m, n);
}
如果最小公倍數是 n
,那麼對於某個切面,若原頂點數 section.length
,那麼可以在原頂點與頂點前,以內插的方式計算出 n / section.length
個頂點:
function interPts(section, n) {
const vts = section.map(p => createVector(p[0], p[1], p[2]))
const pts = [];
for(let i = 0; i < section.length; i++) {
const p1 = vts[i];
const p2 = vts[(i + 1) % vts.length];
for(let j = 0; j < n; j++) {
pts.push(p5.Vector.lerp(p1, p2, j / n));
}
}
return pts;
}
使用者可能提供多個切面,不過,先來解決兩個切面的問題:
function loft2(s1, s2) {
const n = lcm(s1.length, s2.length);
sweep([
interPts(s1, n / s1.length).map(p => [p.x, p.y, p.z]),
interPts(s2, n / s2.length).map(p => [p.x, p.y, p.z])
]);
}
然後,每兩個切面為單位,呼叫 loft2
就可以了:
function loft(sections) {
for(let i = 0; i < sections.length - 1; i++) {
loft2(sections[i], sections[i + 1]);
}
}
來看看效果如何:
你也可以進一步將這個範例進行變化,例如,在兩個切面之間加入 slices
,在兩個切面間,以內插的方式增加切面,這可以讓 3D 物件細緻一些,以下是個示範,有興趣就自行研究: