Skip to content

Threejs实战篇-模拟太阳、地球、月亮的运动

初始化基础场景

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body,
        html {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            overflow: hidden;
        }
        .container{
            height: 100vh;
            width: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        #c2d {
            /* width: 100%;
            height: 100vh; */
            margin: 0 auto;
        }
    </style>
</head>

<body>
    <div class="container">
        <canvas id="c2d" class="c2d"  width="2000" height="1000"></canvas>
    </div>

    <script type="module">
        /**
        * 本章节内容
        * 学习threejs中的常用几何体以及常用材质
        **/
        // 引入官网提供的地址
        import * as THREE from "../libs/three.module.js"
        import { OrbitControls } from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/controls/OrbitControls.js'

        let renderer = null;

        let camera = null;

        let scene = null;

        let sunMesh = null;

        let controls = null;

        let canvas = null;

        let loader = null;

        // 初始化渲染器
        const initRenderer = () => {
            canvas = document.querySelector("#c2d")

            renderer = new THREE.WebGLRenderer({ canvas, antialias: true })

            renderer.setClearColor(0xf5f5f5, 1)

        }

        // 初始化相机
        const initCamera = () => {
            // 视野范围
            const fov = 40

            // 相机的宽高比,画布的宽高比
            const aspect = document.body.clientWidth / document.body.clientHeight

            // 近平面
            const near = 0.1

            // 远平面
            const far = 1000

            // 透视投影相机
            camera = new THREE.PerspectiveCamera(fov, aspect, near, far)

            // 相机位置
            camera.position.set(0, 0, 50)
            camera.lookAt(0, 0, 0) // 相机朝向
        }

        // 初始化场景
        const initScene = () => {
            scene = new THREE.Scene()
        }

        // 初始化灯光
        const initLight = () => {
            const color = 0xffffff
            const intensity = 1
            // 创建光源
            const light = new THREE.PointLight(color, intensity)
            // 光源 加入场景
            scene.add(light)
        }


        // 初始化OrbitControls相机控件
        const initOrbitControls = () => {
            controls = new OrbitControls(camera, canvas)
            controls.update()
        }

        // 初始化网格线和物体
        const initMesh = () => {
            // 纹理加载器
            loader = new THREE.TextureLoader()
        }

        // 初始化场景中的背景颜色
        const initSceneBackground = () => {
            const bgTexture = loader.load("../imgs/4.jpg")
            scene.background = bgTexture
        }

        // 场景和相机放入渲染器中
        const render = (time = 1) => {
            time *= 0.001

            // 加载渲染器
            renderer.render(scene, camera)

            // 开始动画
            requestAnimationFrame(render)

        }

        initScene()
        initRenderer()
        initCamera()
        initOrbitControls()
        initLight()
        initMesh()
        initSceneBackground()
        // 开始渲染
        requestAnimationFrame(render)

    </script>

</body>

</html>

设置背景

添加星空背景,通过TextureLoader将图片转化为纹理

js
        // 初始化场景中的背景颜色
        const initSceneBackground = () => {
            const bgTexture = loader.load("../imgs/4.jpg")
            scene.background = bgTexture
        }

添加太阳和地球

js
        // 初始化网格线和物体
        const initMesh = () => {
            // 纹理加载器
            loader = new THREE.TextureLoader()

            // 添加太阳
            const radius = 2 // 半径
            const widthSegments = 32 // 宽度分段数
            const heightSegments = 32 // 宽度分段数
            // 创建球体
            const sphereGeometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments)

            // 加载太阳贴图
            const sunTexture = loader.load("../imgs/1.jpg", textture => {
                // 创建太阳材质
                const sunMaterial = new THREE.MeshBasicMaterial({ map: textture })
                // 创建太阳网格对象
                sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial)

                // 网格分别在x,y,z轴上面放大的倍数
                sunMesh.scale.set(3, 3, 3)

                obejects.push(sunMesh)
                scene.add(sunMesh)


                // 添加地球
                const earthTexture = loader.load("../imgs/3.jpg", textture2 => {
                    // 创建地球材质
                    const earthMaterial = new THREE.MeshPhongMaterial({ map: textture2 })
                    // 创建地球网格
                    earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial)

                    earthMesh.position.x = 20

                    scene.add(earthMesh)
                    obejects.push(earthMesh)

                  
                })
            })

        }

        // 场景和相机放入渲染器中
        const render = (time = 1) => {
            time *= 0.001

            // 加载渲染器
            renderer.render(scene, camera)

            obejects.forEach(k => {
                k.rotation.y = time
            })

            // 开始动画
            requestAnimationFrame(render)

        }

此时已经添加了地球和太阳,并且它们都已实现了自转,下一步需要实现地球绕太阳转动

地球绕太阳转动

要实现地球绕太阳运动,可以将太阳和地球都添加作为一个网格组,此时太阳在网格组的正中心,设置地球x轴定位在网格组20个单位

js
        // 初始化网格线和物体
        const initMesh = () => {
            // 纹理加载器
            loader = new THREE.TextureLoader()

            // 添加太阳
            const radius = 2 // 半径
            const widthSegments = 32 // 宽度分段数
            const heightSegments = 32 // 宽度分段数
            // 创建球体
            const sphereGeometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments)

            // 加载太阳贴图
            const sunTexture = loader.load("../imgs/1.jpg", textture => {
                // 创建太阳材质
                const sunMaterial = new THREE.MeshBasicMaterial({ map: textture })
                // 创建太阳网格对象
                sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial)

                // 网格分别在x,y,z轴上面放大的倍数
                sunMesh.scale.set(3, 3, 3)

                // obejects.push(sunMesh)
                // scene.add(sunMesh)


                // 添加地球
                const earthTexture = loader.load("../imgs/3.jpg", textture2 => {
                    // 创建地球材质
                    const earthMaterial = new THREE.MeshPhongMaterial({ map: textture2 })
                    // 创建地球网格
                    earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial)

                    earthMesh.position.x = 20

                    // scene.add(earthMesh)
                    obejects.push(earthMesh)

                    const solarSystemGroup = new THREE.Group()
                    solarSystemGroup.add(sunMesh)
                    solarSystemGroup.add(earthMesh)

                    scene.add(solarSystemGroup)
                    obejects.push(solarSystemGroup)
                })
            })
        }

这样既可实现地球绕太阳公转,也可以实现地球自转,月亮是以地球为中心点,所以实现方式是跟太阳系的实现方式类似的

完整代码

html
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        body,
        html {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }

        body {
            overflow: hidden;
        }

        .container {
            height: 100vh;
            width: 100%;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        #c2d {
            /* width: 100%;
            height: 100vh; */
            margin: 0 auto;
        }
    </style>
</head>

<body>
    <div class="container">
        <canvas id="c2d" class="c2d" width="2000" height="1000"></canvas>
    </div>

    <script type="module">
        /**
        * 本章节内容
        * 学习threejs中的常用几何体以及常用材质
        **/
        // 引入官网提供的地址
        import * as THREE from "../libs/three.module.js"
        import { OrbitControls } from 'https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/controls/OrbitControls.js'

        let renderer = null;

        let camera = null;

        let scene = null;

        let sunMesh = null;

        let controls = null;

        let canvas = null;

        let loader = null;

        let earthMesh = null;


        // 添加到该数组中的3D对象渲染的时候遍历循环
        const obejects = []

        // 初始化渲染器
        const initRenderer = () => {
            canvas = document.querySelector("#c2d")

            renderer = new THREE.WebGLRenderer({ canvas, antialias: true })

            renderer.setClearColor(0xf5f5f5, 1)

        }

        // 初始化相机
        const initCamera = () => {
            // 视野范围
            const fov = 40

            // 相机的宽高比,画布的宽高比
            const aspect = document.body.clientWidth / document.body.clientHeight

            // 近平面
            const near = 0.1

            // 远平面
            const far = 1000

            // 透视投影相机
            camera = new THREE.PerspectiveCamera(fov, aspect, near, far)

            // 相机位置
            camera.position.set(0, 0, 50)
            camera.lookAt(0, 0, 0) // 相机朝向
        }

        // 初始化场景
        const initScene = () => {
            scene = new THREE.Scene()
        }

        // 初始化灯光
        const initLight = () => {
            const color = 0xffffff
            const intensity = 1
            // 创建光源
            const light = new THREE.PointLight(color, intensity)
            // 光源 加入场景
            scene.add(light)
        }


        // 初始化OrbitControls相机控件
        const initOrbitControls = () => {
            controls = new OrbitControls(camera, canvas)
            controls.update()
        }

        // 初始化网格线和物体
        const initMesh = () => {
            // 纹理加载器
            loader = new THREE.TextureLoader()

            // 添加太阳
            const radius = 2 // 半径
            const widthSegments = 32 // 宽度分段数
            const heightSegments = 32 // 宽度分段数
            // 创建球体
            const sphereGeometry = new THREE.SphereGeometry(radius, widthSegments, heightSegments)

            // 加载太阳贴图
            const sunTexture = loader.load("../imgs/1.jpg", textture => {
                // 创建太阳材质
                const sunMaterial = new THREE.MeshBasicMaterial({ map: textture })
                // 创建太阳网格对象
                sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial)

                // 网格分别在x,y,z轴上面放大的倍数
                sunMesh.scale.set(3, 3, 3)


                // 加载地球贴图
                const earthTexture = loader.load("../imgs/3.jpg", textture2 => {

                    // 加载月球贴图
                    const monthTexture = loader.load("../imgs/2.jpg", textture3 => {
                        // 创建地球材质
                        const earthMaterial = new THREE.MeshPhongMaterial({ map: textture2 })
                        // 创建地球网格
                        earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial)

                        // 添加地月系
                        const landOrbit = new THREE.Group()
                        // 地月系相对太阳x设置20个单位
                        landOrbit.position.x = 20
                        // 将地球设置为地月系的中心点
                        landOrbit.add(earthMesh)
                        // 地球自转
                        obejects.push(earthMesh)
                        
                        // 创建月球材质
                        const monthMaterial = new THREE.MeshPhongMaterial({ map:textture3 })
                        // 创建月球网格
                        const monthMesh = new THREE.Mesh(sphereGeometry,monthMaterial)
                        // 设置月球相对地月系中心点也就是地球x偏移10个单位
                        monthMesh.position.x = 3
                        // 月球比地球小,缩小体积
                        monthMesh.scale.set(0.5,0.5,0.5)
                        // 添加月球网格到地月系网格组中
                        landOrbit.add(monthMesh)
                        // 地月系自身开始自转,以地球为中心点
                        obejects.push(landOrbit)

                        // 创建太阳系网格
                        const solarSystemGroup = new THREE.Group()
                        // 添加太阳网格,以太阳网格为中心
                        solarSystemGroup.add(sunMesh)
                        // 添加地月系,地月系距离太阳x设置20个单位
                        solarSystemGroup.add(landOrbit)

                        scene.add(solarSystemGroup)
                        // 太阳系自转,以太阳为中心点
                        obejects.push(solarSystemGroup)
                    })

                })
            })
        }

        // 初始化场景中的背景颜色
        const initSceneBackground = () => {
            const bgTexture = loader.load("../imgs/4.jpg")
            scene.background = bgTexture
        }

        // 场景和相机放入渲染器中
        const render = (time = 1) => {
            time *= 0.001

            // 加载渲染器
            renderer.render(scene, camera)

            obejects.forEach(k => {
                k.rotation.y = time
            })

            // 开始动画
            requestAnimationFrame(render)

        }

        initScene()
        initRenderer()
        initCamera()
        initOrbitControls()
        initLight()
        initMesh()
        initSceneBackground()
        // 开始渲染
        requestAnimationFrame(render)

    </script>

</body>

</html>

效果展示

上次更新于: