🌿
🌿 MidoriPhotoArt.
日本の伝統的な建築装飾である「組子」の模様を、3Dグラフィックス技術を用いてデジタル空間に再現する試みです。具体的には、ウェブ開発で広く利用されているJavaScriptのライブラリ「three.js」を活用しています。このライブラリは、3Dモデルの作成、レンダリング、アニメーション化を容易にする機能を提供し、開発者が複雑な3Dグラフィックスをウェブブラウザ上で実現するのを支援します。このプロジェクトでは、three.jsを用いて、様々な組子の模様を3D空間内に生成し、表示しています。ユーザーは、マウスやタッチ操作を通じて、これらの模様をあらゆる角度から眺めたり、拡大・縮小したりすることができ、組子の持つ繊細な美しさや複雑な構造をインタラクティブに体験することが可能です。この試作品は、伝統工芸と最新技術の融合により、新しい表現の可能性を探求するものであり、将来的には、教育、デザイン、エンターテインメントなど、多様な分野での応用が期待されます。

Three.jsによる炎エフェクトの実装解説

1. パラメータ設定

炎エフェクトの挙動を制御する主要なパラメータについて説明します。以下のパラメータを適切に組み合わせることで、リアルな炎の表現を実現しています。

const fireParams = {
    speed: 1.5,        // 炎の動きの速度
    intensity: 0.3,    // 炎の明るさ
    scale: 2.5,        // 炎の全体的なサイズ
    density: 1.2,      // 炎の密度
    turbulence: 1.4,   // 炎のゆらぎの強さ
    flameHeight: 1.3,  // 炎の高さ
    colorIntensity: 1.5, // 炎の色の鮮やかさ
    volumetricLayers: 48, // 炎の3D表現のためのレイヤー数
    coneAngle: 25      // 炎の広がり角度
};
scale: 2.5 flameHeight: 1.3 coneAngle: 25°

パラメータの相互作用

基本パラメータ ・speed ・turbulence 形状パラメータ ・scale ・flameHeight 視覚効果 ・intensity ・colorIntensity
  • 基本パラメータ
    • speed × turbulence → 炎のゆらぎの動きを決定
    • intensity × colorIntensity → 炎の視覚的な強さを制御
  • 形状パラメータ
    • scale + flameHeight → 炎の全体的な大きさを決定
    • density + volumetricLayers → 炎の立体感を表現

2. GUIコントロールの実装

dat.GUIを使用して、ユーザーが各パラメータをリアルタイムで調整できるインターフェースを実装しています。 GUIパネルはドラッグ可能で、画面上の任意の位置に移動できます。

// GUIコントロールの初期化
const gui = new dat.GUI();
gui.domElement.style.opacity = "0.8";

// パラメータの追加
gui.add(fireParams, 'speed', 0, 3).name('炎の速度');
gui.add(fireParams, 'intensity', 0, 1).name('明るさ');
gui.add(fireParams, 'scale', 0.5, 5).name('サイズ');
gui.add(fireParams, 'density', 0.5, 2).name('密度');
gui.add(fireParams, 'turbulence', 0, 3).name('ゆらぎ');
                    
dat.GUI初期化 パラメータコントロール追加 リアルタイム更新処理

実装のポイント

  • 各パラメータにスライダーを実装:min/max値の適切な設定
  • 日本語のラベル表示:name()メソッドで設定
  • ドラッグ機能の実装:CSSでdraggable設定
  • 透明度を持つ背景色の設定:opacity = 0.8

3. シェーダーによる炎の表現

複数の2D平面を円錐状に配置し、シェーダーを使用して炎のような見た目を実現しています。 以下のコードとフローチャートで実装方法を説明します。

// フラグメントシェーダー
uniform float time;
uniform float intensity;
uniform float turbulence;

varying vec2 vUv;

void main() {
    // ノイズ関数による揺らぎの生成
    float noise = snoise(vec3(vUv * 4.0, time));
    
    // グラデーションの計算
    vec3 color = mix(
        vec3(1.0, 0.5, 0.0),  // オレンジ
        vec3(1.0, 0.8, 0.0),  // 黄色
        noise * turbulence
    );
    
    // 透明度の計算
    float alpha = (1.0 - vUv.y) * intensity;
    
    gl_FragColor = vec4(color, alpha);
}
                    
シェーダー初期化 ノイズ生成 レイヤー配置 炎エフェクトの合成

実装の主なポイント

  • ノイズ関数(Simplex Noise)を使用して自然な揺らぎを表現
  • オレンジから黄色へのグラデーションで炎らしい色彩を実現
  • 透明度をy座標に応じて変化させ、上部ほど薄くなるように制御
  • 複数のレイヤーを重ねることで立体的な炎の表現を実現
  • uniformパラメータでリアルタイムに炎の様子を調整可能

4. アニメーションの更新処理

各フレームごとに炎エフェクトを更新し、リアルタイムでダイナミックな動きを実現します。 主な更新内容は以下の通りです:

  • 時間パラメータの更新による炎の揺らぎ制御
  • 速度・強度などの動的パラメータの反映
  • 各レイヤーの空間的な配置の計算
  • 全体的なスケールの調整
フレーム開始 時間パラメータ更新 レイヤー位置計算 スケール適用

// 炎エフェクトの更新処理
const updateFire = () => {
    // 円錐の半径を計算(角度に基づく)
    const radius = Math.sin(fireParams.coneAngle * Math.PI / 180) * 0.5;
    
    // 各レイヤーの更新処理
    layers.forEach((layer, index) => {
        // 時間経過による動的な変化
        layer.material.uniforms.time.value += 0.016;  // 約60FPSに相当
        
        // エフェクトパラメータの更新
        layer.material.uniforms.speed.value = fireParams.speed;      // 炎の速度
        layer.material.uniforms.intensity.value = fireParams.intensity;  // 炎の強度
        // ... その他のパラメータ更新
        
        // 円周上にレイヤーを均等配置
        const angle = (index / (fireParams.volumetricLayers - 1)) * Math.PI * 2;
        layer.position.x = Math.cos(angle) * radius;  // X座標
        layer.position.z = Math.sin(angle) * radius;  // Z座標
        layer.rotation.y = angle;  // レイヤーの向き
    });
    
    // 全体的なスケールの適用
    fireGroup.scale.set(fireParams.scale, fireParams.scale, fireParams.scale);
};
                    

実装のポイント解説

  • 時間値を少しずつ増加させることで、連続的な炎の動きを実現
  • 円錐状の配置により、立体的な炎の表現を実現
  • 各レイヤーを円周上に均等配置することで、自然な体積感を表現
  • スケールパラメータにより、炎の大きさを動的に調整可能

3. 炎エフェクトの完全なソースコード

以下は、Three.jsを使用した炎エフェクトの完全な実装コードです。コードの理解を深めるため、主要な部分ごとに分けて解説します。

3.1 パラメータとGUIの設定


// 炎のマテリアルのパラメータ
const fireParams = {
    speed: 1.5,         // より自然な動きのスピード
    intensity: 0.3,     // より明るい炎の強度
    scale: 2.5,         // より大きなサイズ
    density: 1.2,       // より密度の高い炎
    turbulence: 1.4,    // より自然な乱流
    flameHeight: 1.3,   // より高い炎
    colorIntensity: 1.5, // より鮮やかな色
    volumetricLayers: 48, // より滑らかな見た目のレイヤー数
    coneAngle: 25       // より自然な広がり角度
};

// GUIコントロールの設定
const gui = new GUI({ autoPlace: false });
const fireFolder = gui.addFolder('炎のパラメータ');
fireFolder.add(fireParams, 'speed', 0.1, 3.0).name('速度');
fireFolder.add(fireParams, 'intensity', 0.1, 3.0).name('強度');
// ... その他のGUIパラメータ設定 ...

3.2 ドラッグ可能なGUIコントロール実装


// GUIコンテナの作成と配置
const guiContainer = document.createElement('div');
guiContainer.style.position = 'absolute';
guiContainer.style.right = '10px';
guiContainer.style.top = '50%';
// ... スタイル設定 ...

// ドラッグ機能の実装
let isDragging = false;
let currentX, currentY, initialX, initialY;

dragHandle.addEventListener('mousedown', (e) => {
    isDragging = true;
    initialX = e.clientX - guiContainer.offsetLeft;
    initialY = e.clientY - guiContainer.offsetTop;
    e.preventDefault();
});

3.3 炎のジオメトリとマテリアル生成


// 3D炎エフェクトの作成
const fireGroup = new THREE.Group();
const layers = [];

for(let i = 0; i < fireParams.volumetricLayers; i++) {
    const segments = 64;
    const planeGeometry = new THREE.PlaneGeometry(1, 4, segments, 128);
    
    // 台形状の変形処理
    const positions = planeGeometry.attributes.position.array;
    for(let j = 0; j < positions.length; j += 3) {
        const y = positions[j + 1];
        const widthScale = 1.0 + (y / 4.0) * 0.5;
        positions[j] *= widthScale;
    }
    planeGeometry.attributes.position.needsUpdate = true;

3.4 シェーダーコード


// バーテックスシェーダー
varying vec2 vUv;
varying vec3 vPosition;
void main() {
    vUv = uv;
    vPosition = position;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

// フラグメントシェーダー
uniform float time;
uniform float speed;
uniform float intensity;
// ... その他のユニフォーム変数 ...

void main() {
    vec2 uv = vUv;
    float n = 0.0;
    float amplitude = 1.0;
    float frequency = 1.0;
    
    // ノイズ生成処理
    for(int i = 0; i < 4; i++) {
        vec2 noisePos = uv * noiseScale * frequency * density + 
            vec2(sin(time * speed * 0.1 + layerOffset * 6.28) * turbulence, 
                 -time * speed * (0.5 + float(i) * 0.1));
        n += noise(noisePos) * amplitude;
        amplitude *= 0.5;
        frequency *= 2.0;
    }

    // 炎の形状と色の計算
    float flame = smoothstep(0.1, 0.5, uv.y) * 
        (1.0 - pow(uv.y, flameHeight)) * 2.0;
    flame *= n * intensity;

3.5 アニメーション更新処理


const updateFire = () => {
    const radius = Math.sin(fireParams.coneAngle * Math.PI / 180) * 0.5;
    
    layers.forEach((layer, index) => {
        // 時間更新
        layer.material.uniforms.time.value += 0.016;
        
        // パラメータ更新
        layer.material.uniforms.speed.value = fireParams.speed;
        layer.material.uniforms.intensity.value = fireParams.intensity;
        // ... その他のパラメータ更新 ...
        
        // 位置更新
        const angle = (index / (fireParams.volumetricLayers - 1)) * Math.PI * 2;
        layer.position.x = Math.cos(angle) * radius;
        layer.position.z = Math.sin(angle) * radius;
        layer.rotation.y = angle;
    });
    
    fireGroup.scale.set(fireParams.scale, fireParams.scale, fireParams.scale);
};

実装のポイント

  • コードを機能ごとに分割し、保守性と可読性を向上
  • 各セクションに適切なコメントを追加し、理解を促進
  • シェーダーコードを独立したセクションとして表示
  • 重要なパラメータと更新ロジックを明確に区分