前言
使用threeJs dat.GUI实现一个旋转星空的效果,效果如下:
完整代码可以去
文章末尾
直接拿去使用
大概步骤
- 引入库
- 初始化 Three.js 场景、相机和渲染器
- 设置 dat.GUI 控件
- 创建星星
- 将星星添加到场景中
- 动画循环
- dat.GUI 控制更新
- 窗口调整事件
通过本文的学习, 你将会收获:
- 如何引入和使用 Three.js 和 dat.GUI 库
- 初始化并配置一个 3D 场景,包括相机和渲染器
- 创建和添加星星对象到场景中
- 实现动画效果,使星星不断旋转
- 使用 dat.GUI 控件动态调整星星的颜色、大小和数量
- 处理窗口调整事件,确保渲染器和相机的设置随窗口大小变化而更新
具体实现:
1. 引入库
这里直接写在html 里面, 引入了CDN加载. 如果在vue or react等中使用,可使用包管理器进行依赖的下载.
代码语言:javascript复制 <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
中。
// 初始化场景、相机、渲染器
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 控件对象以便后续更新
// 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
。 - 返回创建的星星对象。
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
函数,确保动画持续进行。
// 动画循环
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 修改设置时,更新星星。 - 从场景中移除旧的星星,创建新的星星,并将其添加到场景中。
function updateStars() {
scene.remove(stars); // 从场景中移除旧的星星
stars = createStars(); // 创建新的星星
scene.add(stars); // 将新的星星添加到场景中
}
执行流程
修改值 ==> 修改starSettings
中的值 ==> 触发updateStars
的函数执行 ==> 删除场景 ==> 重新读取starSettings
参数并创建对象 ==> 放入场景中
7. 窗口调整事件
添加窗口调整事件监听器,当窗口大小变化时,更新相机的宽高比和渲染器的尺寸。
代码语言:javascript复制 // 窗口大小调整
window.addEventListener('resize', () => { // 监听窗口大小变化事件
camera.aspect = window.innerWidth / window.innerHeight; // 更新相机的宽高比
camera.updateProjectionMatrix(); // 更新相机投影矩阵
renderer.setSize(window.innerWidth, window.innerHeight); // 更新渲染器大小
});
完整代码
代码语言:javascript复制<!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>