一、概述
在 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();
}
六、优化与注意事项
- 性能考虑:复杂路径和过多发光效果会影响性能,可以适当减少点数量或使用 LOD 技术
- 移动设备适配:在移动设备上可能需要降低效果强度
- 兼容性:确保使用的 Three.js 版本与后处理组件兼容
以上就是在 Three.js 中实现发光路径效果的完整指南,你可以根据自己的需求选择合适的方法来创建引人注目的 3D 路径效果。