關於從入門 three-js 到做出 3d 地球這件事
開篇介紹
如果你沒接觸過 3d 可視化技術, 你也許會認爲可視化非常難, 光是一個物體的陰影要如何計算就相當複雜, 但是告訴你個好消息, 陰影的計算都是集成好的, 而我們只要設置好光源的位置, 繪製好物體就可以了, 真的沒有想象中那麼複雜, 本文面向有前端基礎, 但零可視化基礎的同學, 我會從最基礎的入門知識說起。
學習可視化方面的技術會讓我們對計算機, 對前端技術有更深的理解, 還可以做出更多有趣味的東西來, 本文是我踩了好多坑後總結出來的, 我更清楚一個初入門的小白哪裏不懂。
three.js
是 webgl
的第三方庫, 它更適合不太複雜的可視化項目, 而我們要做的 3d 地球項目使用它來做會更簡單, 所以選擇了它, 放心後面也會說webgl
相關知識 。
當前效果如下:
一. 關於此係列文章
-
自食其力:
不管是在公司還是網上都有類似的庫, 但是當遇到 bug 或是缺少功能的情況時就會很麻煩, 例如我們公司的 FGL 庫 (一個內網繪製 3d 景象的技術), 它官網上的例子很多都是錯的, 使用起來也是一堆問題, 比如無法精準選擇某個國家, 點擊事件消融等 bug。還比如說Echarts
的地球, 它太注重真實感並且用起來有點卡, 以及交互做的不太好。 -
直指核心:
去年我通過看書、看文章、看視頻認真的學習three.js
, 並做出了 3d 地球這個項目, 而這個系列文章將會直指做出 3d 地圖的核心知識, 儘量不隨意擴散知識面。 -
更好入門:
網上的教學文章千篇一律, 點進去閱讀完感覺其對於一個three.js
零基礎的同學來說都不太好懂, 教學視頻裏的知識點太廣泛, 事無鉅細的羅列, 而這個系列文章將更突出繪製 3d 地球這個重點。 -
同道中人:
我學習three.js
就是爲了做出 3d 地球, 期間走了不少彎路, 被某些問題卡了很久, 所以我更懂一個剛入門的人困惑的點在哪裏。 -
專注vue:
市面上較少專門針對vue
做到開箱即用的 3d 地球插件, 而我們就要編寫這樣一款產品。 -
不斷學習:
編寫文章也是我提高自己能力的一種方法, 死磕每個知識點讓自己的理解更上一層樓。
二. 任務目標
-
入門
three.js
技術。 -
繪製出 3d 地球。
-
做成專門
vue
使用的庫。 -
後期也會介紹
着色器
的概念與基本的使用技巧。 -
會介紹少量
webgl
的相關用法, 並且會有部分數學知識。
三. 文章主線劇情與支線任務
-
主線劇情: 圍繞着如何做出 3d 地球, 這部分在 vue 工程裏面進行。
-
支線任務: 每個分散的知識點, 可能與 3d 地球沒關係, 但是它能幫助我們更好的理解 3d 技術, 而這些知識點我就不在 vue 項目裏面演示了, 會單獨創建一個 html 文件來演示說明。
四. 理解座標系: 彆着急寫代碼先有基本模型
像繪製圖形這類技術, 最基本的概念就座標系, 下圖是二維座標系
, 我們的故事就從這個傢伙開始。
我們用(0, 0)
表示座標的中心點, 繪製一條起點爲中心點長度爲 1 的線段可以使用 (0, 0) (1, 0)
這兩個點相連表示。
關於向量的概念後面需要用數學知識的時候再介紹, 前幾篇文章就越通俗越好。
在three.js
中我們要打交道的就是下面這位三維座標系
他的座標原點就是(0, 0, 0)
, 繪製一條起點爲中心點的長度爲 1 的線段可以是 (0, 0, 0) (1, 0, 0)
。
這裏要記住, three.js
裏面設置的默認座標系就是這種形式x向右, y向上, z向前
, 之所以說是默是因爲它可以修改。
上圖中, 觀看這個三維座標系的目光其實是在斜上方, 正常情況下在我們開發的時候z軸
是正對着我們的眼睛的, 所以你只能看到z軸
是一個點,
在開發與學習的時候, 最好先把座標系繪製到頁面上, 方便我們更好的繪製。
五. 相機的概念
假設現在我們的正前方有一個三維座標系
的全息投影, 那麼此時你的眼睛就相當於一架相機, 你看到的 座標系
景象取決於你站的位置。
在three.js
中就有這樣一個對象, 他就是負責從哪個角度觀察我們繪製的 3d 世界, 也就是相機
這個概念的由來。
相機分爲兩種, 正投影相機和透視投影相機, 正投影相機就是你站的多遠你看到的物體的大小都不變, 透視投影相機就是物體會近大遠小
, 下面是張引用圖 (圖片來自網絡)。
正投影相機可以用在工程製圖
上, 或者可以做一些視覺欺騙小遊戲。
本文主要目的是繪製 3d 地球所以主要使用透視投影相機
六. 繪製座標系, 安放攝像機 (代碼安排上)
引入three.js
, 可以把包下載到本地, 也可以直接獲取在 cdn 上的資源, 引入之後全局會出現THREE
對象, 我們就可以開始編程之旅了。
<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script>
一個普普通通的 html 空文件的 script 標籤裏面, 發生着這樣的故事: 讓我們逐句解析
第一步: 創建場景, 也就是虛擬的空間
我們之後繪製的3d物體
都要放入這個空間裏面, 你可以把它當做一個鴻蒙空間神器, 裏面有一個小世界, 而我們是掌控者 (很中二)。
const scene = new THREE.Scene();
第二步: 創建相機
相機的概念上面講述過了, PerspectiveCamera
這個類就是透視投影相機
, 我們來逐個攻破他參數的意思。
-
35
:視角
也就是我們左眼與右眼可以看到的橫向角度, 其越小物體則越大, 因爲目光變狹窄會突出物體, 你可以做一個實驗, 聚精會神的盯着看一個物體, 你就會發現此時你左右兩邊本來靠餘光可以看到的物體你現在看不清, 這個就是你的視角變小了, 變小視角還可以使目標物體比例變大, 我們知道這些就夠理解這個數字了, 後期可以利用這個原理做一些令人驚訝的動畫特效。 -
window.innerWidth / window.innerHeight
: 縱橫比寬/高
, 這裏寬高不會去寫px
這種單位, 座標系裏面是一種抽象的長度單位, 所以要告訴瀏覽器咱們當前顯示圖像的區域的寬高比例 (可以當它是百分比佈局, 就像我們寫 css 佈局時使用vh
vw
爲單位)。 -
1
:近平面
, 簡單理解就是當一個圖像
距離相機
的距離小於 1 的時候, 就不顯示這個圖像了。 -
1000
:遠平面
, 簡單理解就是當一個圖像
距離相機
的距離大於 1000 的時候, 就不顯示這個圖像了。 -
camera.position.z = 10;
相機的座標不設置的話, 默認就是 (0, 0, 0) 座標原點, 這樣類似腦袋在座標軸原點上看座標軸, 所以這裏要設置距離座標中心有一定距離, 也就是遠距離觀察這個座標系。
const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 10;
-
無聊的知識: 我們在玩
3d遊戲
的時候, 是不是有時候與另一個遊戲人物距離太近了就會出現人物中空
的效果, 這些很可能就是他的某些部分距離你相機的距離, 小於了近平面
的距離導致的。 -
物體距離眼睛越近越大, 越遠越小, 因爲一個物品無限大與無限遠沒有意義, 顯示起來浪費性能, 所以纔會設置近平面與遠平面。
第三步: 生成渲染實例
-
WebGLRenderer
生成一個渲染實例, 用來渲染我們所有的 3d 效果。 -
setSize
設置場景的寬高。 -
setClearColor
設置背景色, 這個背景色不是平面的, 是全方位的, 你可以想想成你在一個屋子裏, 這個顏色就是屋子牆壁、地板、天花板的顏色 (.5 是透明度)。 -
renderer.domElement
生成的渲染的實例, 這個要放到對應的 dom 容器裏面 (是個 canvas 標籤)。
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x00FFFF, .5)
document.body.appendChild(renderer.domElement);
-
知識點:
setClearColor
不寫就是黑色 -
知識點:
setClearColor
可以直接寫 "red" 這種, 不用必須 16 進制。
第四步: 插入座標系實例
-
AxisHelper
: 用於生成輔助座標實例,2
代表這個座標系的長度, 因爲我們不一定需要多長的輔助線。 -
scene
: 老朋友場景
, 它的add
方法就是把某某某加入到場景中來。
const axisHelper = new THREE.AxisHelper(2)
scene.add(axisHelper)
第五步: 渲染出來
- 第一個參數是
場景
, 第二個參數是相機
。
renderer.render(scene, camera);
下面是效果圖, z 軸正對着我們所以看不到:
在斜上方看到是如下的效果, 之後的章節會說如何調整相機的位置與角度
完整的代碼如下
<html>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 10;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x00FFFF, .5)
document.body.appendChild(renderer.domElement);
const axisHelper = new THREE.AxisHelper(2)
scene.add(axisHelper)
renderer.render(scene, camera);
</script>
</body>
</html>
七. 第一個立方體
不畫一個立方體感覺對不起 第一篇
這個題目, 要注意了在three.js
中你可以理解爲繪製一個幾何體需要兩部分, 一個是幾何體
本身, 比如這個幾何體的長寬高, 另一個就是材質
可以簡單理解爲表面的顏色樣式。 geometry
這個單詞我們會經常打交道的, 來一起記下它吧。
BoxGeometry
長方體
const geometry = new THREE.BoxGeometry(1, 2, 3);
-
1:
'長', 也可以理解爲在不設置座標的時候在 x 軸上的長度。 -
2:
'高', 也可以理解爲在不設置座標的時候在 y 軸上的長度。 -
3:
'寬', 也可以理解爲在不設置座標的時候在 z 軸上的長度。
new 出來的實例上面會有這個幾何體的點的信息, 面的信息等等, 這個後面再詳細說這次主要入門。
MeshBasicMaterial
材質
顏色與上面設置setClearColor
一樣, 什麼寫法都行的, 下面是我設置了一個紅色的材質。const material = new THREE.MeshBasicMaterial({ color: 'red' });
生成'網格' Mesh
const cube = new THREE.Mesh(geometry, material);
網格上含有位置信息、旋轉信息、縮放信息等等, 他需要用幾何體
與材質
兩個參數, 但其實並不像網上說的必須要有材質, 不傳材質也能顯示。
放入場景
也就是場景對象scene
本身有個add
方法。scene.add(cube);
右上方視角
放入場景的幾種方式
1: 我直接放入geometry``scene.add(geometry);
會報錯了, 可以理解爲不是網格對象所以報錯了。以後遇到這類報錯一定要考慮類型問題。
2: 未設置材質
const cube = new THREE.Mesh(geometry);
scene.add(cube);
白白的一片, 並且控制檯沒有報錯。
八. 全部代碼
<html>
<body>
<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r122/three.min.js"></script>
<script src="./utils/OrbitControls.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 10;
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x00FFFF, .5)
document.body.appendChild(renderer.domElement);
const axisHelper = new THREE.AxisHelper(2)
scene.add(axisHelper)
const geometry = new THREE.BoxGeometry(1, 2, 3);
const material = new THREE.MeshBasicMaterial({ color: 'red' });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
renderer.render(scene, camera);
</script>
</body>
</html>
第一篇寫的內容並不多, 等基本知識儲備夠了就可以開始編寫3d地球
了, 那裏將會很有意思。希望與你一起進步。
轉自:lulu_up
https://segmentfault.com/a/1190000039647481
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/ku4ld4dyyHaEdvxd-FyOBg