threejs-webgl について
threejs-webglは、WebGLやWebGPUでブラウザー向けのインタラクティブな3Dシーンを作るThree.js開発者向けのスキルです。実装パターンに加え、最適化の指針、リファレンス、補助スクリプトを提供します。
主な機能
- 01シーン、カメラ、レンダラーの設定
- 02ジオメトリ、マテリアル、ライトの実装例
- 03モデル読み込みとインタラクション処理
- 04アニメーションとポストプロセスの例
- 05パフォーマンス調整とリソース解放
- 06リファレンスと補助スクリプト
活用シーン
- 01インタラクティブな3Dウェブ体験の構築
- 02glTFモデルの読み込みと表示
- 03Three.jsシーンのパフォーマンス最適化
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ freshtechbro/ claudedesignskills --skill threejs-webglインストール後、Claude Code で/threejs-webglを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
assets/starter_scene/index.htmlmain.jsREADME.mdreferences/api_reference.mdmaterials_guide.mdoptimization_checklist.mdscripts/setup_scene.py詳細
Three.js WebGL/WebGPU 開発
概要
Three.js は、WebGL と WebGPU を使用してウェブブラウザーで 3D グラフィックスを作成するための、業界標準の JavaScript ライブラリです。このスキルでは、シーン、カメラ、レンダラー、ジオメトリ、マテリアル、ライト、テクスチャ、アニメーションなど、パフォーマンスに優れたインタラクティブな 3D 体験を構築するための包括的なガイダンスを提供します。
基本概念
シーングラフのアーキテクチャ
Three.js では、すべての 3D オブジェクトをツリー構造にまとめる階層型のシーングラフを使用します。
Scene
├── Camera
├── Lights
│ ├── AmbientLight
│ ├── DirectionalLight
│ └── PointLight
├── Meshes
│ ├── Mesh (Geometry + Material)
│ └── InstancedMesh
└── Groups
必須コンポーネント
すべての Three.js アプリケーションには、以下の基本要素が必要です。
- シーン:すべての 3D オブジェクトを格納します
- カメラ:視点を定義します
- レンダラー:シーンをキャンバスに描画します(WebGL または WebGPU)
- ジオメトリ:オブジェクトの形状を定義します
- マテリアル:表面の外観を定義します
- メッシュ:ジオメトリとマテリアルを組み合わせます
クイックスタートのパターン
基本的なシーンのセットアップ
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// Scene, Camera, Renderer
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x333333);
const camera = new THREE.PerspectiveCamera(
75, // FOV
window.innerWidth / window.innerHeight, // Aspect ratio
0.1, // Near clipping plane
1000 // Far clipping plane
);
camera.position.set(0, 2, 5);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
document.body.appendChild(renderer.domElement);
// Lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 7.5);
directionalLight.castShadow = true;
scene.add(directionalLight);
// Controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// Animation Loop
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
// Handle Resize
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
WebGPU のセットアップ(最新の代替手段)
import * as THREE from 'three/webgpu';
const renderer = new THREE.WebGPURenderer({ antialias: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setAnimationLoop(animate);
renderer.toneMapping = THREE.LinearToneMapping;
renderer.toneMappingExposure = 1;
document.body.appendChild(renderer.domElement);
一般的なパターン
1. マテリアルを使ったメッシュの作成
// Basic Mesh
const geometry = new THREE.BoxGeometry(1, 1, 1);
const material = new THREE.MeshStandardMaterial({
color: 0x00ff00,
roughness: 0.5,
metalness: 0.5
});
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// Textured Mesh
const loader = new THREE.TextureLoader();
const texture = loader.load('texture.jpg');
texture.colorSpace = THREE.SRGBColorSpace;
const texturedMaterial = new THREE.MeshStandardMaterial({
map: texture
});
const mesh = new THREE.Mesh(geometry, texturedMaterial);
scene.add(mesh);
2. ライティング戦略
// Three-Point Lighting Setup
function setupThreePointLight(scene) {
// Key Light (Main)
const keyLight = new THREE.DirectionalLight(0xffffff, 3);
keyLight.position.set(5, 10, 7.5);
keyLight.castShadow = true;
scene.add(keyLight);
// Fill Light (Softens shadows)
const fillLight = new THREE.DirectionalLight(0xffffff, 1);
fillLight.position.set(-5, 5, -5);
scene.add(fillLight);
// Rim Light (Edge definition)
const rimLight = new THREE.DirectionalLight(0xffffff, 0.5);
rimLight.position.set(0, 5, -10);
scene.add(rimLight);
// Ambient (Base illumination)
const ambient = new THREE.AmbientLight(0x404040, 0.5);
scene.add(ambient);
}
// Physical Light (Realistic)
const bulbLight = new THREE.PointLight(0xffee88, 1, 100, 2);
bulbLight.power = 1700; // Lumens (100W bulb equivalent)
bulbLight.castShadow = true;
scene.add(bulbLight);
// Hemisphere Light (Sky + Ground)
const hemiLight = new THREE.HemisphereLight(
0xddeeff, // Sky color
0x0f0e0d, // Ground color
0.02
);
scene.add(hemiLight);
3. インスタンス化ジオメトリ(パフォーマンス)
// For rendering thousands of similar objects efficiently
const geometry = new THREE.SphereGeometry(0.1, 16, 16);
const material = new THREE.MeshStandardMaterial({ color: 0xff0000 });
const instancedMesh = new THREE.InstancedMesh(geometry, material, 1000);
const matrix = new THREE.Matrix4();
const color = new THREE.Color();
for (let i = 0; i < 1000; i++) {
matrix.setPosition(
Math.random() * 10 - 5,
Math.random() * 10 - 5,
Math.random() * 10 - 5
);
instancedMesh.setMatrixAt(i, matrix);
instancedMesh.setColorAt(i, color.setHex(Math.random() * 0xffffff));
}
instancedMesh.instanceMatrix.needsUpdate = true;
scene.add(instancedMesh);
4. 3D モデルの読み込み(glTF)
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
// Setup loaders
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('/draco/');
const gltfLoader = new GLTFLoader();
gltfLoader.setDRACOLoader(dracoLoader);
// Load model
gltfLoader.load('model.glb', (gltf) => {
const model = gltf.scene;
// Enable shadows
model.traverse((child) => {
if (child.isMesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
scene.add(model);
// Handle animations
if (gltf.animations.length > 0) {
const mixer = new THREE.AnimationMixer(model);
const action = mixer.clipAction(gltf.animations[0]);
action.play();
// In animation loop:
// mixer.update(deltaTime);
}
});
5. シャドウの設定
// Enable shadows on renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // or VSMShadowMap
// Configure light shadows
directionalLight.castShadow = true;
directionalLight.shadow.mapSize.width = 2048;
directionalLight.shadow.mapSize.height = 2048;
directionalLight.shadow.camera.near = 0.5;
directionalLight.shadow.camera.far = 50;
directionalLight.shadow.camera.left = -10;
directionalLight.shadow.camera.right = 10;
directionalLight.shadow.camera.top = 10;
directionalLight.shadow.camera.bottom = -10;
directionalLight.shadow.radius = 4;
directionalLight.shadow.blurSamples = 8;
// Objects casting/receiving shadows
mesh.castShadow = true;
mesh.receiveShadow = true;
6. レイキャスティング(インタラクション)
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObjects(scene.children, true);
if (intersects.length > 0) {
const object = intersects[0].object;
object.material.color.set(0xff0000);
}
}
window.addEventListener('click', onMouseClick);
統合パターン
アニメーション用の GSAP との連携
import gsap from 'gsap';
// Animate camera
gsap.to(camera.position, {
x: 5,
y: 3,
z: 10,
duration: 2,
ease: "power2.inOut",
onUpdate: () => {
camera.lookAt(scene.position);
}
});
// Animate mesh properties
gsap.to(mesh.rotation, {
y: Math.PI * 2,
duration: 3,
repeat: -1,
ease: "none"
});
React との連携(react-three-fiber スキルを参照)
// Three.js integrates naturally with React Three Fiber
// Use the react-three-fiber skill for React integration patterns
ポストプロセッシングとの連携
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';
const composer = new EffectComposer(renderer);
composer.addPass(new RenderPass(scene, camera));
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5, // strength
0.4, // radius
0.85 // threshold
);
composer.addPass(bloomPass);
// In animation loop:
composer.render();
パフォーマンスの最適化
1. ジオメトリの再利用
// Bad: Creates new geometry for each mesh
for (let i = 0; i < 100; i++) {
const geometry = new THREE.BoxGeometry(1, 1, 1);
const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
}
// Good: Reuse geometry
const sharedGeometry = new THREE.BoxGeometry(1, 1, 1);
for (let i = 0; i < 100; i++) {
const mesh = new THREE.Mesh(sharedGeometry, material);
scene.add(mesh);
}
2. 繰り返しオブジェクトには InstancedMesh を使用
同一のオブジェクトが数百から数千個ある場合は、InstancedMesh を使用してください(上記のパターンを参照)。
3. テクスチャの最適化
// Compress textures
texture.generateMipmaps = true;
texture.minFilter = THREE.LinearMipmapLinearFilter;
texture.magFilter = THREE.LinearFilter;
// Use power-of-two dimensions (512, 1024, 2048)
// Consider texture atlases for multiple small textures
4. 詳細度(LOD)
const lod = new THREE.LOD();
lod.addLevel(highDetailMesh, 0); // 0-50 units
lod.addLevel(mediumDetailMesh, 50); // 50-100 units
lod.addLevel(lowDetailMesh, 100); // 100+ units
scene.add(lod);
5. 視錐台カリング
Three.js はカメラの視野外にあるオブジェクトを自動的にカリングします。オブジェクトに正しいバウンディングスフィアが設定されていることを確認してください。
mesh.geometry.computeBoundingSphere();
6. リソースの破棄
function disposeScene() {
scene.traverse((object) => {
if (object.geometry) object.geometry.dispose();
if (object.material) {
if (Array.isArray(object.material)) {
object.material.forEach(material => material.dispose());
} else {
object.material.dispose();
}
}
});
renderer.dispose();
}
ベストプラクティス
1. 一貫したタイミングにはアニメーションクロックを使用
const clock = new THREE.Clock();
function animate() {
const deltaTime = clock.getDelta();
const elapsedTime = clock.getElapsedTime();
// Use deltaTime for frame-independent animations
mesh.rotation.y += deltaTime * Math.PI * 0.5; // 90° per second
renderer.render(scene, camera);
}
2. カメラ設定のガイドライン
- 視野角:ほとんどのアプリケーションでは 45~75°
- ニアプレーン:可能な限り遠くに設定します(Z ファイティングを回避)
- ファープレーン:可能な限り近くに設定します(精度を確保)
- アスペクト比:常にキャンバスの寸法に合わせます
3. マテリアルの選択
- MeshBasicMaterial:ライティングなし、フラットカラー(デバッグ、UI)
- MeshLambertMaterial:軽量な拡散ライティング(モバイル)
- MeshPhongMaterial:鏡面ハイライト(旧来の標準)
- MeshStandardMaterial:PBR、リアルな表現(推奨)
- MeshPhysicalMaterial:高度な PBR(クリアコート、透過)
4. 座標系
- Three.js は右手座標系を使用します
- +Y は上、+Z はカメラ方向、+X は右です
- 回転にはラジアンを使用します(Math.PI = 180°)
5. シーンの整理
// Group related objects
const building = new THREE.Group();
building.add(walls, roof, windows);
scene.add(building);
// Use meaningful names
mesh.name = 'player-character';
const found = scene.getObjectByName('player-character');
よくある落とし穴
1. リサイズ時にアスペクト比を更新しない
ウィンドウのサイズ変更時には、必ずカメラのアスペクト比と投影行列を更新してください。
2. アニメーションループ内で新しいオブジェクトを作成する
// Bad: Memory leak
function animate() {
const geometry = new THREE.BoxGeometry(); // Created every frame!
// ...
}
// Good: Create once outside loop
const geometry = new THREE.BoxGeometry();
function animate() {
// Reuse geometry
}
3. シャドウを有効にし忘れる
レンダラー、ライト、オブジェクトでシャドウを有効にしてください。
4. Z ファイティング(ちらつき)
- ニアプレーンの距離を大きくします
- ファープレーンの距離を小さくします
- 同一平面上で重なるサーフェスを避けます
material.polygonOffset = trueとmaterial.polygonOffsetFactorを使用します
5. 色空間の問題
// Always set color space for textures
texture.colorSpace = THREE.SRGBColorSpace;
// Set renderer output encoding
renderer.outputColorSpace = THREE.SRGBColorSpace;
6. リソースを破棄しない
不要になったジオメトリ、マテリアル、テクスチャ、レンダラーには、必ず .dispose() を呼び出してください。
リソース
このスキルには、Three.js 開発を効率化するためのリソースが含まれています。
references/
api_reference.md:主要クラス(Scene、Camera、Renderer など)のクイック API リファレンスmaterials_guide.md:マテリアルの種類とプロパティに関する包括的なガイドoptimization_checklist.md:パフォーマンス最適化の戦略
scripts/
setup_scene.py:Three.js のシーンセットアップ用ボイラープレートコードを生成しますtexture_optimizer.py:ウェブ向けにテクスチャを一括最適化します(サイズ変更、圧縮)gltf_validator.py:使用前に glTF モデルを検証します
assets/
starter_scene/:完全な HTML/JS ボイラープレートプロジェクトshaders/:カスタム GLSL シェーダーの例(頂点、フラグメント)hdri/:PBR ライティング用環境マップdraco/:圧縮モデル用 DRACO デコーダー
高度なトピック
カスタムシェーダー(GLSL)
const material = new THREE.ShaderMaterial({
uniforms: {
uTime: { value: 0.0 },
uColor: { value: new THREE.Color(0x00ff00) }
},
vertexShader: `
varying vec2 vUv;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform float uTime;
uniform vec3 uColor;
varying vec2 vUv;
void main() {
gl_FragColor = vec4(uColor * vUv.x, 1.0);
}
`
});
レンダーターゲット(レンダー・トゥ・テクスチャ)
const renderTarget = new THREE.WebGLRenderTarget(512, 512);
// Render scene to texture
renderer.setRenderTarget(renderTarget);
renderer.render(scene, camera);
renderer.setRenderTarget(null);
// Use texture
const material = new THREE.MeshBasicMaterial({
map: renderTarget.texture
});
GPU 計算(GPGPU)
パーティクルシミュレーションや布の物理演算などには、GPUComputationRenderer を使用します。
このスキルを使用する場面
次のような場合にこのスキルを使用します:
- インタラクティブな 3D ウェブ体験を構築する
- 製品コンフィギュレーターやビジュアライザーを作成する
- WebGL/WebGPU レンダリングを実装する
- 3D モデル、シーン、アニメーションを扱う
- Three.js のパフォーマンスを最適化する
- Three.js を他のライブラリ(GSAP、React など)と統合する
- Three.js のレンダリングに関する問題をデバッグする
React との統合には、react-three-fiber スキルを使用してください。 アニメーションには、gsap-scrolltrigger スキルと組み合わせてください。 UI アニメーションには、motion-framer スキルを使用してください。