前言
使用threeJs + dat.GUI实现一个旋转星空的效果,效果如下:
完整代码可以去
文章末尾
直接拿去使用
大概步骤
- 引入库
- 初始化 Three.js 场景、相机和渲染器
- 设置 dat.GUI 控件
- 创建星星
- 将星星添加到场景中
- 动画循环
- dat.GUI 控制更新
- 窗口调整事件
通过本文的学习, 你将会收获:
- 如何引入和使用 Three.js 和 dat.GUI 库
- 初始化并配置一个 3D 场景,包括相机和渲染器
- 创建和添加星星对象到场景中
- 实现动画效果,使星星不断旋转
- 使用 dat.GUI 控件动态调整星星的颜色、大小和数量
- 处理窗口调整事件,确保渲染器和相机的设置随窗口大小变化而更新
具体实现:
1. 引入库
这里直接写在html 里面, 引入了CDN加载. 如果在vue or react等中使用,可使用包管理器进行依赖的下载.
js
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <!-- 引入Three.js库 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script> <!-- 引入dat.GUI库 -->
2. 初始化 Three.js 场景、相机和渲染器
- 创建一个新的 Three.js 场景
scene
。 - 创建一个透视相机
camera
,设置视角、宽高比、近裁剪面和远裁剪面。 - 创建一个 WebGL 渲染器
renderer
,设置渲染器的尺寸,并将其添加到文档的body
中。
js
// 初始化场景、相机、渲染器 const scene = new THREE.Scene(); // 创建一个新的Three.js场景 const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); // 创建透视相机 const renderer = new THREE.WebGLRenderer(); // 创建WebGL渲染器 renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器的大小为窗口的内宽和内高 document.body.appendChild(renderer.domElement); // 将渲染器的canvas元素添加到HTML文档中
3.设置 dat.GUI 控件
- 创建一个包含星星配置的对象
starSettings
,包括颜色、大小和数量以及重置方法. - 创建一个 dat.GUI 对象,并创建GUI 控件对象以便后续更新
js
// dat.GUI配置 const starSettings = { color: 0xffffff, // 初始星星颜色为白色 size: 1, // 初始星星大小 count: 1000, // 初始星星数量 // 重置函数 reset: function () { this.color = 0xffffff; // 重置颜色为白色 this.size = 1; // 重置大小为1 this.count = 1000; // 重置数量为1000 updateStars(); // 更新星星 // 同步 GUI 控件的值 guiControllers.color.setValue(this.color); guiControllers.size.setValue(this.size); guiControllers.count.setValue(this.count); } }; const gui = new dat.GUI(); // 创建dat.GUI对象 // 创建 GUI 控件并保存引用以便后续更新 const guiControllers = { color: gui.addColor(starSettings, 'color').name('颜色').onChange(updateStars), // 添加颜色控制 size: gui.add(starSettings, 'size', 0.1, 10).name('大小').onChange(updateStars), // 添加大小控制 count: gui.add(starSettings, 'count', 100, 10000).name('数量').onChange(updateStars), // 添加数量控制 reset: gui.add(starSettings, 'reset').name('重置') // 添加重置按钮 };
这里使用到的GUI的方法说明:
1. add
add(object, property, [min], [max], [step])
创建一个新的控件,并将其添加到 GUI 中。
object
:包含要控制属性的对象。property
:要控制的属性。min
:属性的最小值(可选)。max
:属性的最大值(可选)。step
:属性的步长(可选)。- 返回一个
GUIController
对象。
2. addColor
addColor(object, property)
创建一个颜色选择控件,并将其添加到 GUI 中。
object
:包含要控制属性的对象。property
:要控制的属性。- 返回一个
GUIController
对象。
4. 创建星星
- 定义
createStars
函数来创建星星。 - 在函数中,创建一个几何体
geometry
和一个空的顶点数组vertices
。 - 根据
starSettings.count
循环生成随机的x
、y
、z
坐标,并将它们添加到vertices
数组中。 - 使用
THREE.Float32BufferAttribute
将顶点数组添加到几何体中。 - 创建一个星星材质
material
,并结合几何体和材质创建一个THREE.Points
对象stars
。 - 返回创建的星星对象。
js
function createStars() { const geometry = new THREE.BufferGeometry(); // 创建几何体 const vertices = []; // 用于存储星星位置的数组 for (let i = 0; i < starSettings.count; i++) { // 根据星星数量生成顶点 const x = THREE.MathUtils.randFloatSpread(2000); // 随机生成x坐标 const y = THREE.MathUtils.randFloatSpread(2000); // 随机生成y坐标 const z = THREE.MathUtils.randFloatSpread(2000); // 随机生成z坐标 vertices.push(x, y, z); // 将生成的顶点添加到数组中 } console.log(vertices); // 包含3000 个 随机顶点值的数组 geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); // 将顶点添加到几何体中 console.log(geometry.getAttribute('position').count); const material = new THREE.PointsMaterial({ color: starSettings.color, size: starSettings.size }); // 创建星星材质 const stars = new THREE.Points(geometry, material); // 创建星星物体 return stars; // 返回创建的星星 } let stars = createStars(); // 调用createStars函数创建星星 scene.add(stars); // 将星星添加到场景中
额外说明:
调用
createStars
方法后,返回的对象stars
是一个包含 1000 个星星的THREE.Points
对象。每个星星的位置由顶点数组中的坐标决定。
具体来说,createStars
方法中:
- 创建一个新的
THREE.BufferGeometry
对象geometry
。 - 创建一个包含 3000 个元素的
vertices
数组,每三个元素(x, y, z)表示一个星星的位置。由于starSettings.count
是 1000,所以会有 1000 个星星,每个星星用 3 个坐标值表示,共计 3000 个值。 - 将
vertices
数组设置为geometry
对象的position
属性。 - 创建一个
THREE.PointsMaterial
对象material
,用于定义星星的材质。 - 使用
geometry
和material
创建一个THREE.Points
对象stars
,该对象包含了所有的星星。
返回的 stars
对象中包含 1000 个星星,每个星星的位置由顶点数组定义。因此,尽管 createStars
方法返回的是一个对象,但这个对象实际上表示了 1000 个星星的位置和材质。
总体来说:
vertices
数组中包含 3000 个值,每三个值表示一个星星的 x, y, z 坐标。geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3))
将这些坐标作为星星的位置属性添加到几何体中。new THREE.Points(geometry, material)
创建了一个包含所有星星的THREE.Points
对象。- 返回的
stars
对象表示 1000 个星星。
因此,返回的 stars
对象是一个包含 1000 个星星的集合。
5. 动画循环
- 定义
animate
函数,用于执行动画循环。 - 在每帧中,旋转星星并渲染场景。
- 使用
requestAnimationFrame
调用animate
函数,确保动画持续进行。
js
// 动画循环 function animate() { requestAnimationFrame(animate); // 请求下一帧动画 stars.rotation.x += 0.001; // 旋转星星 stars.rotation.y += 0.002; // 旋转星星 renderer.render(scene, camera); // 渲染场景 } animate(); // 开始动画
额外说明
requestAnimationFrame 方法可以查看这个链接 developer.mozilla.org/zh-CN/docs/…
6. dat.GUI 控制更新
当我们调制控件某个值的大小的就会触发页面的重更新.
- 定义
updateStars
函数,当用户通过 dat.GUI 修改设置时,更新星星。 - 从场景中移除旧的星星,创建新的星星,并将其添加到场景中。
js
function updateStars() { scene.remove(stars); // 从场景中移除旧的星星 stars = createStars(); // 创建新的星星 scene.add(stars); // 将新的星星添加到场景中 }
执行流程
修改值 ==> 修改starSettings
中的值 ==> 触发updateStars
的函数执行 ==> 删除场景 ==> 重新读取starSettings
参数并创建对象 ==> 放入场景中
7. 窗口调整事件
添加窗口调整事件监听器,当窗口大小变化时,更新相机的宽高比和渲染器的尺寸。
js
// 窗口大小调整 window.addEventListener('resize', () => { // 监听窗口大小变化事件 camera.aspect = window.innerWidth / window.innerHeight; // 更新相机的宽高比 camera.updateProjectionMatrix(); // 更新相机投影矩阵 renderer.setSize(window.innerWidth, window.innerHeight); // 更新渲染器大小 });
完整代码
html
<!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> <style> body { margin: 0; } /* 去掉页面的默认边距 */ canvas { display: block; } /* 将canvas设置为块级元素,去掉默认内边距 */ </style> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <!-- 引入Three.js库 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script> <!-- 引入dat.GUI库 --> <script> // 初始化场景、相机、渲染器 const scene = new THREE.Scene(); // 创建一个新的Three.js场景 const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); // 创建透视相机 const renderer = new THREE.WebGLRenderer(); // 创建WebGL渲染器 renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器的大小为窗口的内宽和内高 document.body.appendChild(renderer.domElement); // 将渲染器的canvas元素添加到HTML文档中 // dat.GUI配置 const starSettings = { color: 0xffffff, // 初始星星颜色为白色 size: 1, // 初始星星大小 count: 1000, // 初始星星数量 rotationSpeedX: 0.001, // 初始星星x轴旋转速度 rotationSpeedY: 0.002, // 初始星星y轴旋转速度 // 重置函数 reset: function () { this.color = 0xffffff; // 重置颜色为白色 this.size = 1; // 重置大小为1 this.count = 1000; // 重置数量为1000 // 旋转速度 this.rotationSpeedY = 0.002; this.rotationSpeedX = 0.001; updateStars(); // 更新星星 // 同步 GUI 控件的值 guiControllers.color.setValue(this.color); guiControllers.size.setValue(this.size); guiControllers.count.setValue(this.count); guiControllers.rotationSpeedX.setValue(this.rotationSpeedX); guiControllers.rotationSpeedY.setValue(this.rotationSpeedY); } }; function createStars() { const geometry = new THREE.BufferGeometry(); // 创建几何体 const vertices = []; // 用于存储星星位置的数组 for (let i = 0; i < starSettings.count; i++) { // 根据星星数量生成顶点 const x = THREE.MathUtils.randFloatSpread(2000); // 随机生成x坐标 const y = THREE.MathUtils.randFloatSpread(2000); // 随机生成y坐标 const z = THREE.MathUtils.randFloatSpread(2000); // 随机生成z坐标 vertices.push(x, y, z); // 将生成的顶点添加到数组中 } geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3)); // 将顶点添加到几何体中 const material = new THREE.PointsMaterial({ color: starSettings.color, size: starSettings.size }); // 创建星星材质 const stars = new THREE.Points(geometry, material); // 创建星星物体 return stars; // 返回创建的星星 } let stars = createStars(); // 调用createStars函数创建星星 scene.add(stars); // 将星星添加到场景中 camera.position.z = 5; // 设置相机位置 // 动画循环 function animate() { requestAnimationFrame(animate); // 请求下一帧动画 stars.rotation.x += starSettings.rotationSpeedX; // 旋转星星 stars.rotation.y += starSettings.rotationSpeedY; // 旋转星星 renderer.render(scene, camera); // 渲染场景 } animate(); // 开始动画 const gui = new dat.GUI(); // 创建dat.GUI对象 // 创建 GUI 控件并保存引用以便后续更新 const guiControllers = { color: gui.addColor(starSettings, 'color').name('颜色').onChange(updateStars), // 添加颜色控制 size: gui.add(starSettings, 'size', 0.1, 10).name('大小').onChange(updateStars), // 添加大小控制 count: gui.add(starSettings, 'count', 100, 10000).name('数量').onChange(updateStars), // 添加数量控制 // 添加星星的旋转速度 rotationSpeedX: gui.add(starSettings, 'rotationSpeedX', 0.001, 0.1, 0.001).name('旋转速度X').onChange(updateStars), rotationSpeedY: gui.add(starSettings, 'rotationSpeedY', 0.001, 0.1, 0.001).name('旋转速度Y').onChange(updateStars), reset: gui.add(starSettings, 'reset').name('重置') // 添加重置按钮 }; function updateStars() { scene.remove(stars); // 从场景中移除旧的星星 stars = createStars(); // 创建新的星星 scene.add(stars); // 将新的星星添加到场景中 } // 窗口大小调整 window.addEventListener('resize', () => { // 监听窗口大小变化事件 camera.aspect = window.innerWidth / window.innerHeight; // 更新相机的宽高比 camera.updateProjectionMatrix(); // 更新相机投影矩阵 renderer.setSize(window.innerWidth, window.innerHeight); // 更新渲染器大小 }); </script> </body> </html>