ジオメトリ

ジオメトリは「形」のデータ。頂点面(インデックス)UV法線を持つ BufferGeometry が中身。 Three.js には組み込みのプリミティブがたくさんあるが、自前で作ることもできる。

プリミティブ一覧

すべて new THREE.XxxGeometry(...)。引数の順序を覚えておくと早い。

クラス主な引数用途
BoxGeometry(w, h, d, ws=1, hs=1, ds=1)
SphereGeometry(r, ws=32, hs=16)
PlaneGeometry(w, h, ws=1, hs=1)板(床・パネル)
CircleGeometry(r, segments=32)円板
RingGeometry(innerR, outerR, segments=32)ドーナツ状の板
CylinderGeometry(rTop, rBottom, h, radialSeg=32)円柱・円錐台
ConeGeometry(r, h, radialSeg=32)円錐
TorusGeometry(r, tube, radialSeg=12, tubularSeg=48)ドーナツ
TorusKnotGeometry(r, tube, tubularSeg=64, radialSeg=8, p=2, q=3)結び目
CapsuleGeometry(r, length, capSeg=4, radialSeg=8)カプセル(薬カプセル状)
TetrahedronGeometry(r, detail=0)正四面体
OctahedronGeometry(r, detail=0)正八面体
DodecahedronGeometry(r, detail=0)正十二面体
IcosahedronGeometry(r, detail=0)正二十面体(球の近似に便利)
LatheGeometry(points, segments=12)回転体(壺・グラスなど)
TubeGeometry(curve, tubularSeg=64, r=1, radialSeg=8)カーブに沿ったチューブ
ExtrudeGeometry(shape, opts)2D Shape の押し出し(ロゴ等)
ShapeGeometry(shape)2D Shape のフラット版
EdgesGeometry(geom, thresholdAngle=1)輪郭線(LineSegments と組む)
WireframeGeometry(geom)ワイヤフレーム
よく使う3つ
const box     = new THREE.BoxGeometry(1, 1, 1)
const sphere  = new THREE.SphereGeometry(0.5, 32, 16)
const plane   = new THREE.PlaneGeometry(10, 10)

BufferGeometry の中身

全ジオメトリの内部は BufferGeometry。これは頂点属性(位置・UV・法線・色…)と インデックスの集合

const geom = new THREE.BoxGeometry()

console.log(geom.attributes.position) // BufferAttribute (Float32, itemSize 3)
console.log(geom.attributes.normal)   // BufferAttribute (Float32, itemSize 3)
console.log(geom.attributes.uv)       // BufferAttribute (Float32, itemSize 2)
console.log(geom.index)               // インデックス(三角形リスト)

主な属性:

自前 BufferGeometry を作る

独自の点群、メッシュ、形をプロシージャルに作るときは BufferGeometry を直接組む。

三角形を1枚作る
const geometry = new THREE.BufferGeometry()

const positions = new Float32Array([
   0,  1, 0,  // v0
  -1, -1, 0,  // v1
   1, -1, 0,  // v2
])

geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3))
// 単一三角形なので index は不要。複雑な形では setIndex で頂点共有

geometry.computeVertexNormals()
geometry.computeBoundingBox()
geometry.computeBoundingSphere()

インデックスを使う

頂点を共有してメモリを節約したい時は setIndex

const positions = new Float32Array([
  -1, -1, 0,  //0
   1, -1, 0,  //1
   1,  1, 0,  //2
  -1,  1, 0,  //3
])
const indices = new Uint16Array([
  0, 1, 2,    // 三角形 1
  0, 2, 3,    // 三角形 2
])
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3))
geometry.setIndex(new THREE.BufferAttribute(indices, 1))

UV を付ける

const uvs = new Float32Array([
  0, 0,
  1, 0,
  1, 1,
  0, 1,
])
geometry.setAttribute("uv", new THREE.BufferAttribute(uvs, 2))

変形・最適化

法線の再計算

頂点を動かしたら法線が古いまま。必ず computeVertexNormals()

geometry.attributes.position.needsUpdate = true
geometry.computeVertexNormals()

動的に頂点を変える

毎フレーム頂点を書き換えたい場合はposition.needsUpdate = trueを立てる。 ただし大量頂点を CPU から書き換えるのは遅い。シェーダ側で動かすほうが速い。

マージ・結合

addons の BufferGeometryUtils複数の geometry を1つに合体できる。 ドローコール削減に有効。

import { mergeGeometries } from "three/addons/utils/BufferGeometryUtils.js"

const merged = mergeGeometries([geomA, geomB, geomC])
const mesh = new THREE.Mesh(merged, material)

輪郭線・ワイヤフレーム

シルエットだけ抜く(EdgesGeometry

const edges = new THREE.EdgesGeometry(box, 15) // 15度以上の折れ目だけ拾う
const line = new THREE.LineSegments(
  edges,
  new THREE.LineBasicMaterial({ color: "white" }),
)
mesh.add(line)

全エッジ表示(WireframeGeometry

const wire = new THREE.WireframeGeometry(box)
const line = new THREE.LineSegments(wire, new THREE.LineBasicMaterial({ color: "white" }))

単に色付きで「ワイヤ表示」にしたいだけならマテリアルの wireframe: true でも可能。 その場合 EdgesGeometry のような「閾値で残すエッジ」の制御はできない。

2D Shape からの押し出し

ロゴや文字を2D で描いて、押し出して 3D にするパターン。

const shape = new THREE.Shape()
shape.moveTo(0, 0)
shape.lineTo(2, 0)
shape.lineTo(2, 1)
shape.lineTo(0, 1)
shape.lineTo(0, 0)

const geometry = new THREE.ExtrudeGeometry(shape, {
  depth: 0.5,
  bevelEnabled: true,
  bevelThickness: 0.05,
  bevelSize: 0.05,
  bevelSegments: 4,
})

カーブに沿ったチューブ

const curve = new THREE.CatmullRomCurve3([
  new THREE.Vector3(-2, 0, 0),
  new THREE.Vector3(0, 1, 0),
  new THREE.Vector3(2, 0, 0),
  new THREE.Vector3(0, -1, 0),
], true) // closed

const tube = new THREE.TubeGeometry(curve, 200, 0.05, 16, true)

点と線

点群(パーティクル)や線は Mesh ではなく Points / Line / LineSegments を使う。

const N = 5000
const positions = new Float32Array(N * 3)
for (let i = 0; i < N; i++) {
  positions[i*3+0] = (Math.random() - 0.5) * 10
  positions[i*3+1] = (Math.random() - 0.5) * 10
  positions[i*3+2] = (Math.random() - 0.5) * 10
}
const geom = new THREE.BufferGeometry()
geom.setAttribute("position", new THREE.BufferAttribute(positions, 3))

const points = new THREE.Points(
  geom,
  new THREE.PointsMaterial({ size: 0.02, color: "white" }),
)
scene.add(points)

Bounding ボックス・球

ヒット判定や frustum culling、自動カメラ調整で使う。

geometry.computeBoundingBox()
console.log(geometry.boundingBox.min, geometry.boundingBox.max)

geometry.computeBoundingSphere()
console.log(geometry.boundingSphere.center, geometry.boundingSphere.radius)
dispose() を忘れない

ジオメトリは GPU バッファを掴む。使い終わったら geometry.dispose()
シーンから外しただけでは GPU メモリは戻らない。詳しくは 注意点