mcpField
T
threejs

threejs

github.com/secondsky開発240467 回インストール5 週間前に更新
リポジトリ

threejs について

Three.jsで3D Web体験を開発する人向けのリファレンススキルです。API資料や実践的な指針、コード例を、描画やアニメーション、操作などの分野別にまとめています。

主な機能

  1. 01シーン、カメラ、レンダラーの設定
  2. 02ジオメトリ、マテリアル、ライティング
  3. 03テクスチャ、ローダー、アニメーション
  4. 04シェーダーとポストプロセス
  5. 05レイキャストとカメラ操作
  6. 06パフォーマンスとリソース解放の指針

活用シーン

  1. 01Three.jsのシーンとレンダラーの構築
  2. 02GLTFモデルの読み込みとアニメーション
  3. 03カスタムシェーダーや視覚効果の追加
  4. 043Dシーンのパフォーマンス手法の確認

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/secondsky/claude-skills --skill threejs

インストール後、Claude Code で/threejsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
threejs-animation.md
threejs-fundamentals.md
threejs-geometry.md
threejs-interaction.md
threejs-lighting.md
threejs-loaders.md
threejs-materials.md
threejs-postprocessing.md
threejs-shaders.md
threejs-textures.md

詳細

Three.js スキル

概要

Three.js を使った 3D ウェブ体験の構築に役立つ包括的なナレッジベースです。このスキルでは、Three.js の主要なすべての領域について、正確な API リファレンス、ベストプラクティス、動作するコード例を提供します。

Three.js のバージョン:r160+(2024年1月)

クイックリファレンス

主なトピック

このスキルでは、Three.js の重要な 10 の領域を扱います。

  1. 基礎 - シーンのセットアップ、カメラ、レンダラー、Object3D の階層構造
  2. ジオメトリ - 組み込み形状、BufferGeometry、カスタムジオメトリ、インスタンシング
  3. マテリアル - PBR マテリアル、シェーダーマテリアル、マテリアルのプロパティ
  4. ライティング - ライトの種類、シャドウ、環境ライティング
  5. テクスチャ - UV マッピング、環境マップ、レンダーターゲット
  6. アニメーション - キーフレームアニメーション、スケルタルアニメーション、アニメーションのミキシング
  7. ローダー - GLTF/GLB の読み込み、非同期処理のパターン、キャッシュ
  8. シェーダー - GLSL の基本、ShaderMaterial、カスタムエフェクト
  9. ポストプロセッシング - EffectComposer、ブルーム、被写界深度、カスタムパス
  10. インタラクション - レイキャスティング、カメラコントロール、マウス/タッチ入力

リファレンスを読み込むタイミング

現在のタスクに応じて、詳細なリファレンスファイルを読み込んでください。

  • 基本的なシーンのセットアップ、カメラ、レンダラー → references/threejs-fundamentals.md を読み込む
  • 形状の作成、カスタムジオメトリ、インスタンシング → references/threejs-geometry.md を読み込む
  • マテリアルのプロパティ、PBR、シェーダーマテリアル → references/threejs-materials.md を読み込む
  • ライトの追加、シャドウの設定 → references/threejs-lighting.md を読み込む
  • テクスチャの読み込み、UV マッピング、環境マップ → references/threejs-textures.md を読み込む
  • オブジェクトのアニメーション、GLTF アニメーション、ミキシング → references/threejs-animation.md を読み込む
  • GLTF/GLB モデルの読み込み、Draco 圧縮 → references/threejs-loaders.md を読み込む
  • GLSL シェーダーの作成、カスタム視覚効果 → references/threejs-shaders.md を読み込む
  • ブルーム、被写界深度、画面エフェクトの追加 → references/threejs-postprocessing.md を読み込む
  • レイキャスティング、マウスによるオブジェクト選択、カメラコントロール → references/threejs-interaction.md を読み込む

クイックスタートの例

1. 基礎:基本的なシーン
import * as THREE from 'three';

// Scene, camera, renderer
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer({ antialias: true });

renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
document.body.appendChild(renderer.domElement);

// Create cube
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

// Add light
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 5, 5);
scene.add(dirLight);

camera.position.z = 5;

// Animation loop
function animate() {
  requestAnimationFrame(animate);
  cube.rotation.x += 0.01;
  cube.rotation.y += 0.01;
  renderer.render(scene, camera);
}
animate();

// Responsive
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
});
2. ジオメトリ:形状の作成
// Built-in geometries
const box = new THREE.BoxGeometry(1, 1, 1);
const sphere = new THREE.SphereGeometry(0.5, 32, 32);
const plane = new THREE.PlaneGeometry(10, 10);

// Custom BufferGeometry
const geometry = new THREE.BufferGeometry();
const vertices = new Float32Array([
  -1, -1,  0,  // vertex 0
   1, -1,  0,  // vertex 1
   1,  1,  0,  // vertex 2
  -1,  1,  0   // vertex 3
]);
geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));

// Indices for triangles
const indices = new Uint16Array([0, 1, 2, 0, 2, 3]);
geometry.setIndex(new THREE.BufferAttribute(indices, 1));

// Instancing for many copies
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.updateMatrix();
  instancedMesh.setMatrixAt(i, dummy.matrix);
}
scene.add(instancedMesh);
3. マテリアル:PBR マテリアル
// Standard PBR material
const material = new THREE.MeshStandardMaterial({
  color: 0xffffff,
  metalness: 0.5,
  roughness: 0.5,
  map: colorTexture,
  normalMap: normalTexture,
  roughnessMap: roughnessTexture,
  metalnessMap: metalnessTexture,
  envMap: environmentMap,
  envMapIntensity: 1
});

// Physical material (advanced PBR)
const glassMaterial = new THREE.MeshPhysicalMaterial({
  color: 0xffffff,
  metalness: 0,
  roughness: 0,
  transmission: 1,     // Glass transparency
  thickness: 0.5,
  ior: 1.5,           // Index of refraction
  envMapIntensity: 1
});

// Shader material (custom)
const shaderMaterial = new THREE.ShaderMaterial({
  uniforms: {
    time: { value: 0 },
    color: { value: new THREE.Color(0xff0000) }
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform float time;
    uniform vec3 color;
    varying vec2 vUv;

    void main() {
      gl_FragColor = vec4(color * sin(vUv.x * 10.0 + time), 1.0);
    }
  `
});
4. ライティング:基本的なライティング
// Ambient light (uniform everywhere)
const ambient = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambient);

// Directional light (sun)
const dirLight = new THREE.DirectionalLight(0xffffff, 1);
dirLight.position.set(5, 10, 5);
dirLight.castShadow = true;

// Shadow configuration
dirLight.shadow.mapSize.width = 2048;
dirLight.shadow.mapSize.height = 2048;
dirLight.shadow.camera.left = -10;
dirLight.shadow.camera.right = 10;
dirLight.shadow.camera.top = 10;
dirLight.shadow.camera.bottom = -10;
scene.add(dirLight);

// Point light (bulb)
const pointLight = new THREE.PointLight(0xffffff, 1, 100);
pointLight.position.set(0, 5, 0);
scene.add(pointLight);

// Enable shadows on renderer
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

// Enable on objects
mesh.castShadow = true;
mesh.receiveShadow = true;
5. テクスチャ:テクスチャの読み込み
const loader = new THREE.TextureLoader();

// Load color texture
const colorTexture = loader.load('texture.jpg');
colorTexture.colorSpace = THREE.SRGBColorSpace; // Important for color accuracy

// Configure texture
colorTexture.wrapS = THREE.RepeatWrapping;
colorTexture.wrapT = THREE.RepeatWrapping;
colorTexture.repeat.set(4, 4);

// HDR environment map
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js';

const rgbeLoader = new RGBELoader();
rgbeLoader.load('environment.hdr', (texture) => {
  texture.mapping = THREE.EquirectangularReflectionMapping;
  scene.environment = texture;
  scene.background = texture;
});

// Cube texture (skybox)
const cubeLoader = new THREE.CubeTextureLoader();
const cubeTexture = cubeLoader.load([
  'px.jpg', 'nx.jpg',  // +X, -X
  'py.jpg', 'ny.jpg',  // +Y, -Y
  'pz.jpg', 'nz.jpg'   // +Z, -Z
]);
scene.background = cubeTexture;
6. アニメーション:シンプルなアニメーション
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const loader = new GLTFLoader();
loader.load('model.glb', (gltf) => {
  const model = gltf.scene;
  scene.add(model);

  // Create animation mixer
  const mixer = new THREE.AnimationMixer(model);

  // Play all animations
  gltf.animations.forEach((clip) => {
    const action = mixer.clipAction(clip);
    action.play();
  });

  // Update in animation loop
  const clock = new THREE.Clock();
  function animate() {
    requestAnimationFrame(animate);
    const delta = clock.getDelta();
    mixer.update(delta);
    renderer.render(scene, camera);
  }
  animate();
});

// Procedural animation
function animate() {
  const time = clock.getElapsedTime();
  mesh.rotation.y = time;
  mesh.position.y = Math.sin(time) * 0.5;
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
7. ローダー:GLTF モデルの読み込み
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';

// Setup Draco compression support
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath('https://www.gstatic.com/draco/versioned/decoders/1.5.6/');

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;
    }
  });

  // Center and scale
  const box = new THREE.Box3().setFromObject(model);
  const center = box.getCenter(new THREE.Vector3());
  model.position.sub(center);

  scene.add(model);
});

// Async/Promise pattern
async function loadModel(url) {
  return new Promise((resolve, reject) => {
    gltfLoader.load(url, resolve, undefined, reject);
  });
}

const gltf = await loadModel('model.glb');
scene.add(gltf.scene);
8. シェーダー:カスタムシェーダーマテリアル
const material = new THREE.ShaderMaterial({
  uniforms: {
    time: { value: 0 },
    amplitude: { value: 0.5 }
  },
  vertexShader: `
    uniform float time;
    uniform float amplitude;
    varying vec2 vUv;

    void main() {
      vUv = uv;
      vec3 pos = position;

      // Wave displacement
      pos.z += sin(pos.x * 5.0 + time) * amplitude;

      gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
    }
  `,
  fragmentShader: `
    uniform float time;
    varying vec2 vUv;

    void main() {
      vec3 color = vec3(vUv, 0.5 + 0.5 * sin(time));
      gl_FragColor = vec4(color, 1.0);
    }
  `
});

// Update in animation loop
function animate() {
  material.uniforms.time.value = clock.getElapsedTime();
  requestAnimationFrame(animate);
  renderer.render(scene, camera);
}
9. ポストプロセッシング:ブルームの追加
import { EffectComposer } from 'three/addons/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/addons/postprocessing/RenderPass.js';
import { UnrealBloomPass } from 'three/addons/postprocessing/UnrealBloomPass.js';

// Create composer
const composer = new EffectComposer(renderer);

// Render scene pass
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);

// Bloom pass
const bloomPass = new UnrealBloomPass(
  new THREE.Vector2(window.innerWidth, window.innerHeight),
  1.5,  // strength
  0.4,  // radius
  0.85  // threshold
);
composer.addPass(bloomPass);

// Use composer instead of renderer
function animate() {
  requestAnimationFrame(animate);
  composer.render(); // NOT renderer.render()
}

// Handle resize
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  composer.setSize(window.innerWidth, window.innerHeight);
});
10. インタラクション:レイキャスティング
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';

// Camera controls
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

// Raycasting setup
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();

function onMouseClick(event) {
  // Convert mouse to normalized coordinates
  mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
  mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

  // Raycast from camera
  raycaster.setFromCamera(mouse, camera);
  const intersects = raycaster.intersectObjects(scene.children, true);

  if (intersects.length > 0) {
    const object = intersects[0].object;
    console.log('Clicked:', object);
    console.log('Point:', intersects[0].point);

    // Highlight selected object
    object.material.emissive.set(0x444444);
  }
}

window.addEventListener('click', onMouseClick);

// Update controls in animation loop
function animate() {
  requestAnimationFrame(animate);
  controls.update(); // Required if enableDamping is true
  renderer.render(scene, camera);
}

よくあるパターン

適切な破棄
// Dispose geometries, materials, textures
geometry.dispose();
material.dispose();
texture.dispose();

// Remove from scene
scene.remove(mesh);

// Dispose renderer
renderer.dispose();
レスポンシブなレンダリング
window.addEventListener('resize', () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
});
パフォーマンスの最適化
  • 繰り返し使うオブジェクトにはインスタンシングを使用する(InstancedMesh)
  • フラスタムカリングを有効にする(デフォルトで有効)
  • 使用しないリソースを破棄する
  • 複雑なシーンでは適切な LOD(詳細度)を使用する
  • ジオメトリを統合してドローコールを最小限に抑える
  • アクティブなライトの数を制限する(ライトごとにシェーダーの複雑さが増します)
  • テクスチャアトラスを使ってテクスチャの切り替えを減らす

バージョン情報

Three.js のバージョン:r160+(2024年1月) インポート形式:ES6 モジュール(three、three/addons/*) 確認日:2024-01

関連項目

コメント