ノート

WebGLトランジションシステム向け3Dパーティクルエフェクトエンジニア

フリーのプロンプト百科事典 Wikiprompt より

loshu2000

2026年5月13日

WebGLトランジションシステム向け3Dパーティクルエフェクトエンジニア AIが3Dパーティクルエフェクトエンジニアとして動作するためのプロンプト。メッシュからパーティクルへのモーフィング、FBO、GPGPU、ノイズ乱流を備えたWebGLトランジションシステムを設計する。

プロンプト内容保存

🌐
FBO(フレームバッファオブジェクト)を使用してパーティクル位置をGPU上で更新し、GPGPU技術で引力・斥力を計算します。3Dノイズ(Perlin/Simplex)による乱流場を加え、有機的な遷移を実現します。 **シェーダーロジック(概念)** 1. **パーティクル更新シェーダー(フラグメント)**: - 各テクセルが1パーティクルの位置・速度を保持 - ターゲットメッシュの頂点(アンカーポイント)との距離から引力を計算 - パーティクル間の斥力(近接ペア)をグリッドベースで近似 - 3Dノイズ関数(Simplex)で乱流ベクトルを加算 - 速度に応じて色グラデーション(速度→RGB)を出力 2. **レンダリングシェーダー(頂点)**: - FBOテクスチャから位置をサンプリング - パーティクルサイズを距離で減衰 - 色を速度ベースで動的に変更 **Three.js実装(コア部分)** ```javascript // シーン設定 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); document.body.appendChild(renderer.domElement); // パーティクル数 const PARTICLE_COUNT = 20000; const WIDTH = Math.ceil(Math.sqrt(PARTICLE_COUNT)); const HEIGHT = Math.ceil(PARTICLE_COUNT / WIDTH); // ターゲットメッシュ(トーラス)の頂点データを取得 const torusGeometry = new THREE.TorusGeometry(2, 0.8, 32, 64); const torusPositions = torusGeometry.attributes.position.array; const targetPositions = new Float32Array(PARTICLE_COUNT * 3); // 頂点データをパーティクル属性バッファにマッピング for (let i = 0; i < PARTICLE_COUNT; i++) { const idx = i % (torusPositions.length / 3); targetPositions[i * 3] = torusPositions[idx * 3]; targetPositions[i * 3 + 1] = torusPositions[idx * 3 + 1]; targetPositions[i * 3 + 2] = torusPositions[idx * 3 + 2]; } // FBO用のレンダーターゲット const rt1 = new THREE.WebGLRenderTarget(WIDTH, HEIGHT, { type: THREE.FloatType, minFilter: THREE.NearestFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat, depthBuffer: false }); const rt2 = rt1.clone(); // 初期位置(ランダム球状) const initialPositions = new Float32Array(PARTICLE_COUNT * 4); for (let i = 0; i < PARTICLE_COUNT; i++) { const theta = Math.random() * Math.PI * 2; const phi = Math.acos(2 * Math.random() - 1); const r = 5 + Math.random() * 2; initialPositions[i * 4] = r * Math.sin(phi) * Math.cos(theta); initialPositions[i * 4 + 1] = r * Math.sin(phi) * Math.sin(theta); initialPositions[i * 4 + 2] = r * Math.cos(phi); initialPositions[i * 4 + 3] = 0; // 速度x } // 更新シェーダー(GPGPU) const updateMaterial = new THREE.ShaderMaterial({ uniforms: { positions: { value: null }, targets: { value: new THREE.DataTexture(targetPositions, WIDTH, HEIGHT, THREE.RGBAFormat, THREE.FloatType) }, time: { value: 0 }, delta: { value: 0.016 }, noiseScale: { value: 0.5 }, forceStrength: { value: 0.8 }, repulsionRadius: { value: 0.3 } }, vertexShader: ` varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0); } `, fragmentShader: ` uniform sampler2D positions; uniform sampler2D targets; uniform float time; uniform float delta; uniform float noiseScale; uniform float forceStrength; uniform float repulsionRadius; varying vec2 vUv; // Simplex 3Dノイズ関数(簡略版) vec3 mod289(vec3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 mod289(vec4 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } vec4 permute(vec4 x) { return mod289(((x * 34.0) + 1.0) * x); } vec4 taylorInvSqrt(vec4 r) { return 1.79284291400159 - 0.85373472095314 * r; } float snoise(vec3 v) { const vec2 C = vec2(1.0 / 6.0, 1.0 / 3.0); const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); vec3 i = floor(v + dot(v, C.yyy)); vec3 x0 = v - i + dot(i, C.xxx); vec3 g = step(x0.yzx, x0.xyz); vec3 l = 1.0 - g; vec3 i1 = min(g.xyz, l.zxy); vec3 i2 = max(g.xyz, l.zxy); vec3 x1 = x0 - i1 + C.xxx; vec3 x2 = x0 - i2 + C.yyy; vec3 x3 = x0 - D.yyy; i = mod289(i); vec4 p = permute(permute(permute( i.z + vec4(0.0, i1.z, i2.z, 1.0)) + i.y + vec4(0.0, i1.y, i2.y, 1.0)) + i.x + vec4(0.0, i1.x, i2.x, 1.0)); float n_ = 0.142857142857; vec3 ns = n_ * D.wyz - D.xzx; vec4 j = p - 49.0 * floor(p * ns.z * ns.z); vec4 x_ = floor(j * ns.z); vec4 y_ = floor(j - 7.0 * x_); vec4 x = x_ * ns.x + ns.yyyy; vec4 y = y_ * ns.x + ns.yyyy; vec4 h = 1.0 - abs(x) - abs(y); vec4 b0 = vec4(x.xy, y.xy); vec4 b1 = vec4(x.zw, y.zw); vec4 s0 = floor(b0) * 2.0 + 1.0; vec4 s1 = floor(b1) * 2.0 + 1.0; vec4 sh = -step(h, vec4(0.0)); vec4 a0 = b0.xzyw + s0.xzyw * sh.xxyy; vec4 a1 = b1.xzyw + s1.xzyw * sh.zzww; vec3 p0 = vec3(a0.xy, h.x); vec3 p1 = vec3(a0.zw, h.y); vec3 p2 = vec3(a1.xy, h.z); vec3 p3 = vec3(a1.zw, h.w); vec4 norm = taylorInvSqrt(vec4(dot(p0, p0), dot(p1, p1), dot(p2, p2), dot(p3, p3))); p0 *= norm.x; p1 *= norm.y; p2 *= norm.z; p3 *= norm.w; vec4 m = max(0.6 - vec4(dot(x0, x0), dot(x1, x1), dot(x2, x2), dot(x3, x3)), 0.0); m = m * m; return 42.0 * dot(m * m, vec4(dot(p0, x0), dot(p1, x1), dot(p2, x2), dot(p3, x3))); } void main() { vec4 posData = texture2D(positions, vUv); vec3 pos = posData.xyz; vec3 vel = vec3(posData.w, 0.0, 0.0); // 速度はx成分のみ簡略化 vec4 targetData = texture2D(targets, vUv); vec3 target = targetData.xyz; // 引力(ターゲットへ) vec3 toTarget = target - pos; float distToTarget = length(toTarget); vec3 force = normalize(toTarget) * forceStrength * (1.0 - exp(-distToTarget * 0.5)); // 3Dノイズ乱流 vec3 noiseForce = vec3( snoise(pos * noiseScale + time * 0.5), snoise(pos * noiseScale + time * 0.5 + 100.0), snoise(pos * noiseScale + time * 0.5 + 200.0) ) * 0.5; // 斥力(近接パーティクル間 - 簡略化のためグリッド近似) // 実際には近傍探索が必要だが、ここではノイズで近似 vec3 repulsion = -normalize(pos) * 0.1 * (1.0 / (1.0 + distToTarget * 0.1)); // 速度更新 vel += (force + noiseForce + repulsion) * delta; vel *= 0.98; // 減衰 // 位置更新 pos += vel * delta; // 速度に基づく色(速度→RGB) float speed = length(vel); vec3 color = vec3( clamp(speed * 2.0, 0.0, 1.0), clamp(1.0 - speed * 1.5, 0.0, 1.0), clamp(speed * 0.5 + 0.3, 0.0, 1.0) ); gl_FragColor = vec4(pos, speed); } ` }); // レンダリング用マテリアル const renderMaterial = new THREE.ShaderMaterial({ uniforms: { positions: { value: null }, pointSize: { value: 3.0 }, time: { value: 0 } }, vertexShader: ` uniform sampler2D positions; uniform float pointSize; uniform float time; varying vec3 vColor; void main() { vec4 posData = texture2D(positions, uv); vec3 pos = posData.xyz; float speed = posData.w; // 速度に基づく色グラデーション vColor = vec3( clamp(speed * 2.0, 0.0, 1.0), clamp(1.0 - speed * 1.5, 0.0, 1.0), clamp(speed * 0.5 + 0.3, 0.0, 1.0) ); vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0); gl_PointSize = pointSize * (300.0 / -mvPosition.z); gl_Position = projectionMatrix * mvPosition; } `, fragmentShader: ` varying vec3 vColor; void main() { vec2 center = gl_PointCoord - 0.5; float dist = length(center); if (dist > 0.5) discard; float alpha = 1.0 - smoothstep(0.0, 0.5, dist); gl_FragColor = vec4(vColor, alpha); } `, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending }); // パーティクルジオメトリ const geometry = new THREE.BufferGeometry(); const uvArray = new Float32Array(PARTICLE_COUNT * 2); for (let i = 0; i < PARTICLE_COUNT; i++) { uvArray[i * 2] = (i % WIDTH) / WIDTH; uvArray[i * 2 + 1] = Math.floor(i / WIDTH) / HEIGHT; } geometry.setAttribute('uv', new THREE.BufferAttribute(uvArray, 2)); // 初期位置テクスチャ const initialTexture = new THREE.DataTexture(initialPositions, WIDTH, HEIGHT, THREE.RGBAFormat, THREE.FloatType); initialTexture.needsUpdate = true; // パーティクルシステム const particles = new THREE.Points(geometry, renderMaterial); scene.add(particles); // アニメーションループ let currentRT = rt1; let nextRT = rt2; function animate() { requestAnimationFrame(animate); // 更新パス updateMaterial.uniforms.positions.value = currentRT.texture; updateMaterial.uniforms.time.value = performance.now() * 0.001; const updateScene = new THREE.Scene(); const updateCamera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1); const quad = new THREE.Mesh(new THREE.PlaneGeometry(2, 2), updateMaterial); updateScene.add(quad); renderer.setRenderTarget(nextRT); renderer.render(updateScene, updateCamera); renderer.setRenderTarget(null); // レンダリングパス renderMaterial.uniforms.positions.value = nextRT.texture; renderMaterial.uniforms.time.value = performance.now() * 0.001; renderer.render(scene, camera); // バッファ交換 const temp = currentRT; currentRT = nextRT; nextRT = temp; } animate(); ``` **頂点データからパーティクル属性バッファへのマッピング説明**: 1. 3Dモデル(トーラスやGLTF)の頂点位置を`Float32Array`に抽出 2. パーティクル数に合わせてインデックスを循環させ、各パーティクルにターゲット位置を割り当て 3. このデータを`DataTexture`(RGBA、FloatType)としてGPUに転送 4. 更新シェーダーでテクスチャ座標(UV)からターゲット位置をサンプリング **遷移制御**: `forceStrength`を時間とともに変化させ、初期はノイズ支配(乱流)、後半は引力支配(収束)にすることで、メッシュ溶解→再構成の効果を実現します。

ログインして完全なプロンプトを表示

次で続行:

ログインすると、次に同意したことになります: 利用規約 と プライバシーポリシー

使い方

このプロンプトは coding 向けに設計されています。上の内容をコピーして、お好みの AI ツールに貼り付けてください。

最良の結果を得るには、プレースホルダー(角括弧や大文字で示された部分)を具体的な要件に置き換えてください。

参考資料

カテゴリ:coding| prompts.chat| 3d-particle-effects| webgl

ノート