最佳实践 ~ThreeJS制作一个炫酷的烟花中秋节专场

社区

引言

在现代网络应用中,炫酷的视觉效果可以极大地提升用户体验和界面的吸引力。在这篇文章中,我们将探讨如何使用 Three.js 库创建一个具有动态烟花效果的三维文字展示场景。我们的目标是创建一个具有渐变颜色效果、动感十足的文字展示效果,同时配合生动的烟花爆炸动画,为用户呈现一个令人惊叹的视觉体验。

值此中秋佳节来临之际,提前预祝火山引擎开发者社区的小伙伴们儿,节日快乐,满满收获。

picture.image

场景设置与初始化

首先,我们需要为我们的场景设置基本的 Three.js 环境。我们创建一个 THREE.Scene 实例,并设置背景颜色为深蓝色,以模拟夜空的效果。

let scene, camera, renderer, rockets = [], fireworks = [];

function init() {
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0x002244); // 深蓝色背景
}

然后,我们设置相机 THREE.PerspectiveCamera,这是一个透视相机,可以给用户提供一个真实的三维感。接着,我们初始化渲染器 THREE.WebGLRenderer 并将其添加到网页中:

camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 50;

renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

添加光源

为了增强场景的立体感,我们添加了环境光和点光源。环境光提供均匀的照明,而点光源模拟了场景中的实际光源。

const ambientLight = new THREE.AmbientLight(0xffffff, 0.8);
scene.add(ambientLight);

const pointLight = new THREE.PointLight(0xffffff, 1.5, 100);
pointLight.position.set(10, 10, 10);
scene.add(pointLight);

加载字体并创建三维文字

在我们的场景中,我们需要加载自定义字体并用它创建动态的文字对象。我们使用 FontLoader 从本地 JSON 文件加载字体,并利用 TextGeometry 创建三维文字几何体。

const loader = new FontLoader();
loader.load('./json/STKaiti_Regular.json', function(font) {
    let xOffset = -20; // 起始位置

    const textData = [
        { text: '中秋快乐', color1: 0xff0000, color2: 0xffff00 },
        { text: '火山引擎', color1: 0x00ff00, color2: 0x0000ff },
        { text: '开发者社区', color1: 0x00ffff, color2: 0xff00ff }
    ];

    textData.forEach(data => {
        createRocket(font, data.text, data.color1, data.color2, xOffset);
        xOffset += 15; // 每段文字之间的间距
    });
});

创建渐变色字体

为字体添加渐变色可以使其更加引人注目。我们使用 THREE.ShaderMaterial 来实现这一效果。我们定义了顶点着色器和片元着色器,利用 UV 坐标在字体上实现颜色渐变。

function createRocket(font, text, color1, color2, xOffset) {
    const textGeometry = new TextGeometry(text, {
        font: font,
        size: 5,
        height: 2,
        curveSegments: 12,
        bevelEnabled: true,
        bevelThickness: 0.5,
        bevelSize: 0.3,
        bevelSegments: 5
    });

    const material = new THREE.ShaderMaterial({
        vertexShader: `
            varying vec2 vUv;
            void main() {
                vUv = uv;
                gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
            }
        `,
        fragmentShader: `
            uniform vec3 color1;
            uniform vec3 color2;
            varying vec2 vUv;
            void main() {
                vec3 color = mix(color1, color2, vUv.y);
                gl_FragColor = vec4(color, 1.0);
            }
        `,
        uniforms: {
            color1: { value: new THREE.Color(color1) },
            color2: { value: new THREE.Color(color2) }
        },
        side: THREE.FrontSide,
        transparent: true
    });

    const rocket = new THREE.Mesh(textGeometry, material);
    rocket.position.set(xOffset, -20, 0);
    scene.add(rocket);

    rockets.push({
        mesh: rocket,
        velocityY: 0.3 + Math.random() * 0.2,
        targetHeight: 15 + Math.random() * 15,
        exploded: false,
        delay: Math.random() * 2
    });
}

创建烟花效果

在文字升空并达到目标高度后,我们将触发烟花效果。我们利用 THREE.BufferGeometryTHREE.PointsMaterial 创建粒子系统,模拟烟花的爆炸效果。粒子系统通过随机位置和速度模拟烟花的飞散效果。

function createFirework(x, y) {
    const particleCount = 200;
    const particles = new THREE.BufferGeometry();
    const positions = new Float32Array(particleCount * 3);
    const velocities = new Float32Array(particleCount * 3);
    const colors = new Float32Array(particleCount * 3);

    for (let i = 0; i < particleCount; i++) {
        const theta = Math.random() * 2 * Math.PI;
        const phi = Math.acos((Math.random() * 2) - 1);
        const distance = Math.random() * 20;

        positions[i * 3] = distance * Math.sin(phi) * Math.cos(theta);
        positions[i * 3 + 1] = distance * Math.sin(phi) * Math.sin(theta);
        positions[i * 3 + 2] = distance * Math.cos(phi);

        velocities[i * 3] = positions[i * 3] * 0.1;
        velocities[i * 3 + 1] = positions[i * 3 + 1] * 0.1;
        velocities[i * 3 + 2] = positions[i * 3 + 2] * 0.1;

        colors[i * 3] = Math.random();
        colors[i * 3 + 1] = Math.random();
        colors[i * 3 + 2] = Math.random();
    }

    particles.setAttribute('position', new THREE.BufferAttribute(positions, 3));
    particles.setAttribute('velocity', new THREE.BufferAttribute(velocities, 3));
    particles.setAttribute('color', new THREE.BufferAttribute(colors, 3));

    const material = new THREE.PointsMaterial({
        size: 1.0,
        vertexColors: true,
        transparent: true,
        opacity: 1
    });

    const particleSystem = new THREE.Points(particles, material);
    particleSystem.position.set(x, y, Math.random() * 10 - 5);
    scene.add(particleSystem);

    fireworks.push(particleSystem);
}

动画循环与更新

在动画循环中,我们处理文字的上升动画和烟花效果的更新。文字在上升到目标高度后触发烟花效果,并在动画中逐渐消失。烟花的粒子在每一帧中更新位置,并模拟重力和空气阻力的效果。

function animate() {
    requestAnimationFrame(animate);

    rockets.forEach((rocket, index) => {
        if (!rocket.exploded) {
            if (performance.now() / 1000 > rocket.delay) {
                rocket.mesh.position.y += rocket.velocityY;
                rocket.mesh.rotation.y += 0.01;
                rocket.mesh.scale.set(1.05, 1.05, 1.05);

                if (rocket.mesh.position.y >= rocket.targetHeight) {
                    createFirework(rocket.mesh.position.x, rocket.mesh.position.y);
                    scene.remove(rocket.mesh);
                    rocket.exploded = true;
                }
            }
        }
    });

    fireworks.forEach(firework => {
        const positions = firework.geometry.attributes.position.array;
        const velocities = firework.geometry.attributes.velocity.array;

        for (let i = 0; i < positions.length; i += 3) {
            positions[i] += velocities[i];
            positions[i + 1] += velocities[i + 1];
            positions[i + 2] += velocities[i + 2];

            velocities[i + 1] -= 0.005;
            velocities[i] *= 0.98;
            velocities[i + 1] *= 0.98;
            velocities[i + 2] *= 0.98;
        }

        firework.geometry.attributes.position.needsUpdate = true;
        firework.material.opacity -= 0.01;
        if (firework.material.opacity <= 0) {
            scene.remove(firework);
            fireworks = fireworks.filter(fw => fw !== firework);
        }
    });

    renderer.render(scene, camera);
}

窗口大小调整

为了确保场景在窗口大小调整时能够正常显示,我们需要更新相机的纵横比和渲染器的大小。

window.addEventListener('resize', onWindowResize);

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}

总结

通过上述代码,我们创建了一个炫酷的三维文字展示场景,其中包含动态的渐变色字体和生动的烟花效果。这个项目不仅展示了 Three.js 在创建复杂视觉效果方面的强大功能,也展示了如何利用自定义着色器和粒子系统来实现细致的动画效果。希望这篇文章能为你提供灵感,帮助你在自己的项目中创建令人惊叹的视觉效果。

0
0
0
0
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论