SKILL.md
唯讀
名稱
threejs-geometry
描述
Three.js 幾何體建立 - 內建形狀、BufferGeometry、自訂幾何體、實例化。用於建立 3D 形狀、處理頂點、建構自訂網格,或透過實例化渲染進行最佳化。
Three.js 幾何體
快速開始
import * as THREE from "three";
// 內建幾何體
const box = new THREE.BoxGeometry(1, 1, 1);
const sphere = new THREE.SphereGeometry(0.5, 32, 32);
const plane = new THREE.PlaneGeometry(10, 10);
// 建立網格
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const mesh = new THREE.Mesh(box, material);
scene.add(mesh);
內建幾何體
基本形狀
// 立方體 - 寬度, 高度, 深度, 寬度分段數, 高度分段數, 深度分段數
new THREE.BoxGeometry(1, 1, 1, 1, 1, 1);
// 球體 - 半徑, 寬度分段數, 高度分段數, phiStart, phiLength, thetaStart, thetaLength
new THREE.SphereGeometry(1, 32, 32);
new THREE.SphereGeometry(1, 32, 32, 0, Math.PI * 2, 0, Math.PI); // 完整球體
new THREE.SphereGeometry(1, 32, 32, 0, Math.PI); // 半球體
// 平面 - 寬度, 高度, 寬度分段數, 高度分段數
new THREE.PlaneGeometry(10, 10, 1, 1);
// 圓形 - 半徑, 分段數, thetaStart, thetaLength
new THREE.CircleGeometry(1, 32);
new THREE.CircleGeometry(1, 32, 0, Math.PI); // 半圓
// 圓柱體 - 頂部半徑, 底部半徑, 高度, 徑向分段數, 高度分段數, 是否開口
new THREE.CylinderGeometry(1, 1, 2, 32, 1, false);
new THREE.CylinderGeometry(0, 1, 2, 32); // 圓錐體
new THREE.CylinderGeometry(1, 1, 2, 6); // 六角柱
// 圓錐體 - 半徑, 高度, 徑向分段數, 高度分段數, 是否開口
new THREE.ConeGeometry(1, 2, 32, 1, false);
// 環面 - 半徑, 管徑, 徑向分段數, 管狀分段數, 弧長
new THREE.TorusGeometry(1, 0.4, 16, 100);
// 環面結 - 半徑, 管徑, 管狀分段數, 徑向分段數, p, q
new THREE.TorusKnotGeometry(1, 0.4, 100, 16, 2, 3);
// 環形 - 內半徑, 外半徑, thetaSegments, phiSegments
new THREE.RingGeometry(0.5, 1, 32, 1);
進階形狀
// 膠囊體 - 半徑, 長度, 蓋子分段數, 徑向分段數
new THREE.CapsuleGeometry(0.5, 1, 4, 8);
// 十二面體 - 半徑, 細節
new THREE.DodecahedronGeometry(1, 0);
// 二十面體 - 半徑, 細節 (0 = 20 面, 越高越平滑)
new THREE.IcosahedronGeometry(1, 0);
// 八面體 - 半徑, 細節
new THREE.OctahedronGeometry(1, 0);
// 四面體 - 半徑, 細節
new THREE.TetrahedronGeometry(1, 0);
// 多面體 - 頂點, 索引, 半徑, 細節
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);
路徑為基礎的形狀
// 車床幾何體 - 點陣列[], 分段數, phiStart, phiLength
const points = [
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(points, 32);
// 擠出幾何體 - 形狀, 選項
const shape = new THREE.Shape();
shape.moveTo(0, 0);
shape.lineTo(1, 0);
shape.lineTo(1, 1);
shape.lineTo(0, 1);
shape.lineTo(0, 0);
const extrudeSettings = {
steps: 2,
depth: 1,
bevelEnabled: true,
bevelThickness: 0.1,
bevelSize: 0.1,
bevelSegments: 3,
};
new THREE.ExtrudeGeometry(shape, extrudeSettings);
// 管狀幾何體 - 路徑, 管狀分段數, 半徑, 徑向分段數, 是否封閉
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/examples/jsm/loaders/FontLoader.js";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry.js";
const loader = new FontLoader();
loader.load("fonts/helvetiker_regular.typeface.json", (font) => {
const geometry = new TextGeometry("Hello", {
font: font,
size: 1,
depth: 0.2, // 舊版本中為 'height'
curveSegments: 12,
bevelEnabled: true,
bevelThickness: 0.03,
bevelSize: 0.02,
bevelSegments: 5,
});
// 置中文字
geometry.computeBoundingBox();
geometry.center();
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
});
BufferGeometry
所有幾何體的基底類別。以型別陣列儲存資料以提升 GPU 效率。
自訂 BufferGeometry
const geometry = new THREE.BufferGeometry();
// 頂點 (每個頂點 3 個浮點數: x, y, z)
const vertices = new Float32Array([
-1,
-1,
0, // 頂點 0
1,
-1,
0, // 頂點 1
1,
1,
0, // 頂點 2
-1,
1,
0, // 頂點 3
]);
geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
// 索引 (用於索引幾何體 - 重複使用頂點)
const indices = new Uint16Array([
0,
1,
2, // 三角形 1
0,
2,
3, // 三角形 2
]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));
// 法線 (光照所需)
const normals = new Float32Array([0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1]);
geometry.setAttribute("normal", new THREE.BufferAttribute(normals, 3));
// UV (紋理貼圖用)
const uvs = new Float32Array([0, 0, 1, 0, 1, 1, 0, 1]);
geometry.setAttribute("uv", new THREE.BufferAttribute(uvs, 2));
// 顏色 (每個頂點顏色)
const colors = new Float32Array([
1,
0,
0, // 紅色
0,
1,
0, // 綠色
0,
0,
1, // 藍色
1,
1,
0, // 黃色
]);
geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
// 搭配使用: material.vertexColors = true
BufferAttribute 型別
// 常見屬性型別
new THREE.BufferAttribute(array, itemSize);
// 型別陣列選項
new Float32Array(count * itemSize); // 位置、法線、UV
new Uint16Array(count); // 索引 (最多 65535 個頂點)
new Uint32Array(count); // 索引 (較大網格)
new Uint8Array(count * itemSize); // 顏色 (0-255 範圍)
// 項目大小
// 位置: 3 (x, y, z)
// 法線: 3 (x, y, z)
// UV: 2 (u, v)
// 顏色: 3 (r, g, b) 或 4 (r, g, b, a)
// 索引: 1
修改 BufferGeometry
const positions = geometry.attributes.position;
// 修改頂點
positions.setXYZ(index, x, y, z);
// 存取頂點
const x = positions.getX(index);
const y = positions.getY(index);
const z = positions.getZ(index);
// 標記需要更新 GPU
positions.needsUpdate = true;
// 位置變更後重新計算法線
geometry.computeVertexNormals();
// 變更後重新計算包圍盒/包圍球
geometry.computeBoundingBox();
geometry.computeBoundingSphere();
交錯緩衝區 (進階)
// 大型網格更有效率的記憶體佈局
const interleavedBuffer = new THREE.InterleavedBuffer(
new Float32Array([
// pos.x, pos.y, pos.z, uv.u, uv.v (每個頂點重複)
-1, -1, 0, 0, 0, 1, -1, 0, 1, 0, 1, 1, 0, 1, 1, -1, 1, 0, 0, 1,
]),
5, // 步長 (每個頂點的浮點數)
);
geometry.setAttribute(
"position",
new THREE.InterleavedBufferAttribute(interleavedBuffer, 3, 0),
); // 大小 3, 偏移 0
geometry.setAttribute(
"uv",
new THREE.InterleavedBufferAttribute(interleavedBuffer, 2, 3),
); // 大小 2, 偏移 3
EdgesGeometry 與 WireframeGeometry
// 邊緣線 (僅硬邊)
const edges = new THREE.EdgesGeometry(boxGeometry, 15); // 15 = 閾值角度
const edgeMesh = new THREE.LineSegments(
edges,
new THREE.LineBasicMaterial({ color: 0xffffff }),
);
// 線框 (所有三角形)
const wireframe = new THREE.WireframeGeometry(boxGeometry);
const wireMesh = new THREE.LineSegments(
wireframe,
new THREE.LineBasicMaterial({ color: 0xffffff }),
);
點雲
// 建立點雲
const geometry = new THREE.BufferGeometry();
const positions = new Float32Array(1000 * 3);
for (let i = 0; i < 1000; i++) {
positions[i * 3] = (Math.random() - 0.5) * 10;
positions[i * 3 + 1] = (Math.random() - 0.5) * 10;
positions[i * 3 + 2] = (Math.random() - 0.5) * 10;
}
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
const material = new THREE.PointsMaterial({
size: 0.1,
sizeAttenuation: true, // 大小隨距離遞減
color: 0xffffff,
});
const points = new THREE.Points(geometry, material);
scene.add(points);
線條
// 線條 (連接的點)
const points = [
new THREE.Vector3(-1, 0, 0),
new THREE.Vector3(0, 1, 0),
new THREE.Vector3(1, 0, 0),
];
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(
geometry,
new THREE.LineBasicMaterial({ color: 0xff0000 }),
);
// LineLoop (封閉迴圈)
const loop = new THREE.LineLoop(geometry, material);
// LineSegments (點對)
const segmentsGeometry = new THREE.BufferGeometry();
segmentsGeometry.setAttribute(
"position",
new THREE.BufferAttribute(
new Float32Array([
-1,
0,
0,
0,
1,
0, // 線段 1
0,
1,
0,
1,
0,
0, // 線段 2
]),
3,
),
);
const segments = new THREE.LineSegments(segmentsGeometry, material);
InstancedMesh
高效渲染多個相同幾何體的副本。
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const count = 1000;
const instancedMesh = new THREE.InstancedMesh(geometry, material, count);
// 設定每個實例的變換
const dummy = new THREE.Object3D();
const matrix = new THREE.Matrix4();
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);
}
// 標記需要更新 GPU
instancedMesh.instanceMatrix.needsUpdate = true;
// 可選:每個實例的顏色
instancedMesh.instanceColor = new THREE.InstancedBufferAttribute(
new Float32Array(count * 3),
3,
);
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);
執行時更新實例
// 更新單一實例
const matrix = new THREE.Matrix4();
instancedMesh.getMatrixAt(index, matrix);
// 修改矩陣...
instancedMesh.setMatrixAt(index, matrix);
instancedMesh.instanceMatrix.needsUpdate = true;
// 對實例網格進行光線投射
const intersects = raycaster.intersectObject(instancedMesh);
if (intersects.length > 0) {
const instanceId = intersects[0].instanceId;
}
InstancedBufferGeometry (進階)
用於自訂每個實例的屬性,超越變換/顏色。
const geometry = new THREE.InstancedBufferGeometry();
geometry.copy(new THREE.BoxGeometry(1, 1, 1));
// 加入每個實例的屬性
const offsets = new Float32Array(count * 3);
for (let i = 0; i < count; i++) {
offsets[i * 3] = Math.random() * 10;
offsets[i * 3 + 1] = Math.random() * 10;
offsets[i * 3 + 2] = Math.random() * 10;
}
geometry.setAttribute("offset", new THREE.InstancedBufferAttribute(offsets, 3));
// 在著色器中使用
// attribute vec3 offset;
// vec3 transformed = position + offset;
幾何體工具
import * as BufferGeometryUtils from "three/examples/jsm/utils/BufferGeometryUtils.js";
// 合併幾何體 (必須有相同屬性)
const merged = BufferGeometryUtils.mergeGeometries([geo1, geo2, geo3]);
// 合併並保留群組 (用於多材質)
const merged = BufferGeometryUtils.mergeGeometries([geo1, geo2], true);
// 計算切線 (法線貼圖所需)
BufferGeometryUtils.computeTangents(geometry);
// 交錯屬性以提升效能
const interleaved = BufferGeometryUtils.interleaveAttributes([
geometry.attributes.position,
geometry.attributes.normal,
geometry.attributes.uv,
]);
常見模式
置中幾何體
geometry.computeBoundingBox();
geometry.center(); // 移動頂點使中心位於原點
縮放至適合
geometry.computeBoundingBox();
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);
複製與變換
const clone = geometry.clone();
clone.rotateX(Math.PI / 2);
clone.translate(0, 1, 0);
clone.scale(2, 2, 2);
形變目標
// 基礎幾何體
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; // 縮放 X
morphPositions[i + 1] *= 0.5; // 壓扁 Y
}
geometry.morphAttributes.position = [
new THREE.BufferAttribute(new Float32Array(morphPositions), 3),
];
const mesh = new THREE.Mesh(geometry, material);
mesh.morphTargetInfluences[0] = 0.5; // 50% 混合
效能提示
- 使用索引幾何體:透過索引重複使用頂點
- 合併靜態網格:使用
mergeGeometries減少繪製呼叫 - 使用 InstancedMesh:用於大量相同物件
- 選擇適當的分段數:更多分段 = 更平滑但更慢
- 釋放未使用的幾何體:
geometry.dispose()
// 常見用途的良好分段數
new THREE.SphereGeometry(1, 32, 32); // 良好品質
new THREE.SphereGeometry(1, 64, 64); // 高品質
new THREE.SphereGeometry(1, 16, 16); // 效能模式
// 使用完畢後釋放
geometry.dispose();
另請參閱
threejs-fundamentals- 場景設定與 Object3Dthreejs-materials- 網格的材質類型threejs-shaders- 自訂頂點操作






