前言
筆者認為Three.js是一個偉大的架構,為什麼這樣說,因為它可以讓我們輕易創造三維世界,甚至好像筆者寫這遍教程,可以創造一個太陽系,在這個三維世界裡你就是創世主。哈哈!好像說得有點誇!!
三維太陽系完整效果
了解一些基本天文知識
學習創造這個三維太陽系之前先了解一下基本的天文知識:太陽系有“八大行星”,按照離太陽的距離從近到遠,它們依次為水星、金星、地球、火星、木星、土星、天王星、海王星。八大行星自轉方向多數也和公轉方向一緻。隻有金星和天王星兩個例外。金星自轉方向與公轉方向相反。而天王星則是在軌道上“橫滾”的。例如地球自轉一天是23.9小時,公轉一年有365.2天 ,而相鄰的火星自轉一天是24.6小時 公轉一年則有687天,其他行星也有不同的公轉和自轉資訊,有了這些資訊就可以定義一些基本規則
image.png
了解Three架構
Three的一些基本概念我在用最簡單方式打造Three.js 3D汽車展示廳一文也粗略介紹一下,為了讓同學們加深了解,筆者就相對于太陽系來比如一下
- 場景
相當于太陽系,宇宙中有無數星系,比如現在說的太陽系,後續還可以增加其他星系,那不是永遠都加不完的呀 o(╥﹏╥)oSence
- 相機
相當一枚哈勃天文望遠鏡Carma
- 幾何體
相當于太陽和八大行星Geometry
- 控制
相當”創世者“的你Controls
有了這幾個概念我們就建立一些函數一一對應
完整效果
螢幕錄制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)
}
複制
效果如下圖:
image.png
建立太陽和行星前先說說行星自轉同時公轉的規律
螢幕錄制2021-07-12 上午11.23.20.gif
旋轉方式:實作旋轉功能有三種方式
- 旋轉照相機
- 旋轉整個場景(Scene)
- 旋轉單個元素
因為我們這裡每個行星的自轉速度,公轉速度都不一樣。是以設定整體旋轉并不可行,是以要給每個元素設定不同的旋轉屬性。
行星需要讓它們圍繞着太陽轉,就要先給它們自身設定一個位置偏移。以水星為例:
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
添加紋理,太陽是品質是最大的,是以設定球體的時候數值是最大。 下圖是太陽的紋理貼圖
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)
})
}
複制
image.png
按照離太陽最近一個接一個建立
建立水星
水星離太陽最近,品質是所有行星中最小,是以球體數值也給一個最小的數值。 下圖水星紋理貼圖
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)
})
}
複制
image.png
建立金星
image.png
O(∩_∩)O哈哈~ 應該是下圖,這張才是金星行星的紋理貼圖,千萬不要用錯喲!!
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)
})
}
複制
image.png
地球
怎可以沒有我們的家園呢,這麼美麗的家園要好好保護它啊!!
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)
})
}
複制
image.png
火星、木星、土星、天王星、海王星
接下來的行星設定都是大同小異、隻是公轉、自轉、和行星大小的設定不同。
接着對應行星的紋理貼圖也一一發給大家
火星的紋理貼圖
mars.jpg
木星的紋理貼圖
jupiter.jpg
土星的紋理貼圖
saturn.jpg
天王星的紋理貼圖
uranus.jpg
海王星的紋理貼圖
neptune.jpg
最後
一個三維太陽系就創造出來啦,這個例子也是很适合剛入門three.js的同學,目的也是提高對三維的興趣,提高自身成就感。當然在這列子上我們還可以增加一些功能,比如定位标注一些行星的資訊,點選行星可以進入星球内部,利用天空盒子做一個VR全景效果,等等。另外小弟找這些行星紋理貼圖也不易,特别找金星的時候?,希望大家如果喜歡這篇文章能給個贊小弟,當鼓勵一下。以後小弟必定為大家創作更多好文,謝謝啦!!^_^
螢幕錄制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>
複制
image.png