ThreeJs Demo 之创建星空效果

2024-07-29 18:13:02 浏览数 (1)

前言

使用threeJs dat.GUI实现一个旋转星空的效果,效果如下:

完整代码可以去文章末尾直接拿去使用

大概步骤

  1. 引入库
  2. 初始化 Three.js 场景、相机和渲染器
  3. 设置 dat.GUI 控件
  4. 创建星星
  5. 将星星添加到场景中
  6. 动画循环
  7. dat.GUI 控制更新
  8. 窗口调整事件

通过本文的学习, 你将会收获:

  • 如何引入和使用 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 场景、相机和渲染器

  1. 创建一个新的 Three.js 场景 scene
  2. 创建一个透视相机 camera,设置视角、宽高比、近裁剪面和远裁剪面。
  3. 创建一个 WebGL 渲染器 renderer,设置渲染器的尺寸,并将其添加到文档的 body 中。
代码语言:javascript复制
    // 初始化场景、相机、渲染器
        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 控件

  1. 创建一个包含星星配置的对象 starSettings,包括颜色、大小和数量以及重置方法.
  2. 创建一个 dat.GUI 对象,并创建GUI 控件对象以便后续更新
代码语言:javascript复制
       // 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 循环生成随机的 xyz 坐标,并将它们添加到 vertices 数组中。
  • 使用 THREE.Float32BufferAttribute 将顶点数组添加到几何体中。
  • 创建一个星星材质 material,并结合几何体和材质创建一个 THREE.Points 对象 stars
  • 返回创建的星星对象。
代码语言:javascript复制
 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 方法中:

  1. 创建一个新的 THREE.BufferGeometry 对象 geometry
  2. 创建一个包含 3000 个元素的 vertices 数组,每三个元素(x, y, z)表示一个星星的位置。由于 starSettings.count 是 1000,所以会有 1000 个星星,每个星星用 3 个坐标值表示,共计 3000 个值。
  3. vertices 数组设置为 geometry 对象的 position 属性。
  4. 创建一个 THREE.PointsMaterial 对象 material,用于定义星星的材质。
  5. 使用 geometrymaterial 创建一个 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 函数,确保动画持续进行。
代码语言:javascript复制
        // 动画循环
        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 修改设置时,更新星星。
  • 从场景中移除旧的星星,创建新的星星,并将其添加到场景中。
代码语言:javascript复制
     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>

0 人点赞