threejs-geometry について
threejs-geometry は、three.js r186 でのジオメトリ作成と管理を解説するリファレンススキルです。組み込み形状、カスタムBufferGeometry、インスタンシング、ユーティリティ、性能上の注意点を扱います。
主な機能
- 01組み込みジオメトリとパス形状
- 02カスタムBufferGeometryと属性
- 03線、点、ワイヤーフレーム
- 04InstancedMeshとBatchedMesh
- 05ジオメトリ変換とモーフターゲット
- 06結合、性能、リソース解放
活用シーン
- 01基本形状やパス形状の作成
- 02カスタム頂点データの構築と更新
- 03インスタンシングによる反復オブジェクトの描画
- 04ジオメトリ結合によるドローコール削減
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ cloudai-x/ threejs-skills --skill threejs-geometryインストール後、Claude Code で/threejs-geometryを入力して読み込まれたか確認します。
詳細
Three.js のジオメトリ
three.js r186 を対象としています。例では WebGLRenderer を使用します。WebGPU と TSL のセクションでは WebGPURenderer を使用します。
クイックスタート
import * as THREE from "three";
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 0, 5);
// A geometry is the shape; many meshes can share one geometry instance
const mesh = new THREE.Mesh(new THREE.BoxGeometry(1, 1, 1), new THREE.MeshNormalMaterial());
scene.add(mesh);
renderer.setAnimationLoop(animate);
function animate() {
mesh.rotation.y += 0.01;
renderer.render(scene, camera);
}
組み込みジオメトリ
基本形状
new THREE.BoxGeometry(1, 1, 1); // width, height, depth, widthSegments, heightSegments, depthSegments
new THREE.SphereGeometry(1, 32, 16); // radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength
new THREE.SphereGeometry(1, 32, 16, 0, Math.PI); // half sphere (phi sweep, cut through the poles)
new THREE.SphereGeometry(1, 32, 16, 0, Math.PI * 2, 0, Math.PI / 2); // dome (cut at the equator)
new THREE.PlaneGeometry(10, 10, 1, 1); // width, height, widthSegments, heightSegments
new THREE.CircleGeometry(1, 32); // radius, segments, thetaStart, thetaLength
new THREE.CylinderGeometry(1, 1, 2, 32); // radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength
new THREE.CylinderGeometry(1, 1, 2, 6); // hexagonal prism
new THREE.ConeGeometry(1, 2, 32); // radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength
new THREE.TorusGeometry(1, 0.4, 16, 100); // radius, tube, radialSegments, tubularSegments, arc, thetaStart, thetaLength
new THREE.TorusGeometry(1, 0.4, 16, 100, Math.PI); // half arc
new THREE.TorusKnotGeometry(1, 0.4, 100, 16, 2, 3); // radius, tube, tubularSegments, radialSegments, p, q
new THREE.RingGeometry(0.5, 1, 32, 1); // innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength
高度な形状
new THREE.CapsuleGeometry(0.5, 1, 4, 8, 1); // radius, height (the middle section only), capSegments, radialSegments, heightSegments
// radius, detail - each detail level subdivides every face
new THREE.IcosahedronGeometry(1, 0); // 20 faces at detail 0
new THREE.DodecahedronGeometry(1, 0);
new THREE.OctahedronGeometry(1, 0);
new THREE.TetrahedronGeometry(1, 0);
// Polyhedron - flat vertices array, indices, radius, detail
const vertices = [1, 1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1];
const indices = [2, 1, 0, 0, 3, 2, 1, 3, 0, 2, 3, 1];
new THREE.PolyhedronGeometry(vertices, indices, 1, 0);
パスベースの形状
// Lathe - a Vector2 profile rotated around the Y axis: points, segments, phiStart, phiLength
const profile = [
new THREE.Vector2(0, 0), new THREE.Vector2(0.5, 0), new THREE.Vector2(0.5, 1), new THREE.Vector2(0, 1),
];
new THREE.LatheGeometry(profile, 32);
// Extrude - a 2D Shape pushed along Z
const shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(1, 0);
shape.lineTo(1, 1);
shape.lineTo(0, 1);
new THREE.ExtrudeGeometry(shape, {
steps: 1, depth: 1, bevelEnabled: true,
bevelThickness: 0.1, bevelSize: 0.1, bevelSegments: 3,
});
// Tube - a circular sweep along a curve: path, tubularSegments, radius, radialSegments, closed
const curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(1, 0, 0),
]);
new THREE.TubeGeometry(curve, 64, 0.2, 8, false);
テキストジオメトリ
import { FontLoader } from "three/addons/loaders/FontLoader.js";
import { TextGeometry } from "three/addons/geometries/TextGeometry.js";
// A .typeface.json font, from examples/fonts in the three.js repository
const font = await new FontLoader().loadAsync("fonts/helvetiker_regular.typeface.json");
const geometry = new TextGeometry("Hello", {
font: font,
size: 1, // text size in world units
depth: 0.2, // extrusion depth
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 0.03,
bevelSize: 0.02,
direction: "ltr", // 'ltr', 'rtl' (right to left) or 'tb' (top to bottom)
});
geometry.center(); // recentre the glyphs on the origin
scene.add(new THREE.Mesh(geometry, new THREE.MeshNormalMaterial()));
BufferGeometry
すべてのジオメトリの基底クラスです。頂点データの型付き配列と、任意のインデックスで構成されます。属性は頂点あたり itemSize 個の値を持つ名前付きバッファーです。position、normal、uv は、マテリアルがデフォルトで読み取る属性です。
カスタム BufferGeometry
const geometry = new THREE.BufferGeometry();
// Four vertices, three floats each (x, y, z)
const vertices = new Float32Array([-1, -1, 0, 1, -1, 0, 1, 1, 0, -1, 1, 0]);
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
// Indices reuse vertices across the two triangles
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
// Normals drive the lighting, UVs address textures
const normals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
geometry.setAttribute("normal", new THREE.BufferAttribute(normals, 3));
const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
geometry.setAttribute("uv", new THREE.BufferAttribute(uvs, 2));
// Optional per-vertex colors, rendered with material.vertexColors = true
const colors = new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0]);
geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
scene.add(new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ vertexColors: true })));
属性の型
const count = 4;
const geometry = new THREE.BufferGeometry();
// Float32 for positions, normals, UVs and floating-point colors
geometry.setAttribute("position", new THREE.BufferAttribute(new Float32Array(count * 3), 3));
// Uint16 indices address vertices 0 to 65534 (65535 is the primitive-restart value); setIndex([...]) with a plain array picks Uint16 or Uint32 itself
geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(count), 1));
// Uint8 colors need normalized = true (third argument) so bytes reach the shader as 0.0-1.0
geometry.setAttribute("color", new THREE.BufferAttribute(new Uint8Array(count * 3), 3, true));
BufferGeometry の更新
const geometry = new THREE.PlaneGeometry(2, 2);
const positions = geometry.attributes.position;
positions.setXYZ(0, -2, -2, 0); // overwrite vertex 0
positions.getX(0); // read single components with getX / getY / getZ
positions.needsUpdate = true; // upload the change; an array cannot grow after its first render, so allocate the maximum and use geometry.setDrawRange(0, n)
geometry.computeVertexNormals(); // normals must match the new positions
geometry.computeBoundingBox(); // bounding volumes drive frustum culling and raycasting
geometry.computeBoundingSphere();
インターリーブバッファー
// One buffer holding position (3 floats) and uv (2 floats) per vertex
const interleavedBuffer = new THREE.InterleavedBuffer(
new Float32Array([
-1, -1, 0, 0, 0, 1, -1, 0, 1, 0, 1, 1, 0, 1, 1, -1, 1, 0, 0, 1, // four vertices: xyz + uv
]),
5, // stride: floats per vertex
);
const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.InterleavedBufferAttribute(interleavedBuffer, 3, 0));
geometry.setAttribute("uv", new THREE.InterleavedBufferAttribute(interleavedBuffer, 2, 3));
geometry.setIndex([0, 1, 2, 0, 2, 3]); // two triangles over the four vertices
// BufferGeometryUtils.interleaveAttributes([position, normal, uv]) packs existing attributes this way
エッジとワイヤーフレーム
const boxGeometry = new THREE.BoxGeometry(1, 1, 1);
// Edges where the two faces' normals differ by at least thresholdAngle (degrees; 15 here), plus open boundary edges
const edges = new THREE.EdgesGeometry(boxGeometry, 15);
const edgeLines = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xffffff }));
// Every triangle edge, diagonals included; an edge shared by two triangles is drawn once
const wireframe = new THREE.WireframeGeometry(boxGeometry);
const wireLines = new THREE.LineSegments(wireframe, new THREE.LineBasicMaterial({ color: 0xffffff }));
scene.add(edgeLines, wireLines);
ポイント
const count = 1000;
const positions = new Float32Array(count * 3).map(() => (Math.random() - 0.5) * 10);
const geometry = new THREE.BufferGeometry();
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
// sizeAttenuation (default true) shrinks points with depth under a perspective camera: a point spans size * (canvas CSS height / 2) / depth CSS pixels
const material = new THREE.PointsMaterial({ size: 0.1, color: 0xffffff });
const points = new THREE.Points(geometry, material);
scene.add(points);
WebGPURenderer では、WebGPU バックエンドはマテリアルのサイズに関係なく、ポイントを 1 ピクセルで描画します。サイズを元に戻すインスタンス化された Sprite については、WebGPU と TSL のセクションを参照してください。
ライン
const linePoints = [
new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 1, 0), new THREE.Vector3(1, 0, 0),
];
const geometry = new THREE.BufferGeometry().setFromPoints(linePoints);
const material = new THREE.LineBasicMaterial({ color: 0xff0000 });
// Line connects the points in order; LineLoop closes back to the first point
const line = new THREE.Line(geometry, material);
const loop = new THREE.LineLoop(geometry, material);
scene.add(line, loop);
// LineSegments draws independent pairs: vertex 0-1, 2-3, ...
const segmentsGeometry = new THREE.BufferGeometry().setFromPoints([linePoints[0], linePoints[1], linePoints[1], linePoints[2]]);
const segments = new THREE.LineSegments(segmentsGeometry, material);
scene.add(segments);
WebGPURenderer では LineLoop はサポートされていません。エラーが記録され、何も描画されません。ループを閉じるには、最初の点を繰り返して Line を使用してください。
InstancedMesh
1 つのジオメトリを多数複製し、それぞれ異なるトランスフォームを持たせて、1 回の描画呼び出しで描画します。
const geometry = new THREE.BoxGeometry(0.2, 0.2, 0.2);
const material = new THREE.MeshBasicMaterial(); // multiplies by instance colors; MeshNormalMaterial ignores them
const count = 1000;
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
const dummy = new THREE.Object3D();
for (let i = 0; i < count; i++) {
dummy.position.set((Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20, (Math.random() - 0.5) * 20);
dummy.rotation.set(Math.random() * Math.PI, Math.random() * Math.PI, 0);
dummy.scale.setScalar(0.5 + Math.random());
dummy.updateMatrix();
instancedMesh.setMatrixAt(i, dummy.matrix);
}
instancedMesh.instanceMatrix.needsUpdate = true; // needed after changes once the mesh has rendered; harmless before
// setColorAt creates instanceColor on first use; flag it after the loop
for (let i = 0; i < count; i++) {
instancedMesh.setColorAt(i, new THREE.Color(Math.random(), Math.random(), Math.random()));
}
instancedMesh.instanceColor.needsUpdate = true;
scene.add(instancedMesh);
実行時にインスタンスを更新し、レイキャストする方法は次のとおりです。
// mesh here is an InstancedMesh
const matrix = new THREE.Matrix4();
const index = 7;
mesh.getMatrixAt(index, matrix);
matrix.setPosition(0, 2, 0); // move just this instance
mesh.setMatrixAt(index, matrix);
mesh.instanceMatrix.needsUpdate = true; // flag after any batch of setMatrixAt calls
mesh.computeBoundingSphere(); // instances moved: refresh the bounds used by frustum culling and raycasting
// Raycasting reports which instance was hit
const raycaster = new THREE.Raycaster();
const hits = raycaster.intersectObject(mesh);
if (hits.length > 0) {
const instanceId = hits[0].instanceId; // undefined for a non-instanced object
}
InstancedBufferGeometry
トランスフォームとカラー以外の、インスタンスごとの属性を指定します。
const count = 1000;
const geometry = new THREE.InstancedBufferGeometry();
geometry.copy(new THREE.BoxGeometry(1, 1, 1)); // copies the index and all attributes
geometry.instanceCount = count; // required: copy() carries no instance count
const offsets = new Float32Array(count * 3).map(() => Math.random() * 10);
geometry.setAttribute("offset", new THREE.InstancedBufferAttribute(offsets, 3));
// With WebGLRenderer and a ShaderMaterial, declare `in vec3 offset;` in the vertex
// shader yourself and add it to position (see threejs-shaders). Under WebGPURenderer,
// per-instance data is a TSL node - see the WebGPU and TSL section.
BatchedMesh
異なるジオメトリを 1 つのマテリアルで共有し、各インスタンスにそれぞれトランスフォームを設定します。WEBGL_multi_draw が利用可能な場合、WebGLRenderer は 1 回のマルチドロー呼び出しで描画します。WebGPU バックエンドでは、インスタンスごとに 1 回描画します。
const material = new THREE.MeshNormalMaterial();
const maxInstanceCount = 1000;
const maxVertexCount = 20000;
const maxIndexCount = 20000;
const batchedMesh = new THREE.BatchedMesh(maxInstanceCount, maxVertexCount, maxIndexCount, material);
const boxGeometryId = batchedMesh.addGeometry(new THREE.BoxGeometry(1, 1, 1));
const sphereGeometryId = batchedMesh.addGeometry(new THREE.SphereGeometry(0.5, 16, 16));
// A geometry renders only through its instances; added geometries need matching attributes and index state
const boxInstanceId = batchedMesh.addInstance(boxGeometryId);
batchedMesh.setMatrixAt(boxInstanceId, new THREE.Matrix4().setPosition(-2, 0, 0));
const sphereInstanceId = batchedMesh.addInstance(sphereGeometryId);
batchedMesh.setMatrixAt(sphereInstanceId, new THREE.Matrix4().setPosition(2, 0, 0));
scene.add(batchedMesh);
ジオメトリユーティリティ
import * as BufferGeometryUtils from "three/addons/utils/BufferGeometryUtils.js";
import * as MikkTSpace from "three/addons/libs/mikktspace.module.js";
const geo1 = new THREE.BoxGeometry(1, 1, 1);
const geo2 = new THREE.SphereGeometry(0.5, 16, 16);
// Merge static geometries; inputs need the same attributes and must all be indexed or all non-indexed (else null).
// useGroups = true adds one group per input, for a material array such as [boxMaterial, sphereMaterial].
const merged = BufferGeometryUtils.mergeGeometries([geo1, geo2], true);
// Weld duplicate vertices; also generates an index for a non-indexed geometry
const welded = BufferGeometryUtils.mergeVertices(geo2);
// Tangents for normal mapping - the core method needs an index plus position, normal and uv
welded.computeTangents();
// MikkTSpace tangents (the convention glTF normal maps use) come from the addon; this makes merged non-indexed in place
await MikkTSpace.ready;
BufferGeometryUtils.computeMikkTSpaceTangents(merged, MikkTSpace);
ジオメトリの変換
const geometry = new THREE.TorusKnotGeometry(0.5, 0.15, 100, 16);
geometry.center(); // move the vertices so the bounding-box center lands on the origin
geometry.computeBoundingBox(); // refresh after vertex changes
const size = new THREE.Vector3();
geometry.boundingBox.getSize(size);
const maxDim = Math.max(size.x, size.y, size.z);
geometry.scale(1 / maxDim, 1 / maxDim, 1 / maxDim); // fit inside a unit cube
const clone = geometry.clone(); // clone first: these methods mutate the geometry
clone.rotateX(Math.PI / 2);
clone.translate(0, 1, 0);
頂点データに触れずにオブジェクトをある点を中心に回転または拡大縮小するには、ジオメトリを再センタリングするのではなく、ピボットを設定します。
// Rotation and scale normally use the object origin; pivot is a point in the object's own coordinates
mesh.pivot = new THREE.Vector3(1, 0, 0); // default null
mesh.rotation.y += 0.05; // now swings around the local point (1, 0, 0)
モーフターゲット
const geometry = new THREE.BoxGeometry(1, 1, 1, 4, 4, 4);
const morphPositions = geometry.attributes.position.array.slice();
for (let i = 0; i < morphPositions.length; i += 3) {
morphPositions[i] *= 2; // stretch X
morphPositions[i + 1] *= 0.5; // squash Y
}
geometry.morphAttributes.position = [new THREE.BufferAttribute(morphPositions, 3)];
// Build the mesh after assigning morphAttributes so it picks up the targets
const mesh = new THREE.Mesh(geometry, new THREE.MeshNormalMaterial());
mesh.morphTargetInfluences[0] = 0.5; // 50% blend between base and target
scene.add(mesh);
パフォーマンスと破棄
- 描画呼び出しを減らすには、静的メッシュには mergeGeometries、ジオメトリの繰り返しには InstancedMesh、異なるジオメトリが多数ある場合には BatchedMesh を使用します(WEBGL_multi_draw が利用可能な場合、WebGLRenderer では 1 回のマルチドロー呼び出しになります)。
- セグメント数を増やすと滑らかさが増す一方、頂点数も増えます。球体のデフォルトである 32 x 16 はほとんどの用途に適しています。数値を増やすのは、近くから見る場合に限ってください。
scene.remove(mesh);
mesh.dispose(); // frees renderer state kept for this object only, not children or shared resources
mesh.geometry.dispose(); // frees the GPU buffers at once, even while another mesh shares them (that mesh uploads them again)
mesh.material.dispose(); // its textures need their own texture.dispose()
サブツリー全体を解放するには、よくある間違いのセクションと同様に、サブツリーを走査します。InstancedMesh.dispose() はモーフテクスチャも解放します。
WebGPU と TSL
WebGPURenderer でも、ジオメトリクラス、InstancedMesh、BatchedMesh は上記の API を維持します。異なる点は、WebGPU バックエンドでは Points が 1 ピクセルで描画され、BatchedMesh のインスタンスごとに 1 回描画され、LineLoop はエラーとともにスキップされること、そしてインスタンスごとのシェーダーデータが TSL ノードであることです。
import * as THREE from "three/webgpu";
const renderer = new THREE.WebGPURenderer({ antialias: true });
await renderer.init(); // before any other use of the renderer
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111111); // the WebGPU canvas is transparent by default
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 100);
camera.position.set(0, 0, 5);
renderer.setAnimationLoop(animate);
function animate() {
renderer.render(scene, camera);
}
WebGPU バックエンドでは、マテリアルのサイズに関係なく、ポイントは 1 ピクセルで描画されます。より大きなポイントは、各位置につき 1 回描画される単一の Sprite としてレンダリングされます。
import * as THREE from "three/webgpu";
import { instancedBufferAttribute } from "three/tsl";
const count = 1000;
const positions = new Float32Array(count * 3).map(() => (Math.random() - 0.5) * 10);
const material = new THREE.PointsNodeMaterial({
positionNode: instancedBufferAttribute(new THREE.InstancedBufferAttribute(positions, 3)),
size: 0.5, // depth-scaled like PointsMaterial; CSS pixels with sizeAttenuation: false; sizeNode for a node
});
const points = new THREE.Sprite(material);
points.count = count; // how many instances the sprite draws
points.frustumCulled = false; // culling tests one 0.7-unit sphere at the sprite origin, not the positions
scene.add(points);
カスタムのインスタンスごとの属性は GLSL 宣言ではなく、TSL ノードです。変位は、インスタンスのトランスフォームをすでに反映している positionLocal 上に構築してください。
import * as THREE from "three/webgpu";
import { instancedBufferAttribute, positionLocal } from "three/tsl";
const count = 1000;
const jitter = new THREE.InstancedBufferAttribute(new Float32Array(count * 3).map(() => Math.random() - 0.5), 3);
const material = new THREE.MeshBasicNodeMaterial();
material.positionNode = positionLocal.add(instancedBufferAttribute(jitter).mul(0.5)); // per-instance data, no GLSL
const mesh = new THREE.InstancedMesh(new THREE.BoxGeometry(0.2, 0.2, 0.2), material, count);
const matrix = new THREE.Matrix4();
for (let i = 0; i < count; i++) {
mesh.setMatrixAt(i, matrix.setPosition(Math.random() * 8 - 4, Math.random() * 8 - 4, Math.random() * 8 - 4));
}
scene.add(mesh);
よくある間違い
BufferGeometryUtils.computeTangents(geometry)→geometry.computeTangents():BufferGeometryUtils は computeTangents をエクスポートしません。コアメソッドには、インデックスと position、normal、uv 属性が必要です。new THREE.BufferAttribute(new Uint8Array(rgbBytes), 3)→new THREE.BufferAttribute(new Uint8Array(rgbBytes), 3, true):正規化されていないバイト値はシェーダーに 0.0~255.0 として渡され、色が白飛びします。geometry.copy(new THREE.BoxGeometry(1, 1, 1));→geometry.copy(new THREE.BoxGeometry(1, 1, 1)); geometry.instanceCount = count;:InstancedBufferGeometry.copy() は instanceCount をコピー元の undefined で上書きするため、WebGLRenderer はインスタンスを描画しません。batchedMesh.addGeometry(boxGeometry)→batchedMesh.addInstance(batchedMesh.addGeometry(boxGeometry)):BatchedMesh に追加したジオメトリは、addInstance で作成したインスタンスを通じてのみ描画されます(r166 以降必須)。new THREE.LineLoop(geometry, material)→new THREE.Line(new THREE.BufferGeometry().setFromPoints([...linePoints, linePoints[0]]), material):WebGPURenderer は LineLoop をサポートしていません。エラーを記録してオブジェクトをスキップするため、最初の点を繰り返して Line を閉じます。import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js"→import { TextGeometry } from "three/addons/geometries/TextGeometry.js":examples/jsm の指定子は、three/addons/ はマッピングするものの three/examples/ はマッピングしない、ドキュメント記載のインポートマップでは解決されません。capsule.parameters.length→capsule.parameters.height:CapsuleGeometry のパラメーターは r176 でlengthからheightに名前が変更され、5 番目のパラメーターheightSegmentsが追加されました。{ font: font, size: 1, height: 0.2 }→{ font: font, size: 1, depth: 0.2 }:TextGeometry の押し出しオプションは r163 でheightからdepthに名前が変更されました。古い height は無視され、テキストは既定の 50 単位だけ押し出されます。new THREE.Points(cloud, new THREE.PointsMaterial({ size: 10 }))→new THREE.Sprite(new THREE.PointsNodeMaterial({ positionNode: instancedBufferAttribute(attr) })):WebGPU バックエンドでは、size の値にかかわらず Points は 1 ピクセルで描画されるため、大きな点にはインスタンス化した Sprite 形式が必要です。new THREE.ShaderMaterial({ vertexShader: offsetVertexShader })→material.positionNode = positionLocal.add(instancedBufferAttribute(offsetAttribute)):WebGPURenderer には ShaderMaterial のノード版がありません。マテリアルに互換性がないというログを記録して、通常の NodeMaterial にフォールバックするため、インスタンスごとのデータには TSL ノードを使います。material.positionNode = positionGeometry.add(offset)→material.positionNode = positionLocal.add(offset):positionLocal にはスキニング、モーフターゲット、バッチ処理、インスタンス化がすでに反映されています。positionGeometry は生の属性です。mesh.morphTargetInfluences[0] = 0.5→mesh.updateMorphTargets(); mesh.morphTargetInfluences[0] = 0.5;:モーフ属性の影響値は、メッシュの構築時に認識されたものだけが存在します。そのため、既存のメッシュにターゲットを追加した後は更新してください。gltf.scene.dispose()→gltf.scene.traverse((child) => { child.geometry?.dispose(); for (const material of [child.material ?? []].flat()) { for (const value of Object.values(material)) if (value?.isTexture) value.dispose(); material.dispose(); } child.dispose(); }):Object3D.dispose() はそのオブジェクトのレンダラー状態のみを解放します。子を走査したり、ジオメトリ、マテリアル、テクスチャを破棄したりはしません。new THREE.BoxBufferGeometry(1, 1, 1)→new THREE.BoxGeometry(1, 1, 1):組み込み形状の*BufferGeometryエイリアスはなくなりました。組み込みジオメトリはすべて、もともと BufferGeometry です。BufferGeometryUtils.mergeBufferGeometries([geo1, geo2])→BufferGeometryUtils.mergeGeometries([geo1, geo2]):古い名前はエクスポートされないため、呼び出すと TypeError が発生します。geometry.setAttribute("uv2", geometry.attributes.uv)→material.aoMap = aoTexture;:aoMap は既定でuv(チャンネル 0)を読み取ります。別途ベイクした展開図のみをuv1に設定し、その場合はaoTexture.channel = 1とします。new THREE.LineBasicMaterial({ linewidth: 4 })→new Line2(new LineGeometry().setPositions(xyzArray), new LineMaterial({ linewidth: 4 })):WebGL と WebGPU は、linewidth の指定にかかわらず、線プリミティブを 1 ピクセル幅で描画します。three/addons/lines/ の Line2、LineGeometry、LineMaterial は WebGLRenderer で太い線を描画し、WebGPURenderer では three/addons/lines/webgpu/Line2.js の Line2 に three/webgpu の Line2NodeMaterial を使用します。
関連項目
threejs-fundamentals- シーンのセットアップ、レンダーループ、Object3D の変換と破棄。threejs-materials- ここで構築するメッシュのマテリアルの種類と設定。threejs-shaders- WebGLRenderer 用の ShaderMaterial の頂点コードとフラグメントコード。threejs-animation- モーフターゲットの影響度と変換を時間経過に応じてアニメーションさせる方法。threejs-loaders- 独自のジオメトリを持ち込む glTF などの形式。threejs-performance- 計測したドローコール数に基づいて、マージ、インスタンシング、バッチ処理、LOD のどれを選ぶか。