昨天我們在網頁上成功渲染出了一塊深色的 3D 地板,但作為一個開發中的地下城,我們需要能夠自由地從各個角度檢視場景。此外,純色的幾何體太過單調,今天我們要裝上「上帝視角控制器」,並載入真正的 16-bit 像素圖,將它完美地「拼貼」成一片 HD-2D 風格的復古廣場!
Three.js 提供了一個非常實用的工具 OrbitControls(軌道控制器),讓玩家能用滑鼠左鍵拖曳旋轉、右鍵平移、滾輪縮放。
main.js 檔案最上方匯入控制器:
import './style.css';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
接著在宣告 renderer 的下方,加入控制器的設定,並開啟滑順的阻尼慣性:
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
確保你的 public 資料夾中已經準備好一張 16x16 像素的無縫石磚圖,並命名為 floor.png。在 Three.js 中,我們使用 TextureLoader 來載入這張圖片。
關鍵點:WebGL 預設會開啟線性過濾來模糊圖片邊緣,這對 16-bit 像素藝術是致命傷!我們必須強制設定「最近鄰插值 (NearestFilter)」,保持像素的絕對銳利。
const textureLoader = new THREE.TextureLoader();
const floorTexture = textureLoader.load('/floor.png');
// 關閉線性模糊,保持 16-bit 像素的絕對銳利!
floorTexture.magFilter = THREE.NearestFilter;
如果直接把這張 16x16 的小圖貼到 10x10 的 3D 平面上,圖片會被嚴重拉伸。我們需要開啟「紋理重複平鋪」,讓這張小圖在平面上像貼磁磚一樣完美複製 100 次:
// 開啟水平與垂直的重複平鋪
floorTexture.wrapS = THREE.RepeatWrapping;
floorTexture.wrapT = THREE.RepeatWrapping;
// 設定水平與垂直各重複 10 次 (配合 PlaneGeometry 的大小)
floorTexture.repeat.set(10, 10);
將原本純色的材質改為使用剛設定好的貼圖 (map),並在渲染迴圈中持續更新控制器,讓阻尼效果生效:
const geometry = new THREE.PlaneGeometry(10, 10);
const material = new THREE.MeshBasicMaterial({
map: floorTexture, // 套用像素貼圖
side: THREE.DoubleSide
});
const floor = new THREE.Mesh(geometry, material);
floor.rotation.x = -Math.PI / 2;
scene.add(floor);
function animate() {
requestAnimationFrame(animate);
controls.update(); // 持續更新控制器
renderer.render(scene, camera);
}
為確保進度一致,這是完成 Day 4 後 src/main.js 的完整樣貌:
import './style.css';
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
// 1. 建立場景與攝影機
const scene = new THREE.Scene();
scene.background = new THREE.Color('#1a1a2e');
const sizes = { width: window.innerWidth, height: window.innerHeight };
const camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height, 0.1, 1000);
camera.position.set(0, 5, 10);
scene.add(camera);
// 2. 建立渲染器
const canvas = document.querySelector('#webgl-canvas');
const renderer = new THREE.WebGLRenderer({ canvas: canvas });
renderer.setSize(sizes.width, sizes.height);
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
// 3. 初始化 OrbitControls
const controls = new OrbitControls(camera, canvas);
controls.enableDamping = true;
// 4. 載入紋理、關閉模糊並設定重複拼貼
const textureLoader = new THREE.TextureLoader();
const floorTexture = textureLoader.load('/floor.png');
floorTexture.magFilter = THREE.NearestFilter;
floorTexture.wrapS = THREE.RepeatWrapping;
floorTexture.wrapT = THREE.RepeatWrapping;
floorTexture.repeat.set(10, 10);
// 5. 建立 3D 地板並加入場景
const geometry = new THREE.PlaneGeometry(10, 10);
const material = new THREE.MeshBasicMaterial({
map: floorTexture,
side: THREE.DoubleSide
});
const floor = new THREE.Mesh(geometry, material);
floor.rotation.x = -Math.PI / 2;
scene.add(floor);
// 6. 啟動渲染迴圈
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
存檔並切換到瀏覽器,用滑鼠拖曳感受一下自由視角,並欣賞那片由 100 塊小石磚完美接合而成的銳利像素廣場。這就是 HD-2D 開發最迷人的第一步!