WebGLトランジションシステム向け3Dパーティクルエフェクトエンジニア
フリーのプロンプト百科事典 Wikiprompt より
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 ツールに貼り付けてください。
最良の結果を得るには、プレースホルダー(角括弧や大文字で示された部分)を具体的な要件に置き換えてください。
ノート
0 件のコメント