千二百インスタンス — 庭を数回の描画に収める
草六百、花百二十、苔石八十五、低木四十二。庭の密度は数を減らすのではなく、InstancedMesh で同じ種類を一描画にまとめて作ります。
島の庭にはおよそ千二百の物体があります。一つずつ Mesh にすれば、その区画だけで千二百回の描画呼び出しになります。実際は四、五回です。
同じ種類は一つの InstancedMesh
草は円錐、花の茎は円柱、苔石は二十面体。種類ごとにジオメトリ一つ、マテリアル一つ、InstancedMesh 一つを用意し、各インスタンスの位置・拡大・回転を行列に書きます。
const dummy = new THREE.Object3D();
transforms.forEach((item, index) => {
dummy.position.set(...item.position);
dummy.scale.set(...(item.scale ?? [1, 1, 1]));
dummy.rotation.set(...(item.rotation ?? [0, 0, 0]));
dummy.updateMatrix();
object.setMatrixAt(index, dummy.matrix);
if (item.color) object.setColorAt(index, new THREE.Color(item.color));
});
鍵は setColorAt です。草には三つの緑、花には三色、石には三つの灰があります。色をマテリアルに固定すると色ごとに InstancedMesh が要り、インスタンス化の利得がすぐ半分になります。
位置も選ぶ
円盤全体にばら撒けばよいわけではありません。草を生やすときは三箇所を除外します。水たまり(楕円)、小川(矩形)、あずまやの基壇です。条件は真偽値の羅列で、読みやすくはありませんが後から直すよりずっと速いです。
if (x * x + z * z > 16.7) continue;
if (((x - 0.48) ** 2) / 2.7 + ((z - 1.26) ** 2) / 1.5 < 1) continue;
if (x > 1 && x < 2.6 && z > 1.5) continue;
インスタンス化を使わない場合
花びらはインスタンス化していません。毎フレーム行列を書き換える必要があるからです。落下し、回転し、また生まれます。InstancedMesh でも可能ですが(書き換えたら instanceMatrix.needsUpdate = true)、蛍のようにゆっくり漂うだけのものは Points のほうが安いです。
判断は単純です。毎フレーム全行列を書き換えるものは Points か Sprite、一度だけ書くものは InstancedMesh。
影も勘定に入れる
インスタンス化は影もまとめて落とすので、数が増えれば影の描画も重くなります。ここではシーン全体の影マップを一度だけ計算するので、インスタンス数の増加が毎フレームの費用に影響しません。
まとめ
インスタンス化の利点は「少し速い」ではなく、密度を費用の問題から設計の問題に変えることです。草を六百本植えられるかどうかが、レンダラではなく自分の判断で決まります。

コメント
…