昨天先跑官方的範例程式碼,驗證環境沒問題後就結束。
當時只是把範例複製貼上,確定方塊會動,但很多東西都還沒研究
今天就來搞懂一番吧~
本日目標:
先來回顧昨天的 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 | 電影呈現,把拍到的畫面一幀一幀投到螢幕上 | 把畫面實際畫在網頁上 |
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
四個數字依序是 視野角度(FOV)、寬高比、near、far。
1. FOV(視野角度,75)
單位是度,是垂直方向能看到的範圍。可以想成鏡頭的焦段:
我調整程式碼讓畫面分別呈現 FOV 40, 70 和 100,可以看到 FOV 40 的方塊最大,100 最小
2. 寬高比(window.innerWidth / window.innerHeight)
視窗寬度除以高度。官方說要用元素的寬除以高,否則顛倒的話畫面會有被壓扁的感覺;我故意把昨天的範例改成 window.innerHeight / window.innerWidth,方塊整個變超胖 XD
3. near 與 far(0.1 與 1000)
相機能看到的距離範圍。官方的說法是:比 near 更近或比 far 更遠的物體不會被繪製。
想像戴了一副有能見度限制的眼鏡:貼在鏡頭前 0.1 以內的東西看不到,超過 1000 遠的東西也看不到。
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
setSize:決定畫布多大renderer.domElement:Renderer 產生的畫布,要用 appendChild 放進網頁,畫面才會出現,不然畫了也看不到昨天的方塊由兩個零件組成:
BoxGeometry 決定它是個方塊MeshBasicMaterial 決定它的顏色和質感兩者合起來才是一個 Mesh(網格物件),放進 Scene 才看得到。
renderer.setAnimationLoop(animate);
這行是告訴瀏覽器:每一幀畫面更新時,都呼叫 animate 一次,並自動把經過的時間(毫秒)傳進去。昨天方塊會轉,是因為 animate 裡每一幀都在改它的旋轉角度。
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) // 載入失敗
);
有兩個新手容易卡住的地方:
scene.add 要寫在載入成功的 callback,也就是上方例子 load() 的第二個參數。我第一次載入後,畫面只看到公園的一部分。

原因是我把相機放在離原點很近的位置,而公園模型是真實尺寸,很大,等於鏡頭貼在模型上。
解法是不要猜相機位置,讓程式自己算。這裡用到 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(); // 改了相機參數,要通知它重新計算
調整後的資訊長這樣,可以看到模型全貌了

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