讨论

3D粒子效果工程师(WebGL转场系统)

来自 Wikiprompt,自由的提示词百科全书

loshu2000

2026年5月13日

3D粒子效果工程师(WebGL转场系统) 一个AI提示,要求其扮演3D粒子特效工程师,设计一套WebGL过渡系统,包含网格到粒子的变形、FBO、GPGPU和噪声湍流。

提示词内容收藏

🌐
我将作为3D粒子效果工程师,为您设计一个基于WebGL的网格到粒子过渡系统。以下是核心实现方案: ## 概念Shader逻辑 ### 顶点着色器(Particle Vertex Shader) ```glsl // 粒子顶点着色器 - 处理位置和大小 uniform float uTime; uniform float uProgress; // 0 = 原始网格,1 = 目标网格 uniform sampler2D uPositionTexture; // FBO位置纹理 uniform float uPixelRatio; attribute float aScale; attribute vec3 aColor; varying vec3 vColor; varying float vAlpha; void main() { // 从FBO纹理读取粒子位置 vec3 pos = texture2D(uPositionTexture, uv).xyz; // 根据进度混合原始位置和目标位置 vec3 originalPos = position; vec3 targetPos = pos; vec3 mixedPos = mix(originalPos, targetPos, uProgress); // 添加噪声扰动(在片段着色器中计算更精确) float noise = snoise(mixedPos * 0.5 + uTime * 0.3); mixedPos += normal * noise * 0.1 * (1.0 - uProgress); vec4 mvPosition = modelViewMatrix * vec4(mixedPos, 1.0); gl_Position = projectionMatrix * mvPosition; // 粒子大小随距离衰减 gl_PointSize = aScale * uPixelRatio * (300.0 / -mvPosition.z); vColor = aColor; vAlpha = smoothstep(0.0, 0.2, uProgress) * (1.0 - smoothstep(0.8, 1.0, uProgress)); } ``` ### 片段着色器(Particle Fragment Shader) ```glsl // 粒子片段着色器 - 处理颜色和透明度 uniform sampler2D uPositionTexture; uniform float uTime; uniform vec3 uCenter; varying vec3 vColor; varying float vAlpha; void main() { // 圆形粒子形状 vec2 center = gl_PointCoord - 0.5; float dist = length(center); if (dist > 0.5) discard; // 基于速度的颜色渐变 vec3 pos = texture2D(uPositionTexture, gl_FragCoord.xy / resolution).xyz; float speed = length(pos - previousPos); vec3 velocityColor = mix(vec3(0.2, 0.8, 1.0), vec3(1.0, 0.2, 0.8), speed * 2.0); // 基于距离中心的颜色 float distFromCenter = length(pos - uCenter); vec3 distanceColor = mix(vec3(0.1, 0.9, 0.5), vec3(0.9, 0.1, 0.5), distFromCenter * 0.5); vec3 finalColor = mix(vColor, velocityColor, 0.5) + distanceColor * 0.3; // 边缘柔化 float alpha = smoothstep(0.5, 0.1, dist) * vAlpha; gl_FragColor = vec4(finalColor, alpha); } ``` ### 计算着色器(GPGPU Position Update) ```glsl // 位置更新着色器 - 在FBO中执行 uniform sampler2D uTargetPosition; // 目标网格顶点位置 uniform sampler2D uCurrentPosition; // 当前粒子位置 uniform float uTime; uniform float uDeltaTime; uniform float uAttractionStrength; uniform float uRepulsionRadius; void main() { vec2 uv = gl_FragCoord.xy / resolution; vec3 currentPos = texture2D(uCurrentPosition, uv).xyz; vec3 targetPos = texture2D(uTargetPosition, uv).xyz; // 吸引力 vec3 toTarget = targetPos - currentPos; float dist = length(toTarget); vec3 attractionForce = normalize(toTarget) * uAttractionStrength * dist; // 排斥力(粒子间) vec3 repulsionForce = vec3(0.0); for (int i = 0; i < 8; i++) { vec2 offset = vec2(cos(float(i) * 0.785), sin(float(i) * 0.785)) * uRepulsionRadius; vec3 neighborPos = texture2D(uCurrentPosition, uv + offset).xyz; vec3 diff = currentPos - neighborPos; float neighborDist = length(diff); if (neighborDist < uRepulsionRadius) { repulsionForce += normalize(diff) * (1.0 - neighborDist / uRepulsionRadius); } } // 噪声湍流 float noise = snoise(currentPos * 0.8 + uTime * 0.5); vec3 noiseForce = vec3( noise * 0.5, snoise(currentPos.yzx * 0.8 + uTime * 0.5) * 0.5, snoise(currentPos.zxy * 0.8 + uTime * 0.5) * 0.5 ); // 速度更新 vec3 velocity = (attractionForce + repulsionForce + noiseForce) * uDeltaTime; // 位置更新 vec3 newPos = currentPos + velocity; gl_FragColor = vec4(newPos, 1.0); } ``` ## Three.js核心实现 ```javascript import * as THREE from 'three'; import { GPUComputationRenderer } from 'three/examples/jsm/misc/GPUComputationRenderer.js'; class ParticleTransitionSystem { constructor(scene, renderer) { this.scene = scene; this.renderer = renderer; // 初始化GPGPU this.gpuCompute = new GPUComputationRenderer(512, 512, renderer); // 粒子数量 this.particleCount = 100000; // 创建粒子系统 this.initParticles(); // 创建FBO计算 this.initCompute(); // 动画状态 this.progress = 0; this.transitioning = false; } initParticles() { // 创建粒子几何体 const geometry = new THREE.BufferGeometry(); // 位置属性(初始为原点) const positions = new Float32Array(this.particleCount * 3); const colors = new Float32Array(this.particleCount * 3); const scales = new Float32Array(this.particleCount); // 随机初始化粒子位置 for (let i = 0; i < this.particleCount; i++) { positions[i * 3] = (Math.random() - 0.5) * 10; positions[i * 3 + 1] = (Math.random() - 0.5) * 10; positions[i * 3 + 2] = (Math.random() - 0.5) * 10; colors[i * 3] = Math.random(); colors[i * 3 + 1] = Math.random(); colors[i * 3 + 2] = Math.random(); scales[i] = Math.random() * 2 + 0.5; } geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3)); geometry.setAttribute('aColor', new THREE.BufferAttribute(colors, 3)); geometry.setAttribute('aScale', new THREE.BufferAttribute(scales, 1)); // 创建粒子材质 const material = new THREE.ShaderMaterial({ uniforms: { uTime: { value: 0 }, uProgress: { value: 0 }, uPositionTexture: { value: null }, uPixelRatio: { value: this.renderer.getPixelRatio() } }, vertexShader: vertexShaderCode, fragmentShader: fragmentShaderCode, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending }); this.particles = new THREE.Points(geometry, material); this.scene.add(this.particles); } initCompute() { // 创建计算纹理 const positionTexture = this.gpuCompute.createTexture(); // 初始化粒子位置数据 const data = positionTexture.image.data; for (let i = 0; i < data.length; i += 4) { data[i] = (Math.random() - 0.5) * 10; // x data[i + 1] = (Math.random() - 0.5) * 10; // y data[i + 2] = (Math.random() - 0.5) * 10; // z data[i + 3] = 1.0; // w } // 创建目标位置纹理(从3D模型获取) this.targetTexture = this.createTargetTexture(); // 设置计算变量 this.positionVariable = this.gpuCompute.addVariable( 'uCurrentPosition', computeShaderCode, positionTexture ); this.gpuCompute.setVariableDependencies(this.positionVariable, [this.positionVariable]); // 创建计算材质 const error = this.gpuCompute.init(); if (error !== null) { console.error(error); } } createTargetTexture() { // 从3D模型提取顶点数据 const geometry = this.createTorusGeometry(); const vertices = geometry.attributes.position.array; // 创建纹理 const texture = this.gpuCompute.createTexture(); const data = texture.image.data; // 映射顶点数据到纹理 for (let i = 0; i < data.length; i += 4) { const vertexIndex = Math.floor(i / 4) * 3; if (vertexIndex < vertices.length) { data[i] = vertices[vertexIndex]; data[i + 1] = vertices[vertexIndex + 1]; data[i + 2] = vertices[vertexIndex + 2]; } else { // 超出顶点数量的粒子随机分布 data[i] = (Math.random() - 0.5) * 5; data[i + 1] = (Math.random() - 0.5) * 5; data[i + 2] = (Math.random() - 0.5) * 5; } data[i + 3] = 1.0; } return texture; } createTorusGeometry() { // 创建环形几何体 const geometry = new THREE.TorusGeometry(2, 0.8, 64, 128); // 提取顶点数据 const positions = geometry.attributes.position.array; // 创建目标粒子位置数组 const targetPositions = new Float32Array(this.particleCount * 3); // 将网格顶点映射到粒子 for (let i = 0; i < this.particleCount; i++) { const vertexIndex = i % (positions.length / 3); targetPositions[i * 3] = positions[vertexIndex * 3]; targetPositions[i * 3 + 1] = positions[vertexIndex * 3 + 1]; targetPositions[i * 3 + 2] = positions[vertexIndex * 3 + 2]; } // 更新粒子几何体的位置属性 this.particles.geometry.attributes.position.array = targetPositions; this.particles.geometry.attributes.position.needsUpdate = true; return geometry; } startTransition() { this.transitioning = true; this.progress = 0; } update(deltaTime) { // 更新进度 if (this.transitioning) { this.progress += deltaTime * 0.5; // 控制过渡速度 if (this.progress >= 1) { this.progress = 1; this.transitioning = false; } } // 更新GPGPU计算 this.gpuCompute.compute(); // 更新粒子材质uniforms this.particles.material.uniforms.uTime.value += deltaTime; this.particles.material.uniforms.uProgress.value = this.progress; this.particles.material.uniforms.uPositionTexture.value = this.gpuCompute.getCurrentRenderTarget(this.positionVariable).texture; } // 自定义GLTF模型加载 loadCustomModel(url) { const loader = new THREE.GLTFLoader(); loader.load(url, (gltf) => { const geometry = gltf.scene.children[0].geometry; this.updateTargetGeometry(geometry); }); } updateTargetGeometry(geometry) { // 更新目标位置纹理 const vertices = geometry.attributes.position.array; const data = this.targetTexture.image.data; for (let i = 0; i < data.length; i += 4) { const vertexIndex = Math.floor(i / 4) * 3; if (vertexIndex < vertices.length) { data[i] = vertices[vertexIndex]; data[i + 1] = vertices[vertexIndex + 1]; data[i + 2] = vertices[vertexIndex + 2]; } } this.targetTexture.needsUpdate = true; } } // 使用示例 const system = new ParticleTransitionSystem(scene, renderer); system.startTransition(); // 动画循环 function animate() { requestAnimationFrame(animate); const deltaTime = clock.getDelta(); system.update(deltaTime); renderer.render(scene, camera); } ``` ## 关键实现说明 ### 1. 顶点数据映射 - 使用`GPUComputationRenderer`创建512x512的FBO纹理 - 每个纹素存储一个粒子的位置(x, y, z, w) - 从3D模型几何体提取顶点数据,映射到纹理坐标 ### 2. 力场计算 - **吸引力**:粒子向目标锚点移动,强度与距离成正比 - **排斥力**:粒子间保持最小距离,防止重叠 - **噪声湍流**:使用3D Simplex噪声创建有机运动 ### 3. 动态颜色 - 基于粒子速度生成渐变颜色 - 基于距中心距离生成第二层颜色 - 两种颜色混合产生丰富视觉效果 ### 4. 性能优化 - 所有计算在GPU上执行,CPU零负担 - 使用FBO ping-pong技术避免数据竞争 - 粒子渲染使用Point Sprites减少顶点数 这个系统可以轻松扩展到任意3D模型之间的过渡,只需更新目标位置纹理即可。

登录以查看完整提示词

继续使用:

登录即表示你同意我们的 使用条款 和 隐私政策

用法

此提示词专为 coding 设计。复制上方内容并粘贴到你常用的 AI 工具中。

为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。

参考资料

分类:coding| prompts.chat| 3d-particle-effects| webgl

讨论