阅读视图
ThreeJS 着色器图形特效
本文档涵盖Three.js中高级着色器图形特效的实现方法,基于实际代码示例进行讲解。
最终效果如图:
1. 着色器图形特效基础
1.1 复杂着色器材质创建
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as dat from "dat.gui";
import deepVertexShader from "../shaders/deep/vertex.glsl";
import deepFragmentShader from "../shaders/deep/fragment.glsl";
// 创建带有多个uniforms的着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: deepVertexShader,
fragmentShader: deepFragmentShader,
uniforms: {
uColor: {
value: new THREE.Color("purple"),
},
// 波浪的频率
uFrequency: {
value: params.uFrequency,
},
// 波浪的幅度
uScale: {
value: params.uScale,
},
// 动画时间
uTime: {
value: 0,
},
uTexture: {
value: texture,
},
},
side: THREE.DoubleSide,
transparent: true,
});
1.2 GUI参数控制
通过dat.GUI实时控制着色器参数:
// 控制频率参数
gui
.add(params, "uFrequency")
.min(0)
.max(50)
.step(0.1)
.onChange((value) => {
shaderMaterial.uniforms.uFrequency.value = value;
});
// 控制幅度参数
gui
.add(params, "uScale")
.min(0)
.max(1)
.step(0.01)
.onChange((value) => {
shaderMaterial.uniforms.uScale.value = value;
});
2. 高级片元着色器技术
2.1 UV坐标操作
UV坐标是纹理映射的基础,也是创建各种图形效果的关键:
void main(){
// 1. 通过顶点对应的uv,决定每一个像素在uv图像的位置,通过这个位置x,y决定颜色
// gl_FragColor =vec4(vUv,0,1) ;
// 2. 对第一种变形
// gl_FragColor = vec4(vUv,1,1);
// 3. 利用uv实现渐变效果,从左到右
float strength = vUv.x;
gl_FragColor =vec4(strength,strength,strength,1);
}
2.2 数学函数应用
利用GLSL内置数学函数创建复杂效果:
// 随机函数
float random (vec2 st) {
return fract(sin(dot(st.xy,vec2(12.9898,78.233)))*43758.5453123);
}
// 噪声函数
float noise (in vec2 _st) {
vec2 i = floor(_st);
vec2 f = fract(_st);
// 四个角落的随机值
float a = random(i);
float b = random(i + vec2(1.0, 0.0));
float c = random(i + vec2(0.0, 1.0));
float d = random(i + vec2(1.0, 1.0));
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(a, b, u.x) +
(c - a)* u.y * (1.0 - u.x) +
(d - b) * u.x * u.y;
}
2.3 几何图形绘制
使用数学函数绘制各种几何图形:
// 绘制圆形
float strength = 1.0 - step(0.5,distance(vUv,vec2(0.5))+0.25) ;
gl_FragColor =vec4(strength,strength,strength,1);
// 绘制圆环
float strength = step(0.5,distance(vUv,vec2(0.5))+0.35) ;
strength *= (1.0 - step(0.5,distance(vUv,vec2(0.5))+0.25)) ;
gl_FragColor =vec4(strength,strength,strength,1);
// 波浪效果
vec2 waveUv = vec2(
vUv.x+sin(vUv.y*100.0)*0.1,
vUv.y+sin(vUv.x*100.0)*0.1
);
float strength = 1.0 - step(0.01,abs(distance(waveUv,vec2(0.5))-0.25)) ;
gl_FragColor =vec4(strength,strength,strength,1);
3. 动画与时间控制
3.1 时间uniform应用
在动画循环中更新时间uniform:
const clock = new THREE.Clock();
function animate(t) {
const elapsedTime = clock.getElapsedTime();
shaderMaterial.uniforms.uTime.value = elapsedTime; // 更新时间
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
3.2 着色器中的动画效果
// 使用时间创建波浪动画
float strength = step(0.9,sin(cnoise(vUv * 10.0)*20.0+uTime)) ;
// 波纹效果
float strength = sin(cnoise(vUv * 10.0)*5.0+uTime) ;
4. 颜色混合与插值
4.1 颜色混合函数
// 使用混合函数混颜色
vec3 purpleColor = vec3(1.0, 0.0, 1.0);
vec3 greenColor = vec3(1.0, 1.0, 1.0);
vec3 uvColor = vec3(vUv,1.0);
float strength = step(0.9,sin(cnoise(vUv * 10.0)*20.0)) ;
vec3 mixColor = mix(greenColor,uvColor,strength);
gl_FragColor =vec4(mixColor,1.0);
5. 纹理与采样
5.1 纹理采样
uniform sampler2D uTexture;
void main(){
vec4 textureColor = texture2D(uTexture,vUv);
textureColor.rgb*=height;
gl_FragColor = textureColor;
}
6. 几何变换
6.1 旋转函数
// 旋转函数
vec2 rotate(vec2 uv, float rotation, vec2 mid)
{
return vec2(
cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x,
cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y
);
}
// 使用旋转函数
vec2 rotateUv = rotate(vUv,-uTime*5.0,vec2(0.5));
7. 复杂效果实现
7.1 万花筒效果
// 万花筒效果
float angle = atan(vUv.x-0.5,vUv.y-0.5)/PI;
float strength = mod(angle*10.0,1.0);
gl_FragColor =vec4(strength,strength,strength,1);
7.2 雷达扫描效果
// 雷达扫描效果
vec2 rotateUv = rotate(vUv,-uTime*5.0,vec2(0.5));
float alpha = 1.0 - step(0.5,distance(vUv,vec2(0.5)));
float angle = atan(rotateUv.x-0.5,rotateUv.y-0.5);
float strength = (angle+3.14)/6.28;
gl_FragColor =vec4(strength,strength,strength,alpha);
8. 性能优化与调试
8.1 性能优化技巧
- 减少复杂计算:避免在着色器中进行过于复杂的数学运算
- 合理使用纹理:预先计算复杂效果并存储在纹理中
- 简化几何体:在不影响视觉效果的前提下减少顶点数
8.2 调试技巧
- 逐步构建:从简单效果开始,逐步增加复杂性
- 输出中间值:将中间计算结果输出为颜色进行调试
- 使用常量验证:先用常量验证逻辑,再引入变量
总结
本章深入探讨了Three.js中高级着色器图形特效的实现方法,包括:
- 复杂着色器材质的创建和参数控制
- 数学函数在图形生成中的应用
- UV坐标操作和几何图形绘制
- 时间动画和颜色混合技术
- 纹理采样和几何变换
- 复杂视觉效果的实现方法
- 性能优化和调试技巧
通过掌握这些技术,可以创建出丰富的视觉效果和动态图形。
ThreeJS 着色器编程基础入门
本文档涵盖Three.js中着色器编程的基础概念和实现方法,基于实际代码示例进行讲解。
最终效果如图:
1. 着色器基础概念
着色器(Shader)是运行在GPU上的小程序,用于计算3D场景中每个像素的颜色。在Three.js中,有两种主要的着色器:
- 顶点着色器(Vertex Shader):处理每个顶点的位置变换
- 片元着色器(Fragment Shader):确定每个像素的最终颜色
1.1 着色器导入和初始化
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import gsap from "gsap";
import * as dat from "dat.gui";
// 顶点着色器
import basicVertexShader from "../shader/raw/vertex.glsl";
// 片元着色器
import basicFragmentShader from "../shader/raw/fragment.glsl";
2. 着色器材质创建
2.1 RawShaderMaterial vs ShaderMaterial
RawShaderMaterial直接使用GLSL代码,不会自动添加默认的uniforms和attributes:
// 创建原始着色器材质
const rawShaderMaterial = new THREE.RawShaderMaterial({
vertexShader: basicVertexShader,
fragmentShader: basicFragmentShader,
side: THREE.DoubleSide,
uniforms: {
uTime: {
value: 0,
},
uTexture: {
value: texture,
},
},
});
2.2 基础着色器材质
// 创建着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: `
void main(){
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
}
`,
fragmentShader: `
void main(){
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0);
}
`,
});
3. 顶点着色器详解
顶点着色器负责处理3D空间中的顶点位置,以下是一个包含动画效果的顶点着色器:
precision lowp float;
attribute vec3 position;
attribute vec2 uv;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
// 获取时间
uniform float uTime;
varying vec2 vUv;
varying float vElevation;
void main(){
vUv = uv;
vec4 modelPosition = modelMatrix * vec4( position, 1.0 );
// 添加基于时间的波浪动画
modelPosition.z = sin((modelPosition.x+uTime) * 10.0)*0.05 ;
modelPosition.z += sin((modelPosition.y+uTime) * 10.0)*0.05 ;
vElevation = modelPosition.z;
gl_Position = projectionMatrix * viewMatrix * modelPosition ;
}
4. 片元着色器详解
片元着色器负责确定每个像素的颜色,以下是一个处理纹理和高度的片元着色器:
precision lowp float;
varying vec2 vUv;
varying float vElevation;
uniform sampler2D uTexture;
void main(){
// 根据UV,取出对应的颜色
float height = vElevation + 0.05 * 20.0;
vec4 textureColor = texture2D(uTexture,vUv);
textureColor.rgb*=height;
gl_FragColor = textureColor;
}
5. Uniforms统一变量
Uniforms是在JavaScript代码和着色器之间传递数据的变量:
const rawShaderMaterial = new THREE.RawShaderMaterial({
vertexShader: basicVertexShader,
fragmentShader: basicFragmentShader,
side: THREE.DoubleSide,
uniforms: {
uTime: {
value: 0, // 时间变量,用于动画
},
uTexture: {
value: texture, // 纹理变量
},
},
});
在动画循环中更新uniform值:
const clock = new THREE.Clock();
function animate(t) {
const elapsedTime = clock.getElapsedTime();
// 更新着色器中的时间uniform
rawShaderMaterial.uniforms.uTime.value = elapsedTime;
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
6. 几何体与着色器结合
使用平面几何体展示着色器效果:
// 创建平面
const floor = new THREE.Mesh(
new THREE.PlaneBufferGeometry(1, 1, 64, 64), // 细分更多,波浪效果更明显
rawShaderMaterial
);
scene.add(floor);
7. 基础着色器示例
创建一个简单的黄色平面着色器:
// 创建基础着色器材质
const shaderMaterial = new THREE.ShaderMaterial({
vertexShader: `
void main(){
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 ) ;
}
`,
fragmentShader: `
void main(){
gl_FragColor = vec4(1.0, 1.0, 0.0, 1.0); // 黄色
}
`,
});
8. 着色器开发最佳实践
-
精度声明:在片元着色器中声明精度
precision lowp float; // 低精度 precision mediump float; // 中等精度 precision highp float; // 高精度 -
变量类型:
-
attribute:每个顶点独有的数据(如位置、UV坐标) -
uniform:所有顶点共享的数据(如时间、纹理) -
varying:在顶点着色器和片元着色器之间传递的数据
-
-
性能优化:避免在着色器中使用复杂运算,尽可能在CPU端预计算
-
调试技巧:通过将中间计算结果输出到颜色来调试着色器
总结
本章介绍了Three.js中着色器编程的基础知识,包括:
- 着色器的基本概念和类型
- 如何创建和使用着色器材质
- 顶点着色器和片元着色器的编写
- 如何通过uniforms在JavaScript和着色器间传递数据
- 基础的着色器动画实现
通过掌握这些基础知识,可以进一步探索更复杂的着色器效果。
ThreeJS GSAP动画库综合应用
本文档涵盖了Three.js与GSAP动画库综合应用的关键技术和实现方法,基于实际代码示例进行讲解,展示如何利用GSAP库创建丰富的3D动画效果。
1. GSAP动画库导入与初始化
在项目中使用GSAP动画库需要先导入并进行初始化:
import * as THREE from "three";
// 导入轨道控制器
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
// 导入动画库
import gsap from "gsap";
// 导入dat.gui
import * as dat from "dat.gui";
const textureLoader = new THREE.TextureLoader();
const particlesTexture = textureLoader.load("./textures/particles/1.png");
// 1、创建场景
const scene = new THREE.Scene();
// 2、创建相机
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
300
);
// 设置相机位置
camera.position.set(0, 0, 18);
scene.add(camera);
// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ alpha: true });
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 开启场景中的阴影贴图
renderer.shadowMap.enabled = true;
renderer.physicallyCorrectLights = true;
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);
// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
// 设置时钟
const clock = new THREE.Clock();
// 鼠标的位置对象
const mouse = new THREE.Vector2();
// 创建投射光线对象
const raycaster = new THREE.Raycaster();
// 红色材质(用于交互效果)
const redMaterial = new THREE.MeshBasicMaterial({
color: "#ff0000",
});
2. 立方体网格动画
2.1 立方体网格创建
创建一个由多个立方体组成的3D网格,用于第一屏的视觉效果:
// 创建立方体几何体
const cubeGeometry = new THREE.BoxBufferGeometry(2, 2, 2);
const material = new THREE.MeshBasicMaterial({
wireframe: true, // 线框模式
});
// 创建立方体网格
let cubeArr = [];
let cubeGroup = new THREE.Group();
for (let i = 0; i < 5; i++) {
for (let j = 0; j < 5; j++) {
for (let z = 0; z < 5; z++) {
const cube = new THREE.Mesh(cubeGeometry, material);
cube.position.set(i * 2 - 4, j * 2 - 4, z * 2 - 4); // 设置立方体位置
cubeGroup.add(cube);
cubeArr.push(cube);
}
}
}
scene.add(cubeGroup);
2.2 立方体网格旋转动画
使用GSAP库实现立方体网格的连续旋转动画:
// 立方体网格旋转动画
gsap.to(cubeGroup.rotation, {
x: "+=" + Math.PI * 2, // X轴旋转一周
y: "+=" + Math.PI * 2, // Y轴旋转一周
duration: 10, // 动画持续时间10秒
ease: "power2.inOut", // 缓动函数
repeat: -1, // 无限重复
});
3. 三角形几何体动画
3.1 随机三角形生成
创建一系列随机形状的三角形,形成独特的视觉效果:
// 创建三角形组
var sjxGroup = new THREE.Group();
for (let i = 0; i < 50; i++) {
// 每一个三角形,需要3个顶点,每个顶点需要3个值
const geometry = new THREE.BufferGeometry();
const positionArray = new Float32Array(9);
for (let j = 0; j < 9; j++) {
if (j % 3 == 1) {
positionArray[j] = Math.random() * 10 - 5; // Y轴特殊处理
} else {
positionArray[j] = Math.random() * 10 - 5;
}
}
geometry.setAttribute(
"position",
new THREE.BufferAttribute(positionArray, 3)
);
// 随机颜色
let color = new THREE.Color(Math.random(), Math.random(), Math.random());
const material = new THREE.MeshBasicMaterial({
color: color,
transparent: true,
opacity: 0.5,
side: THREE.DoubleSide,
});
// 根据几何体和材质创建物体
let sjxMesh = new THREE.Mesh(geometry, material);
sjxGroup.add(sjxMesh);
}
sjxGroup.position.set(0, -30, 0); // 设置三角形组的位置
scene.add(sjxGroup);
3.2 三角形组旋转动画
使用GSAP库实现三角形组的连续旋转动画:
// 三角形组旋转动画
gsap.to(sjxGroup.rotation, {
x: "-=" + Math.PI * 2, // X轴反向旋转一周
z: "+=" + Math.PI * 2, // Z轴正向旋转一周
duration: 12, // 动画持续时间12秒
ease: "power2.inOut", // 缓动函数
repeat: -1, // 无限重复
});
4. 点光源与小球动画
4.1 球体和光源设置
创建一个带有光源的动态场景:
// 创建球体组
const sphereGroup = new THREE.Group();
const sphereGeometry = new THREE.SphereBufferGeometry(1, 20, 20);
const spherematerial = new THREE.MeshStandardMaterial({
side: THREE.DoubleSide,
});
const sphere = new THREE.Mesh(sphereGeometry, spherematerial);
sphere.castShadow = true; // 启用阴影投射
sphereGroup.add(sphere);
// 创建平面
const planeGeometry = new THREE.PlaneBufferGeometry(20, 20);
const plane = new THREE.Mesh(planeGeometry, spherematerial);
plane.position.set(0, -1, 0);
plane.rotation.x = -Math.PI / 2;
plane.receiveShadow = true; // 启用阴影接收
sphereGroup.add(plane);
// 添加环境光
const light = new THREE.AmbientLight(0xffffff, 0.5);
sphereGroup.add(light);
// 创建小球(带光源)
const smallBall = new THREE.Mesh(
new THREE.SphereBufferGeometry(0.1, 20, 20),
new THREE.MeshBasicMaterial({ color: 0xff0000 })
);
smallBall.position.set(2, 2, 2);
// 点光源
const pointLight = new THREE.PointLight(0xff0000, 3);
pointLight.castShadow = true;
pointLight.shadow.radius = 20; // 阴影模糊度
pointLight.shadow.mapSize.set(512, 512); // 阴影贴图分辨率
smallBall.add(pointLight);
sphereGroup.add(smallBall);
sphereGroup.position.set(0, -60, 0);
scene.add(sphereGroup);
4.2 小球位置动画
使用GSAP库实现小球的位置动画,创造弹跳和移动效果:
// 小球水平移动动画
gsap.to(smallBall.position, {
x: -3, // 移动到x=-3位置
duration: 6, // 动画持续时间6秒
ease: "power2.inOut", // 缓动函数
repeat: -1, // 无限重复
yoyo: true, // 往返运动
});
// 小球垂直移动动画(弹跳效果)
gsap.to(smallBall.position, {
y: 0, // 移动到y=0位置
duration: 0.5, // 动画持续时间0.5秒
ease: "power2.inOut", // 缓动函数
repeat: -1, // 无限重复
yoyo: true, // 往返运动
});
5. 动画循环与渲染
5.1 动画循环函数
实现基本的动画循环和渲染:
function render() {
let deltaTime = clock.getDelta();
// 鼠标移动影响相机位置
camera.position.x += (mouse.x * 10 - camera.position.x) * deltaTime * 5;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render(); // 启动动画循环
// 监听鼠标位置
window.addEventListener("mousemove", (event) => {
mouse.x = event.clientX / window.innerWidth - 0.5;
mouse.y = event.clientY / window.innerHeight - 0.5;
});
5.2 相机跟随动画
实现相机跟随鼠标移动的动画效果:
// 监听鼠标位置
window.addEventListener("mousemove", (event) => {
mouse.x = event.clientX / window.innerWidth - 0.5;
mouse.y = event.clientY / window.innerHeight - 0.5;
});
function render() {
let deltaTime = clock.getDelta();
// 鼠标移动影响相机位置
camera.position.x += (mouse.x * 10 - camera.position.x) * deltaTime * 5;
renderer.render(scene, camera);
requestAnimationFrame(render);
}
6. 交互动画
6.1 鼠标点击交互动画
实现鼠标点击时的交互效果:
// 创建投射光线对象
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
// 监听鼠标点击事件
window.addEventListener("click", (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -((event.clientY / window.innerHeight) * 2 - 1);
raycaster.setFromCamera(mouse, camera);
let result = raycaster.intersectObjects(cubeArr);
// 改变相交物体的材质颜色
result.forEach((item) => {
item.object.material = redMaterial;
});
});
总结
本章详细介绍了Three.js中创建3D动画特效的几种主要动画效果:
- GSAP动画库导入与初始化:导入GSAP动画库并初始化所需变量
- 立方体网格动画:通过GSAP库实现的连续旋转动画,创造动态的3D网格效果
- 三角形几何体动画:随机生成的三角形组合,配合旋转动画创造抽象艺术效果
- 点光源与小球动画:带动画的小球与点光源,展现动态光影效果
- 动画循环与渲染:实现基本的动画循环和渲染机制
- 相机跟随动画:实现相机跟随鼠标移动的动画效果
此外,还实现了实现鼠标点击交互效果,共同构成了完整的3D动画体验。通过合理运用Three.js和GSAP动画库,可以创造出丰富多样的3D动画效果。
ThreeJS 详解光线投射与物体交互
本文档涵盖了Three.js中光线投射(Raycaster)与物体交互的关键概念和实现方法,基于实际代码示例进行讲解。
1. 光线投射基础概念
光线投射是一种在三维空间中追踪光线路径的技术,主要用于检测鼠标与3D物体的交互。在Three.js中,Raycaster类提供了光线投射功能,可以用来检测鼠标点击、悬停等事件与场景中物体的交点。
2. Raycaster对象创建
首先需要创建一个Raycaster对象和鼠标位置对象:
// 创建投射光线对象
const raycaster = new THREE.Raycaster();
// 鼠标的位置对象
const mouse = new THREE.Vector2();
3. 场景设置
在进行光线投射之前,需要先设置好场景、相机和待检测的物体:
// 1、创建场景
const scene = new THREE.Scene();
// 2、创建相机
const camera = new THREE.PerspectiveCamera(
75, // 视野角度
window.innerWidth / window.innerHeight, // 宽高比
0.1, // 近平面
300 // 远平面
);
// 设置相机位置
camera.position.set(0, 0, 20);
scene.add(camera);
// 创建几何体和材质
const cubeGeometry = new THREE.BoxBufferGeometry(1, 1, 1);
const material = new THREE.MeshBasicMaterial({
wireframe: true, // 线框模式显示
});
const redMaterial = new THREE.MeshBasicMaterial({
color: "#ff0000", // 红色材质
});
// 创建多个立方体用于交互测试
let cubeArr = [];
for (let i = -5; i < 5; i++) {
for (let j = -5; j < 5; j++) {
for (let z = -5; z < 5; z++) {
const cube = new THREE.Mesh(cubeGeometry, material);
cube.position.set(i, j, z); // 设置立方体位置
scene.add(cube);
cubeArr.push(cube); // 将立方体添加到数组中便于检测
}
}
}
4. 鼠标事件监听
监听鼠标事件并将屏幕坐标转换为标准化设备坐标(NDC):
// 监听鼠标点击事件
window.addEventListener("click", (event) => {
// 将鼠标位置归一化为设备坐标 [-1, 1]
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -((event.clientY / window.innerHeight) * 2 - 1);
// 从相机设置光线投射器
raycaster.setFromCamera(mouse, camera);
// 检测与物体的交点
let result = raycaster.intersectObjects(cubeArr);
// 对相交的物体进行处理
result.forEach((item) => {
item.object.material = redMaterial; // 改变相交物体的材质
});
});
5. 鼠标移动事件监听(可选)
除了点击事件,也可以监听鼠标移动事件实现实时交互:
// 监听鼠标移动事件(注释掉的部分)
/*
window.addEventListener("mousemove", (event) => {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -((event.clientY / window.innerHeight) * 2 - 1);
raycaster.setFromCamera(mouse, camera);
let result = raycaster.intersectObjects(cubeArr);
result.forEach((item) => {
item.object.material = redMaterial;
});
});
*/
6. 渲染器配置
配置渲染器以支持场景渲染:
// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
// 设置渲染的尺寸大小
renderer.setSize(window.innerWidth, window.innerHeight);
// 开启场景中的阴影贴图
renderer.shadowMap.enabled = true;
renderer.physicallyCorrectLights = true;
// 将webgl渲染的canvas内容添加到body
document.body.appendChild(renderer.domElement);
7. 轨道控制器设置
添加轨道控制器以支持相机交互:
// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼,让控制器更有真实效果,必须在动画循环里调用.update()。
controls.enableDamping = true;
// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
8. 动画循环
在动画循环中更新控制器并渲染场景:
// 设置时钟
const clock = new THREE.Clock();
function render() {
let time = clock.getElapsedTime();
controls.update(); // 更新控制器
renderer.render(scene, camera); // 渲染场景
// 渲染下一帧的时候就会调用render函数
requestAnimationFrame(render);
}
render();
9. 响应式设计
处理窗口大小变化:
// 监听画面变化,更新渲染画面
window.addEventListener("resize", () => {
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio);
});
10. Raycaster方法详解
10.1 setFromCamera方法
该方法根据相机和鼠标位置设置光线:
raycaster.setFromCamera(mouse, camera);
10.2 intersectObjects方法
该方法检测光线与指定物体数组的交点:
let result = raycaster.intersectObjects(cubeArr);
返回的结果是一个数组,每个元素包含交点信息,如交点位置、相交的物体等。
11. 交点结果处理
交点结果包含丰富的信息:
result.forEach((item) => {
// item.distance: 交点与射线起点之间的距离
// item.point: 交点的三维坐标
// item.face: 相交的面
// item.object: 相交的物体
item.object.material = redMaterial; // 更改相交物体的材质
});
12. 性能优化建议
- 只对需要交互的物体进行检测,避免检测整个场景
- 合理设置检测频率,避免每帧都进行检测造成性能问题
- 使用分组管理需要检测的物体,便于批量处理
总结
光线投射是Three.js中实现用户交互的重要技术,通过Raycaster类可以轻松实现鼠标与3D物体的交互。主要步骤包括:
- 创建Raycaster和鼠标位置对象
- 设置场景、相机和待检测物体
- 监听鼠标事件并转换坐标
- 使用setFromCamera方法设置光线
- 使用intersectObjects方法检测交点
- 处理交点结果实现交互效果
通过这种技术,可以实现点击选择物体、悬停高亮、拖拽等功能,大大增强用户的交互体验。
ThreeJS 精通粒子特效
本文档涵盖了Three.js中粒子特效的关键概念和实现方法,基于实际代码示例进行讲解。
1. 粒子系统基础
1.1 点材质 (PointsMaterial) 设置
点材质是创建粒子特效的基础,可以通过多种参数配置粒子外观:
// 设置点材质
const pointsMaterial = new THREE.PointsMaterial();
pointsMaterial.size = 0.1; // 粒子大小
pointsMaterial.color.set(0xfff000); // 粒子颜色
pointsMaterial.sizeAttenuation = true; // 是否根据相机深度衰减粒子大小
pointsMaterial.sizeAttenuation = true; // 相机深度衰减
1.2 粒子纹理配置
为了增强粒子的视觉效果,通常会使用纹理:
// 载入纹理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load("./textures/particles/2.png");
// 设置点材质纹理
pointsMaterial.map = texture; // 纹理贴图
pointsMaterial.alphaMap = texture; // 透明度贴图
pointsMaterial.transparent = true; // 启用透明度
pointsMaterial.depthWrite = false; // 禁用深度写入
pointsMaterial.blending = THREE.AdditiveBlending; // 混合模式
2. 粒子几何体创建
2.1 使用缓冲几何体 (BufferGeometry)
高效的粒子系统通常使用缓冲几何体来存储大量顶点数据:
// 创建粒子几何体
const particlesGeometry = new THREE.BufferGeometry();
const count = 5000; // 粒子数量
// 设置缓冲区数组
const positions = new Float32Array(count * 3); // 位置数组
const colors = new Float32Array(count * 3); // 颜色数组
// 设置顶点位置
for (let i = 0; i < count * 3; i++) {
positions[i] = (Math.random() - 0.5) * 100; // 随机位置
colors[i] = Math.random(); // 随机颜色
}
// 将属性添加到几何体
particlesGeometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
particlesGeometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
2.2 从现有几何体创建粒子
可以从现有的几何体(如球体)转换为粒子:
// 创建球几何体
const sphereGeometry = new THREE.SphereBufferGeometry(3, 30, 30);
// 删除UV属性(如果不需要纹理映射)
delete sphereGeometry.attributes.uv;
// 创建粒子系统
const points = new THREE.Points(sphereGeometry, pointsMaterial);
scene.add(points);
3. 星河粒子系统
3.1 基础星河效果
创建具有随机分布的星河效果:
// 生成星河粒子
const generateGalaxy = () => {
geometry = new THREE.BufferGeometry();
const positions = new Float32Array(params.count * 3);
const colors = new Float32Array(params.count * 3);
// 循环生成点
for (let i = 0; i < params.count; i++) {
// 当前的点应该在哪一条分支的角度上
const branchAngel = (i % params.branch) * ((2 * Math.PI) / params.branch);
// 当前点距离圆心的距离
const distance = Math.random() * params.radius * Math.pow(Math.random(), 3);
const current = i * 3;
// 随机偏移
const randomX = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5;
const randomY = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5;
const randomZ = (Math.pow(Math.random() * 2 - 1, 3) * (params.radius - distance)) / 5;
// 计算最终位置
positions[current] = Math.cos(branchAngel + distance * params.rotateScale) * distance + randomX;
positions[current + 1] = 0 + randomY;
positions[current + 2] = Math.sin(branchAngel + distance * params.rotateScale) * distance + randomZ;
// 混合颜色,形成渐变色
const mixColor = centerColor.clone();
mixColor.lerp(endColor, distance / params.radius);
colors[current] = mixColor.r;
colors[current + 1] = mixColor.g;
colors[current + 2] = mixColor.b;
}
geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
geometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
// 设置点材质
material = new THREE.PointsMaterial({
size: params.size,
sizeAttenuation: true,
depthWrite: false,
blending: THREE.AdditiveBlending,
map: particlesTexture,
alphaMap: particlesTexture,
transparent: true,
vertexColors: true, // 使用顶点颜色
});
points = new THREE.Points(geometry, material);
scene.add(points);
};
4. 雪花粒子系统
4.1 多层雪花效果
创建多个层次的雪花粒子系统:
function createPoints(url, size = 0.5) {
const particlesGeometry = new THREE.BufferGeometry();
const count = 10000;
// 设置缓冲区数组
const positions = new Float32Array(count * 3);
const colors = new Float32Array(count * 3);
// 设置顶点
for (let i = 0; i < count * 3; i++) {
positions[i] = (Math.random() - 0.5) * 100; // 在空间内随机分布
colors[i] = Math.random(); // 随机颜色
}
particlesGeometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
particlesGeometry.setAttribute("color", new THREE.BufferAttribute(colors, 3));
// 设置点材质
const pointsMaterial = new THREE.PointsMaterial();
pointsMaterial.size = size; // 不同大小的粒子
pointsMaterial.color.set(0xfff000);
pointsMaterial.sizeAttenuation = true;
// 载入纹理
const textureLoader = new THREE.TextureLoader();
const texture = textureLoader.load(`./textures/particles/${url}.png`);
pointsMaterial.map = texture;
pointsMaterial.alphaMap = texture;
pointsMaterial.transparent = true;
pointsMaterial.depthWrite = false;
pointsMaterial.blending = THREE.AdditiveBlending;
pointsMaterial.vertexColors = true; // 启用顶点颜色
const points = new THREE.Points(particlesGeometry, pointsMaterial);
scene.add(points);
return points;
}
// 创建多层雪花效果
const points = createPoints("1", 1.5);
const points2 = createPoints("xh", 1);
const points3 = createPoints("xh", 2);
4.2 粒子动画
为粒子系统添加动画效果:
function render() {
let time = clock.getElapsedTime();
// 旋转动画
points.rotation.x = time * 0.3;
points2.rotation.x = time * 0.5;
points2.rotation.y = time * 0.4;
points3.rotation.x = time * 0.2;
points3.rotation.y = time * 0.2;
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(render);
}
5. 渲染器配置
5.1 基础渲染器设置
针对粒子系统优化渲染器:
// 初始化渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
// 开启场景中的阴影贴图
renderer.shadowMap.enabled = true;
renderer.physicallyCorrectLights = true;
// 将渲染器添加到DOM
document.body.appendChild(renderer.domElement);
6. 粒子系统优化技巧
6.1 混合模式
使用适当的混合模式提升视觉效果:
// 加法混合,常用于发光效果
pointsMaterial.blending = THREE.AdditiveBlending;
6.2 深度写入控制
控制粒子的深度写入行为:
// 禁用深度写入,避免粒子间的遮挡问题
pointsMaterial.depthWrite = false;
6.3 粒子大小衰减
根据距离调整粒子大小:
// 启用大小衰减,远处的粒子看起来更小
pointsMaterial.sizeAttenuation = true;
7. 控制器和辅助工具
7.1 轨道控制器
添加交互式视角控制:
// 创建轨道控制器
const controls = new OrbitControls(camera, renderer.domElement);
// 设置控制器阻尼,让控制器更有真实效果
controls.enableDamping = true;
// 添加坐标轴辅助器
const axesHelper = new THREE.AxesHelper(5);
scene.add(axesHelper);
8. 响应式设计
处理窗口大小变化:
// 监听画面变化,更新渲染画面
window.addEventListener("resize", () => {
// 更新摄像头
camera.aspect = window.innerWidth / window.innerHeight;
// 更新摄像机的投影矩阵
camera.updateProjectionMatrix();
// 更新渲染器
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置渲染器的像素比
renderer.setPixelRatio(window.devicePixelRatio);
});
总结
本章详细介绍了Three.js中粒子特效的各个方面,包括:
- 粒子系统基础概念
- 点材质的配置和优化
- 缓冲几何体的使用
- 星河和雪花粒子系统的创建
- 粒子动画和交互
- 渲染优化技巧
通过合理运用这些技术,可以创建出丰富多彩的粒子特效,如星系、雪花、烟雾、火焰等视觉效果。关键在于理解粒子的几何体构建、材质配置以及性能优化方法,从而在保证视觉效果的同时维持良好的运行性能。