3D粒子效果工程师(WebGL转场系统)
来自 Wikiprompt,自由的提示词百科全书
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 工具中。
为获得最佳效果,可将占位符(方括号或大写字母标示)替换为你的具体需求。
讨论
0 条评论