目次/第05章
三角形の向きと、面の傾き
winding order & normals — 巻き順と法線
- 3Dの立体の正体は、点の座標と点を三角形に結ぶ番号表の2つだけです。
- 三角形には表と裏があり(巻き順)、裏向きの面は初めから描かれません(背面カリング)。
- 面の向き=法線が明るさを決めます。平均してならすと、なめらかに見えます。
3Dの立体は、突き詰めると点の座標と点を三角形に結ぶ番号表の2つです。
Babylon.js では前者が positions、後者が indices。
さらに normals(法線)と uvs を足して VertexData に詰めれば、
それがもうメッシュになります。
const vd = new BABYLON.VertexData(); vd.positions = positions; // 頂点の座標 [x,y,z, x,y,z, ...] vd.indices = indices; // 三角形 [0,1,2, 2,1,3, ...] vd.normals = normals; // 面の向き(光の当たり方を決める) vd.uvs = uvs; // 模様を貼る位置 vd.applyToMesh(mesh, false);
三角形には表と裏がある
三角形の3点を、表から見て反時計回りに並べると表向き、逆に並べると裏向きになります。 これを巻き順(winding order)と言います。 GPUは処理を減らすため、裏向きの面を初めから描きません(背面カリング)。 だから巻き順を間違えると、立体が消えたり内側が見えたりします。
コードの finalize() は、これを自動で直します。やり方が上手いので見てください。
代表の頂点をひとつ選び、その法線と「中心からその頂点へ向かうベクトル」の
内積を取る。内積が負なら法線が内側を向いている=巻き順が逆、と判定して、
三角形の2番目と3番目の番号を入れ替えます。
const dot = normals[k]*(positions[k]-center.x) + normals[k+1]*(positions[k+1]-center.y) + normals[k+2]*(positions[k+2]-center.z); if (dot < 0) { // 内側を向いていた for (let i = 0; i < indices.length; i += 3) { const t = indices[i+1]; indices[i+1] = indices[i+2]; indices[i+2] = t; } }
法線をならすと、なめらかになる
法線は「その面がどっちを向いているか」を表す矢印で、明るさはこれで決まります。 面ごとに1本ずつ持つとカクカクした多面体に見え(フラットシェーディング)、 頂点ごとにまわりの面の平均を持たせるとなめらかに見えます(スムーズシェーディング)。 同じ形なのに、法線の作り方だけで見た目が変わります。
実験 — 向きを壊してみる
「巻き順を逆」+「裏面を描かない」で、トマトが消えます。3Dで最初に必ず出会うバグです。
1周してつながる部分(u=0 と u=1)は、UVのために同じ場所に頂点が2つあります。
放っておくと2つの法線がわずかに違い、そこだけ縦の線が入って見えます。
weldNormals() は座標を8192倍して丸めた値をキーに同じ位置の頂点を集め、
法線を平均して揃えます。これで縫い目が消えます。
- 「巻き順をわざと逆にする」+「裏面を描かない」で、トマトを消してみる。
- 「法線をならす」を切って、カクカクになる様子を見る。分割数を8まで下げるとよく分かる。
- 「法線の色」モードで、上を向いた面が何色になるか観察する(緑が強くなります)。