iT邦幫忙

2026 iThome 鐵人賽

DAY 14
0
佛心分享-IT 人自學之術

從空拍到3D展示系列 第 14 篇

Day 14. 拆解範例程式碼 + 把獅山公園載進 Three.js

  • 分享至 

  • xImage
  •  

昨天先跑官方的範例程式碼,驗證環境沒問題後就結束。
當時只是把範例複製貼上,確定方塊會動,但很多東西都還沒研究

今天就來搞懂一番吧~

本日目標:

  1. 把昨天的程式碼拆開,弄清楚每一段在做什麼
  2. 把方塊換成真正的主角:獅山公園的 3D 模型

一、拆解昨天的程式碼

先來回顧昨天的 main.js 主要內容

const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

const geometry = new THREE.BoxGeometry( 1, 1, 1 );
const material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );

const cube = new THREE.Mesh(geometry, material);
scene.add(cube);

camera.position.z = 5;

function animate(time) {
    cube.rotation.x = time / 2000;
    cube.rotation.y = time / 1000; 
    renderer.render(scene, camera);
}
renderer.setAnimationLoop(animate);

Three.js 要顯示任何東西,官方說需要三樣:Scene(場景)、Camera(相機)、Renderer(渲染器)。可以想成拍電影:

角色 電影的比喻 程式裡的角色
Scene 攝影棚,所有道具都擺在裡面 放物件和燈光的容器
Camera 攝影機,決定從哪裡拍 決定看到什麼畫面
Renderer 電影呈現,把拍到的畫面一幀一幀投到螢幕上 把畫面實際畫在網頁上

Camera 的四個參數

const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

四個數字依序是 視野角度(FOV)、寬高比、near、far。

1. FOV(視野角度,75)

單位是度,是垂直方向能看到的範圍。可以想成鏡頭的焦段:

  • 數字大(例如 100):像廣角鏡頭,看到的範圍寬,物體看起來比較小
  • 數字小(例如 30):像望遠鏡頭,看到的範圍窄,物體看起來比較大

我調整程式碼讓畫面分別呈現 FOV 40, 70 和 100,可以看到 FOV 40 的方塊最大,100 最小
https://ithelp.ithome.com.tw/upload/images/20260928/20112674BKnbqTTy9e.png

2. 寬高比(window.innerWidth / window.innerHeight)

視窗寬度除以高度。官方說要用元素的寬除以高,否則顛倒的話畫面會有被壓扁的感覺;我故意把昨天的範例改成 window.innerHeight / window.innerWidth,方塊整個變超胖 XD
https://ithelp.ithome.com.tw/upload/images/20260928/20112674UCxnC9ZkTX.png

3. near 與 far(0.1 與 1000)

相機能看到的距離範圍。官方的說法是:比 near 更近或比 far 更遠的物體不會被繪製。
想像戴了一副有能見度限制的眼鏡:貼在鏡頭前 0.1 以內的東西看不到,超過 1000 遠的東西也看不到。

Renderer

const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
  • setSize:決定畫布多大
  • renderer.domElement:Renderer 產生的畫布,要用 appendChild 放進網頁,畫面才會出現,不然畫了也看不到

Mesh = 形狀 + 材質

昨天的方塊由兩個零件組成:

  • Geometry(形狀):BoxGeometry 決定它是個方塊
  • Material(材質):MeshBasicMaterial 決定它的顏色和質感

兩者合起來才是一個 Mesh(網格物件),放進 Scene 才看得到。

animate:讓畫面持續更新

renderer.setAnimationLoop(animate);

這行是告訴瀏覽器:每一幀畫面更新時,都呼叫 animate 一次,並自動把經過的時間(毫秒)傳進去。昨天方塊會轉,是因為 animate 裡每一幀都在改它的旋轉角度。


二、把方塊換成獅山公園:GLTFLoader

Three.js 本身讀不懂 .glb 檔案,需要 GLTFLoader 這個載入器當翻譯。它是額外的 addon,要另外 import。昨天 html 檔裡的 importmap 已經有 three/addons/ 這個路徑,所以直接引入即可:

import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

const loader = new GLTFLoader();
loader.load(
  'blender_output.glb',   // 檔案放在跟 index.html 同一個資料夾
  (gltf) => { scene.add(gltf.scene); },          // 載入成功
  undefined,                                      // 載入進度(先不用特別撰寫)
  (error) => console.error('載入失敗', error)     // 載入失敗
);

有兩個新手容易卡住的地方:

  1. 載入是「非同步」的:檔案有 49.9MB,讀取要花時間,程式不會停下來等它。所以要用 callback(載入完成後才執行的函式)處理後續動作,scene.add 要寫在載入成功的 callback,也就是上方例子 load() 的第二個參數。
  2. 要加進場景 官方 Object3D 文件寫 scene.add(),要放 Object3D。方塊 Mesh、燈光、相機、Group 都是它的一種,scene 本身也是,所以這邊要放的是 gltf.scene

三、載入後第一個畫面:鏡頭太近

我第一次載入後,畫面只看到公園的一部分。

https://ithelp.ithome.com.tw/upload/images/20260928/20112674o2y77hVHsS.png

原因是我把相機放在離原點很近的位置,而公園模型是真實尺寸,很大,等於鏡頭貼在模型上。
解法是不要猜相機位置,讓程式自己算。這裡用到 Box3(包圍盒):想像把模型裝進一個剛好放得下的透明箱子,就能知道模型的大小和中心:

const box = new THREE.Box3().setFromObject(gltf.scene);
const center = box.getCenter(new THREE.Vector3());   // 箱子的中心
const size = box.getSize(new THREE.Vector3());       // 箱子的長寬高
console.log('size', size);
console.log('center', center);

接著用這些數字擺相機:

const maxDim = Math.max(size.x, size.y, size.z);   // 最長的一邊
camera.position.set(center.x, center.y + maxDim, center.z + maxDim);   // 從斜上方看
camera.far = maxDim * 10;          // 遠界放大,避免遠處被裁掉
camera.updateProjectionMatrix();   // 改了相機參數,要通知它重新計算

調整後的資訊長這樣,可以看到模型全貌了

https://ithelp.ithome.com.tw/upload/images/20260928/20112674LtgiNrB9It.png


今天先到模型載入,並知道需要調整哪些參數,讓畫面看到的模型大小比較合理。
目前的模型是靜止狀態,明天來研究怎麼讓使用者可以在畫面中轉動模型、縮放模型,慢慢往使用者能尋找畫面彩蛋的方向前進!


上一篇
Day 13. 搭建 Three.js 開發環境
下一篇
Day15. Three.js 之燈光與控制器
系列文
從空拍到3D展示 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言