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 // 炎の広がり角度
};
// 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('ゆらぎ');
// フラグメントシェーダー
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);
}
各フレームごとに炎エフェクトを更新し、リアルタイムでダイナミックな動きを実現します。 主な更新内容は以下の通りです:
// 炎エフェクトの更新処理
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);
};
// 炎のマテリアルのパラメータ
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パラメータ設定 ...
// 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();
});
// 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;
// バーテックスシェーダー
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;
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);
};