呦呦鹿鸣,食野之苹。我有嘉宾,鼓瑟吹笙。-滋你

72 阅读1分钟

墨烟

let pointCloud: any;
const PARTICLE_COUNT = 2000;
const geometry = new THREE.BufferGeometry();

// 初始化三维粒子属性
const positions = new Float32Array(PARTICLE_COUNT * 3);
const directions = new Float32Array(PARTICLE_COUNT * 3); // 三维方向向量
const lifeParams = new Float32Array(PARTICLE_COUNT * 2); // [生命周期偏移, 速度]
for (let i = 0; i < PARTICLE_COUNT; i++) {
    // 初始位置在原点
    positions[i * 3] = 0;
    positions[i * 3 + 1] = 0;
    positions[i * 3 + 2] = 0;

    // 生成三维随机方向(球面均匀分布)
    const theta = Math.random() * Math.PI * 2;   // 方位角
    const phi = Math.acos(2 * Math.random() - 1); // 极角
    const r = Math.random() * 0.5;

    directions[i * 3] = r * Math.sin(phi) * Math.cos(theta); // x
    directions[i * 3 + 1] = r * Math.sin(phi) * Math.sin(theta); // y
    directions[i * 3 + 2] = r * Math.cos(phi); // z

    lifeParams[i * 2] = Math.random();    // 生命周期偏移
    lifeParams[i * 2 + 1] = Math.random() * 0.3 + 0.5; // 速度
}


geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('aDirection', new THREE.BufferAttribute(directions, 3));
geometry.setAttribute('aLifeParams', new THREE.BufferAttribute(lifeParams, 2));
    
   
const texture = new THREE.TextureLoader().load('https://threejs.org/examples/textures/sprites/disc.png');

// 三维喷射Shader材质

const material = new THREE.ShaderMaterial({
    uniforms: {
        uTime: { value: 0 },
        uTexture: { value: texture },
        uGravity: { value: -0.8 },     // 重力强度
        uTurbulence: { value: 0.15 }   // 湍流强度
    },

    vertexShader: `
        uniform float uTime;
        uniform float uGravity;
        uniform float uTurbulence;
        attribute vec3 aDirection;
        attribute vec2 aLifeParams;

        varying float vLife;
        varying vec3 vPosition;

        void main() {
            // 生命周期计算
            float lifeOffset = aLifeParams.x;
            float speed = aLifeParams.y;
            float life = mod(uTime * speed + lifeOffset, 1.0);
            vLife = life;

            // 基础运动
            vec3 pos = position;

            // 主喷射方向(Y轴)叠加随机三维方向
            vec3 moveVec = vec3(
                aDirection.x * uTurbulence,
                1.0 + aDirection.y * 0.3,
                aDirection.z * uTurbulence
            );

            // 运动轨迹计算
            pos += moveVec * life * 5.0; // 基础运动
            pos.y += uGravity * pow(life, 2.0); // 重力影响

            // 湍流效果
            pos.x += sin(uTime * 10.0 + lifeOffset * 10.0) * 0.1 * life;
            pos.z += cos(uTime * 8.0 + lifeOffset * 10.0) * 0.1 * life;

            // 保存位置供片段着色器使用
            vPosition = pos;

            // 投影计算
            vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0);
            gl_PointSize = 8.0 * (1.0 - life) * (1.0 / length(mvPosition.xyz));//透视校正大小
            gl_Position = projectionMatrix * mvPosition;
        }
    `,

    fragmentShader: `

        uniform sampler2D uTexture;
        varying float vLife;
        varying vec3 vPosition;


        void main() {

            // 颜色渐变(从蓝色到透明)
            vec3 baseColor = mix(
                vec3(0.3, 0.5, 1.0),
                vec3(0.8, 0.9, 1.0),
                smoothstep(0.0, 0.5, vLife)
            );


            // 贴图混合
            vec4 texColor = texture2D(uTexture, gl_PointCoord);
            float alpha = texColor.a * (1.0 - smoothstep(0.7, 1.0, vLife));

            // 深度衰减效果
            float depthFactor = 1.0 - smoothstep(0.0, 5.0, abs(vPosition.z));
            alpha *= depthFactor;

            gl_FragColor = vec4(baseColor, alpha);
        }

    `,

    transparent: true,
    depthWrite: false,
    blending: THREE.AdditiveBlending
});


pointCloud = new THREE.Points(geometry, material);
pointCloud.rotateX(Math.PI / 2);
pointCloud.position.setY(0.3)
pointCloud.scale.set(50, 50, 50)
scene.add(pointCloud);

function animate() {
    requestAnimationFrame(animate);
    pointCloud.material.uniforms.uTime.value = performance.now() / 1000;
}
animate()