天天看點

教你如何用Three.js創造一個三維太陽系

前言

筆者認為Three.js是一個偉大的架構,為什麼這樣說,因為它可以讓我們輕易創造三維世界,甚至好像筆者寫這遍教程,可以創造一個太陽系,在這個三維世界裡你就是創世主。哈哈!好像說得有點誇!!

三維太陽系完整效果

了解一些基本天文知識

學習創造這個三維太陽系之前先了解一下基本的天文知識:太陽系有“八大行星”,按照離太陽的距離從近到遠,它們依次為水星、金星、地球、火星、木星、土星、天王星、海王星。八大行星自轉方向多數也和公轉方向一緻。隻有金星和天王星兩個例外。金星自轉方向與公轉方向相反。而天王星則是在軌道上“橫滾”的。例如地球自轉一天是23.9小時,公轉一年有365.2天 ,而相鄰的火星自轉一天是24.6小時 公轉一年則有687天,其他行星也有不同的公轉和自轉資訊,有了這些資訊就可以定義一些基本規則

教你如何用Three.js創造一個三維太陽系

image.png

了解Three架構

Three的一些基本概念我在用最簡單方式打造Three.js 3D汽車展示廳一文也粗略介紹一下,為了讓同學們加深了解,筆者就相對于太陽系來比如一下

  1. 場景

    Sence

    相當于太陽系,宇宙中有無數星系,比如現在說的太陽系,後續還可以增加其他星系,那不是永遠都加不完的呀 o(╥﹏╥)o
  2. 相機

    Carma

    相當一枚哈勃天文望遠鏡
  3. 幾何體

    Geometry

    相當于太陽和八大行星
  4. 控制

    Controls

    相當”創世者“的你

有了這幾個概念我們就建立一些函數一一對應

完整效果

教你如何用Three.js創造一個三維太陽系

螢幕錄制2021-07-12 下午2.34.26.gif

進入教程

先引入Three 要用到的對象

import {
        Group,
        Mesh,
        MeshBasicMaterial,
        PerspectiveCamera,
        PointCloud,
        PointCloudMaterial,
        Scene,
        SphereGeometry,
        TextureLoader,
        Vector3,
        WebGLRenderer
    } from 'three'
     import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'           

複制

場景(太陽系),相機(哈勃天文望遠鏡),控制(創世主)

//場景
const setScene = () => {
        scene = new Scene()
        renderer = new WebGLRenderer({
            antialias: true,
        })
        renderer.setSize(innerWidth, innerHeight)
        document.querySelector('#planet').appendChild(renderer.domElement)
    }
//相機
const setCamera = () => {
        camera = new PerspectiveCamera(60, innerWidth / innerHeight, 1, 100000)
        camera.position.set(0, 500, 2000)
        camera.lookAt(scene.position)
    }
//控制    
 const setControls = () => {
        controls = new OrbitControls(camera, renderer.domElement)
    }                   

複制

建立一個太陽系的背景(繁星背景)

這個滿天星效果是太陽系的背景,運用到Three的粒子系統,行星密度可自行調整

const starForge = () => {
        const starQty = 10000
        const geometry = new SphereGeometry(10000, 100, 50)
        const materialOptions = {}
        const starStuff = new PointCloudMaterial(materialOptions)
        geometry.vertices = []
        for (let i = 0; i < starQty; i++) {
            let starVertex = new Vector3()
            starVertex.x = Math.random() * 20000 - 10000
            starVertex.y = Math.random() * 20000 - 10000
            starVertex.z = Math.random() * 20000 - 10000
            geometry.vertices.push(starVertex)
        }
        const stars = new PointCloud(geometry, starStuff)
        scene.add(stars)
    }           

複制

效果如下圖:

教你如何用Three.js創造一個三維太陽系

image.png

建立太陽和行星前先說說行星自轉同時公轉的規律

教你如何用Three.js創造一個三維太陽系

螢幕錄制2021-07-12 上午11.23.20.gif

旋轉方式:實作旋轉功能有三種方式

  1. 旋轉照相機
  2. 旋轉整個場景(Scene)
  3. 旋轉單個元素

因為我們這裡每個行星的自轉速度,公轉速度都不一樣。是以設定整體旋轉并不可行,是以要給每個元素設定不同的旋轉屬性。

行星需要讓它們圍繞着太陽轉,就要先給它們自身設定一個位置偏移。以水星為例:

mercury.position.x -= 300

,而此時設定

mercury.rotation.y

屬性,它就會實作自轉。因為它的Y軸位置已經改變了。

當我們移動了

mercury

時,

mercuryParent

的位置是沒有變的,自然它的Y軸也不會變,又因為

mercuryParent

包含了

mercury

,是以旋轉

mercuryParent

時,

mercury

也會繞着初始的預設Y軸旋轉。是以設定那麼多組,是為了實作每顆行星不同的速度和公轉的同時自轉。至于設定以下代碼數值就根據 行星自轉一天、公轉一年用多少時間來大概定義一下。

//設定公轉函數
const revolution = () => {
        mercuryParent.rotation.y += 0.015
        venusParent.rotation.y += 0.0065
        earthParent.rotation.y += 0.05
        marsParent.rotation.y += 0.03
        jupiterParent.rotation.y += 0.001
        saturnParent.rotation.y += 0.02
        uranusParent.rotation.y += 0.09
        neptuneParent.rotation.y += 0.001
    }
    //設定自轉函數
    const selfRotation = () => {
        sun.rotation.y += 0.004
        mercury.rotation.y += 0.002
        venus.rotation.y += 0.005
        earth.rotation.y += 0.01
        mars.rotation.y += 0.01
        jupiter.rotation.y += 0.08
        saturn.rotation.y += 1.5
        uranus.rotation.y += 1
        neptune.rotation.y += 0.1
    }           

複制

建立太陽和八大行星

建立星系用到幾何球體+紋理貼圖

首先介紹一下太陽如何創造,利用

SphereGeometry

建立球體,利用

MeshBasicMaterial

添加紋理,太陽是品質是最大的,是以設定球體的時候數值是最大。 下圖是太陽的紋理貼圖

教你如何用Three.js創造一個三維太陽系

sun.jpg

// 添加設定太陽
    let sun, sunParent
    const setSun = () => {
        sun = new Group()//建立一個組
        sunParent = new Group()
        scene.add(sunParent) //把組都添加到場景裡
        loader.load('src/assets/universe/sun.jpg', (texture) => {
            const geometry = new SphereGeometry(500, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            sun.add(mesh)//添加到組裡
            sunParent.add(sun)
        })
    }           

複制

教你如何用Three.js創造一個三維太陽系

image.png

按照離太陽最近一個接一個建立

建立水星

水星離太陽最近,品質是所有行星中最小,是以球體數值也給一個最小的數值。 下圖水星紋理貼圖

教你如何用Three.js創造一個三維太陽系

mercury.jpg

let mercury, mercuryParent
    const setMercury = () => {
        mercury = new Group()
        mercuryParent = new Group()
        scene.add(mercuryParent)
        loader.load('src/assets/universe/mercury.jpg', (texture) => {
            const geometry = new SphereGeometry(25, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            mercury.position.x -= 600
            mercury.add(mesh)//添加到組裡
            mercuryParent.add(mercury)
        })
    }           

複制

教你如何用Three.js創造一個三維太陽系

image.png

建立金星

教你如何用Three.js創造一個三維太陽系

image.png

O(∩_∩)O哈哈~ 應該是下圖,這張才是金星行星的紋理貼圖,千萬不要用錯喲!!

教你如何用Three.js創造一個三維太陽系

venus.jpg

let venus, venusParent
    const setVenus = () => {
        venus = new Group()//建立一個組
        venusParent = new Group()
        scene.add(venusParent)
        loader.load('src/assets/universe/venus.jpg', (texture) => {
            const geometry = new SphereGeometry(100, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            venus.position.x -= 700
            venus.add(mesh)//添加到組裡
            venusParent.add(venus)
        })
    }           

複制

教你如何用Three.js創造一個三維太陽系

image.png

地球

怎可以沒有我們的家園呢,這麼美麗的家園要好好保護它啊!!

教你如何用Three.js創造一個三維太陽系

earth.jpg

let earth, earthParent
    const setEarth = () => {
        earth = new Group()//建立一個組
        earthParent = new Group()
        scene.add(earthParent)
        loader.load('src/assets/universe/earth.jpg', (texture) => {
            const geometry = new SphereGeometry(100, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            earth.position.x -= 900
            earth.add(mesh)//添加到組裡
            earthParent.add(earth)
        })
    }           

複制

教你如何用Three.js創造一個三維太陽系

image.png

火星、木星、土星、天王星、海王星

接下來的行星設定都是大同小異、隻是公轉、自轉、和行星大小的設定不同。

接着對應行星的紋理貼圖也一一發給大家

火星的紋理貼圖

教你如何用Three.js創造一個三維太陽系

mars.jpg

木星的紋理貼圖

教你如何用Three.js創造一個三維太陽系

jupiter.jpg

土星的紋理貼圖

教你如何用Three.js創造一個三維太陽系

saturn.jpg

天王星的紋理貼圖

教你如何用Three.js創造一個三維太陽系

uranus.jpg

海王星的紋理貼圖

教你如何用Three.js創造一個三維太陽系

neptune.jpg

最後

一個三維太陽系就創造出來啦,這個例子也是很适合剛入門three.js的同學,目的也是提高對三維的興趣,提高自身成就感。當然在這列子上我們還可以增加一些功能,比如定位标注一些行星的資訊,點選行星可以進入星球内部,利用天空盒子做一個VR全景效果,等等。另外小弟找這些行星紋理貼圖也不易,特别找金星的時候?,希望大家如果喜歡這篇文章能給個贊小弟,當鼓勵一下。以後小弟必定為大家創作更多好文,謝謝啦!!^_^

教你如何用Three.js創造一個三維太陽系

螢幕錄制2021-07-12 下午2.34.26.gif

上完整代碼

<template>
    <div id="planet">

    </div>
</template>
<script setup>
    import {onMounted} from 'vue'
    import {
        Group,
        Mesh,
        MeshBasicMaterial,
        PerspectiveCamera,
        PointCloud,
        PointCloudMaterial,
        Scene,
        SphereGeometry,
        TextureLoader,
        Vector3,
        WebGLRenderer
    } from 'three'
    import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'

    const loader = new TextureLoader() //引入模型的loader執行個體
    let scene, camera, renderer, group, controls // 定義所有three執行個體變量

    // 建立場景
    const setScene = () => {
        scene = new Scene()
        renderer = new WebGLRenderer({
            antialias: true,
        })
        renderer.setSize(innerWidth, innerHeight)
        document.querySelector('#planet').appendChild(renderer.domElement)
    }

    // 建立相機
    const setCamera = () => {
        camera = new PerspectiveCamera(60, innerWidth / innerHeight, 1, 100000)
        camera.position.set(0, 500, 2000)
        camera.lookAt(scene.position)
    }

    // 設定模型控制
    const setControls = () => {
        controls = new OrbitControls(camera, renderer.domElement)
    }
    
    // 添加設定太陽
    let sun, sunParent
    const setSun = () => {
        sun = new Group()//建立一個組
        sunParent = new Group()
        scene.add(sunParent) //把組都添加到場景裡
        loader.load('src/assets/universe/sun.jpg', (texture) => {
            const geometry = new SphereGeometry(500, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            sun.add(mesh)//添加到組裡
            sunParent.add(sun)
        })
    }

    // 設定水星
    let mercury, mercuryParent
    const setMercury = () => {
        mercury = new Group()//建立一個組
        mercuryParent = new Group()
        scene.add(mercuryParent)
        loader.load('src/assets/universe/mercury.jpg', (texture) => {
            const geometry = new SphereGeometry(25, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            mercury.position.x -= 600
            mercury.add(mesh)//添加到組裡
            mercuryParent.add(mercury)
        })
    }

    //設定金星
    let venus, venusParent
    const setVenus = () => {
        venus = new Group()//建立一個組
        venusParent = new Group()
        scene.add(venusParent)
        loader.load('src/assets/universe/venus.jpg', (texture) => {
            const geometry = new SphereGeometry(100, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            venus.position.x -= 700
            venus.add(mesh)//添加到組裡
            venusParent.add(venus)
        })
    }
    //設定地球
    let earth, earthParent
    const setEarth = () => {
        earth = new Group()//建立一個組
        earthParent = new Group()
        scene.add(earthParent)
        loader.load('src/assets/universe/earth.jpg', (texture) => {
            const geometry = new SphereGeometry(100, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            earth.position.x -= 900
            earth.add(mesh)//添加到組裡
            earthParent.add(earth)
        })
    }
//設定火星
    let mars, marsParent
    const setMars = () => {
        mars = new Group()//建立一個組
        marsParent = new Group()
        scene.add(marsParent)
        loader.load('src/assets/universe/mars.jpg', (texture) => {
            const geometry = new SphereGeometry(85, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            mars.position.x -= 1200
            mars.add(mesh)//添加到組裡
            marsParent.add(mars)
        })
    }

    // 設定木星
    let jupiter, jupiterParent
    const setJupiter = () => {
        jupiter = new Group()//建立一個組
        jupiterParent = new Group()
        scene.add(jupiterParent)
        loader.load('src/assets/universe/jupiter.jpg', (texture) => {
            const geometry = new SphereGeometry(150, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            jupiter.position.x -= 1500
            jupiter.add(mesh)//添加到組裡
            jupiterParent.add(jupiter)
        })
    }

    // 設定土星
    let saturn, saturnParent
    const setSaturn = () => {
        saturn = new Group()//建立一個組
        saturnParent = new Group()
        scene.add(saturnParent)
        loader.load('src/assets/universe/saturn.jpg', (texture) => {
            const geometry = new SphereGeometry(120, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            saturn.position.x -= 1800
            saturn.add(mesh)//添加到組裡
            saturnParent.add(saturn)
        })
    }

    //設定天王星
    let uranus, uranusParent
    const setUranus = () => {
        uranus = new Group()
        uranusParent = new Group()
        scene.add(uranusParent)
        loader.load('src/assets/universe/uranus.jpg', (texture) => {
            const geometry = new SphereGeometry(50, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            uranus.position.x -= 2100
            uranus.add(mesh)//添加到組裡
            saturnParent.add(uranus)
        })
    }

    //設定海王星
    let neptune, neptuneParent
    const setNeptune = () => {
        neptune = new Group()
        neptuneParent = new Group()
        scene.add(neptuneParent)
        loader.load('src/assets/universe/neptune.jpg', (texture) => {
            const geometry = new SphereGeometry(50, 20, 20) //球體模型
            const material = new MeshBasicMaterial({map: texture}) //材質 将圖檔解構成THREE能了解的材質
            const mesh = new Mesh(geometry, material)  //網孔對象 第一個參數是幾何模型(結構),第二參數是材料(外觀)
            neptune.position.x -= 2300
            neptune.add(mesh)//添加到組裡
            neptuneParent.add(neptune)
        })
    }

//監聽浏覽器改變大小時重新渲染
    function onWindowResize() {
        const WIDTH = window.innerWidth,
            HEIGHT = window.innerHeight
        camera.aspect = WIDTH / HEIGHT
        camera.updateProjectionMatrix()
        renderer.setSize(WIDTH, HEIGHT)
    }

//設定公轉函數
    const revolution = () => {
        mercuryParent.rotation.y += 0.015
        venusParent.rotation.y += 0.0065
        earthParent.rotation.y += 0.05
        marsParent.rotation.y += 0.03
        jupiterParent.rotation.y += 0.01
        saturnParent.rotation.y += 0.02
        uranusParent.rotation.y += 0.09
        neptuneParent.rotation.y += 0.01
    }

    //設定自轉
    const selfRotation = () => {
        sun.rotation.y += 0.004
        mercury.rotation.y += 0.002
        venus.rotation.y += 0.005
        earth.rotation.y += 0.01
        mars.rotation.y += 0.01
        jupiter.rotation.y += 0.08
        saturn.rotation.y += 1.5
        uranus.rotation.y += 1
        neptune.rotation.y += 0.1
    }

    // 設定太陽系背景
    const starForge = () => {
        const starQty = 10000
        const geometry = new SphereGeometry(10000, 100, 50)
        const materialOptions = {}
        const starStuff = new PointCloudMaterial(materialOptions)
        geometry.vertices = []
        for (let i = 0; i < starQty; i++) {
            let starVertex = new Vector3()
            starVertex.x = Math.random() * 20000 - 10000
            starVertex.y = Math.random() * 20000 - 10000
            starVertex.z = Math.random() * 20000 - 10000
            geometry.vertices.push(starVertex)
        }
        const stars = new PointCloud(geometry, starStuff)
        scene.add(stars)
    }


    // 循環場景 、相機、 位置更新
    const loop = () => {
        requestAnimationFrame(loop)
        revolution()
        selfRotation()
        renderer.render(scene, camera)
        camera.lookAt(scene.position)
    }

  //初始化所有函數
    const init = () => {
        setScene() //設定場景
        setCamera() //設定相機
        setSun() // 設定太陽
        setMercury() //設定水星
        setVenus() //設定金星
        setEarth() // 地球
        setMars() //火星
        setJupiter() // 木星
        setSaturn() // 土星
        setUranus()// 天王星
        setNeptune()//海王星
        starForge()//設定滿天星背景
        setControls() //設定可旋轉控制
        loop() // 循環動畫
    }

    onMounted(init)
    window.addEventListener('resize', onWindowResize)

</script>           

複制

教你如何用Three.js創造一個三維太陽系

image.png