Three.js 发光路径效果实现指南

0 阅读3分钟

一、概述

在 Three.js 中创建发光路径效果可以增强 3D 场景的视觉吸引力,这种效果常用于引导用户视线、展示运动轨迹或创建科幻风格的视觉效果。本文将详细介绍如何实现这一效果。

二、准备工作

首先需要创建一个基本的 Three.js 环境。以下是初始化代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js发光路径效果</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/three@0.160.0/build/three.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/three@0.160.0/examples/js/controls/OrbitControls.js"></script>
</head>
<body class="bg-gray-900 text-white m-0 p-0 overflow-hidden">
    <div id="canvas-container" class="w-full h-screen"></div>
    
    <script>
        // 初始化场景、相机和渲染器
        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.getElementById('canvas-container').appendChild(renderer.domElement);
        
        // 添加轨道控制器
        const controls = new THREE.OrbitControls(camera, renderer.domElement);
        
        // 设置相机位置
        camera.position.z = 5;
        
        // 添加环境光
        const ambientLight = new THREE.AmbientLight(0x404040);
        scene.add(ambientLight);
        
        // 添加方向光
        const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
        directionalLight.position.set(1, 1, 1);
        scene.add(directionalLight);
        
        // 动画循环
        function animate() {
            requestAnimationFrame(animate);
            controls.update();
            renderer.render(scene, camera);
        }
        
        animate();
        
        // 响应窗口大小变化
        window.addEventListener('resize', () => {
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
            renderer.setSize(window.innerWidth, window.innerHeight);
        });
    </script>
</body>
</html>

三、创建基础路径

我们可以使用 THREE.CatmullRomCurve3 创建平滑曲线作为路径:

// 创建路径点
const points = [];
points.push(new THREE.Vector3(-2, 0, 0));
points.push(new THREE.Vector3(-1, 1, 0));
points.push(new THREE.Vector3(0, 0, 0));
points.push(new THREE.Vector3(1, -1, 0));
points.push(new THREE.Vector3(2, 0, 0));
// 创建曲线
const curve = new THREE.CatmullRomCurve3(points);
curve.curveType = 'catmullrom';
curve.tension = 0.5;
// 从曲线上获取更多点用于渲染
const pathPoints = curve.getPoints(50);
// 创建几何体和材质
const geometry = new THREE.BufferGeometry().setFromPoints(pathPoints);
const material = new THREE.LineBasicMaterial({ color: 0x00ffff });
// 创建线条对象
const pathLine = new THREE.Line(geometry, material);
scene.add(pathLine);

四、实现发光效果

方法一:使用 LineBasicMaterial 和后处理

// 首先需要引入EffectComposer和相关后处理组件
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
// 创建后处理合成器
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
// 创建发光通道
const bloomPass = new UnrealBloomPass(new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 0.4, 0.85);
bloomPass.threshold = 0;
bloomPass.strength = 2; // 控制发光强度
bloomPass.radius = 0;
composer.addPass(bloomPass);
// 修改动画循环使用合成器渲染
function animate() {
    requestAnimationFrame(animate);
    controls.update();
    // renderer.render(scene, camera); // 注释掉原来的渲染
    composer.render(); // 使用合成器渲染
}

方法二:自定义着色器材质

// 创建自定义发光材质
const glowMaterial = new THREE.ShaderMaterial({
    uniforms: {
        color: { value: new THREE.Color(0x00ffff) },
        thickness: { value: 1.0 },
        opacity: { value: 0.8 }
    },
    vertexShader: `
        attribute float lineDistance;
        varying float vLineDistance;
        void main() {
            vLineDistance = lineDistance;
            vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
            gl_Position = projectionMatrix * mvPosition;
        }
    `,
    fragmentShader: `
        uniform vec3 color;
        uniform float thickness;
        uniform float opacity;
        varying float vLineDistance;
        void main() {
            float alpha = thickness * (1.0 - abs(vLineDistance));
            gl_FragColor = vec4(color, alpha * opacity);
        }
    `,
    transparent: true,
    depthWrite: false
});
// 创建几何体时添加lineDistance属性
const positions = new Float32Array(pathPoints.length * 3);
const lineDistances = new Float32Array(pathPoints.length);
for (let i = 0; i < pathPoints.length; i++) {
    const point = pathPoints[i];
    positions[i * 3] = point.x;
    positions[i * 3 + 1] = point.y;
    positions[i * 3 + 2] = point.z;
    
    // 计算线段距离
    lineDistances[i] = i / (pathPoints.length - 1);
}
const geometry = new THREE.BufferGeometry();
geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
geometry.setAttribute('lineDistance', new THREE.BufferAttribute(lineDistances, 1));
// 创建发光线条
const glowLine = new THREE.Line(geometry, glowMaterial);
scene.add(glowLine);

五、动态路径效果

我们可以通过动画让路径动起来:

// 动态路径动画
let pathAnimationProgress = 0;
const pathAnimationSpeed = 0.01;
function updatePathAnimation() {
    pathAnimationProgress = (pathAnimationProgress + pathAnimationSpeed) % 1;
    
    // 更新几何体
    const points = curve.getPoints(50);
    const positions = geometry.attributes.position.array;
    
    for (let i = 0; i < points.length; i++) {
        const t = i / (points.length - 1);
        if (t <= pathAnimationProgress) {
            positions[i * 3] = points[i].x;
            positions[i * 3 + 1] = points[i].y;
            positions[i * 3 + 2] = points[i].z;
        } else {
            // 未到达的部分隐藏
            positions[i * 3] = points[Math.floor(pathAnimationProgress * (points.length - 1))].x;
            positions[i * 3 + 1] = points[Math.floor(pathAnimationProgress * (points.length - 1))].y;
            positions[i * 3 + 2] = points[Math.floor(pathAnimationProgress * (points.length - 1))].z;
        }
    }
    
    geometry.attributes.position.needsUpdate = true;
}
// 在动画循环中调用更新函数
function animate() {
    requestAnimationFrame(animate);
    controls.update();
    updatePathAnimation();
    composer.render();
}

六、优化与注意事项

  1. 性能考虑:复杂路径和过多发光效果会影响性能,可以适当减少点数量或使用 LOD 技术
  1. 移动设备适配:在移动设备上可能需要降低效果强度
  1. 兼容性:确保使用的 Three.js 版本与后处理组件兼容

以上就是在 Three.js 中实现发光路径效果的完整指南,你可以根据自己的需求选择合适的方法来创建引人注目的 3D 路径效果。