依照上一篇 Three.js 3D Library (1) 基本介紹 介紹了製造 3D 圖形的基本元素,接下來進行實作吧!
座標系統
開始實作之前,必須先了解 Three.js 使用 右手座標系統 Right-Hand Coordinate 來定義 3D 空間:
X軸:向右為正方向,Y軸:向上為正方向,Z軸:向外為正方向
如果對此概念不熟悉也沒關係,Three.js 提供座標輔助:
const axesHelper = new THREE.AxesHelper(10); scene.add(axesHelper);
|
實作行星自轉畫面,步驟如下:
- 建立場景
- 建立相機
- 建立渲染器
- 建立物體
- 建立光源
- 渲染場景
以下為進階功能:
- 加入軌道控制器
- 打造 RWD 響應式畫面
建立場景
設定背景底色
const scene = new THREE.Scene(); scene.background = new THREE.Color(0x000000);
|
建立相機
使用透視相機,設定相機位置跟相機焦點
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.set(120, 120, 120); camera.lookAt(scene.position);
|
建立渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true }); renderer.setSize(window.innerWidth, window.innerHeight);
document.getElementById('container').appendChild(renderer.domElement);
|
建立物體
結構如下:
再次複習一下建立網格(Mesh)或粒子(Points)物體順序:
建立幾何體 → 建立材質 → 建立物體 → 將物體加入場景
行星體(網格物體)
分為球型體跟行星環,軸心旋轉(X軸-30度、Y軸-10度)
輔助座標
首先建立輔助座標,可以協助我們快速理解3D空間
const axesHelper = new THREE.AxesHelper(50); axesHelper.rotation.x = THREE.MathUtils.degToRad(-30); axesHelper.rotation.y = THREE.MathUtils.degToRad(-10); scene.add(axesHelper);
|
使用 THREE.MathUtils.degToRad()
運算函式,將角度(degrees)轉換成弧度(radians)
球型體
使用 IcosahedronGeometry(radius, detail)
建立多面體,詳細說明見官方文件
const sphereGeometry = new THREE.IcosahedronGeometry(20, 1);
const sphereMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x000000, specular: 0xffffff, shininess: 100, flatShading: true });
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
|
行星環
使用 RingGeometry()
建立圓環幾何體,詳細說明見官方文件
const RingGeometry = new THREE.RingGeometry(42, 28, 30, 30);
const RingMaterial = new THREE.MeshPhongMaterial({ color: 0xffffff, emissive: 0x000000, specular: 0xffffff, shininess: 100, flatShading: true, transparent: true, opacity: 0.7 });
const ring = new THREE.Mesh(RingGeometry, RingMaterial);
ring.rotation.x = THREE.MathUtils.degToRad(90);
|
組合行星體
使用 Group()
建立群組,軸心旋轉(同前面說明:X軸-30度、Y軸-10度)
const planet = new THREE.Group(); planet.add(sphere, ring); planet.rotation.x = THREE.MathUtils.degToRad(-30); planet.rotation.y = THREE.MathUtils.degToRad(-10);
scene.add(planet);
|
群星背景(粒子物體)
使用 BufferGeometry()
建立幾何體,可以減少 GPU 的消耗,詳細說明見官方文件
const geometry = new THREE.BufferGeometry(); const particleAmount = 2000; const vertices = []; for (let i = 0; i < particleAmount; i++) { const x = 2000 * Math.random() - 1000; const y = 2000 * Math.random() - 1000; const z = 2000 * Math.random() - 1000; vertices.push(x, y, z); } geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
const particleTexture = new THREE.TextureLoader().load('https://i.imgur.com/9TwBBHH.png'); const material = new THREE.PointsMaterial({ size: 5, sizeAttenuation: true, map: particleTexture, transparent: true, alphaTest: 0.5, color: 0xf3f3af });
const particles = new THREE.Points(geometry, material);
scene.add(particles);
|
建立光源
依序建立三種光源,讓光線看起來更自然:
- 環境光 AmbientLight
- 點光源 PointLight
- 聚光燈 SpotLight
環境光
const ambientLight = new THREE.AmbientLight(0x222222);
|
點光源
const pointLight = new THREE.PointLight(0x777777, 1, 0); pointLight.position.set(100, 100, 0);
|
聚光燈
const spotLight = new THREE.SpotLight(0xffffff, 3, 150, Math.PI / 6, 1, 1); spotLight.position.set(50, 100, -80); spotLight.castShadow = true; spotLight.shadow.mapSize.width = 1024; spotLight.shadow.mapSize.height = 1024; spotLight.shadow.camera.near = 10; spotLight.shadow.camera.far = 200; spotLight.shadow.focus = 1;
|
聚光燈輔助可以更明確地看到光線位置
const lightHelper = new THREE.SpotLightHelper(spotLight); scene.add(lightHelper);
|
最後將光源加入場景
scene.add(ambientLight, pointLight, spotLight);
|
渲染場景
搭配前一篇說明過的 動畫循環渲染 Render Loop 方法
function animate() { requestAnimationFrame(animate); planet.rotation.y += 0.01; renderer.render(scene, camera); } animate();
|
到這裡畫面就完成囉!👏👏👏
接著說明進階的功能
加入軌道控制器
軌道控制器 OrbitControls 讓使用者可以透過滑鼠或是觸控面板來縮放和移動相機,改變場景中的視角和位置,軌道圍繞著前面提到的 camera.lookAt()
相機焦點
軌道控制器也可以啟用自動旋轉功能,詳細請見官方文件
const control = new OrbitControls(camera, renderer.domElement);
|
打造 RWD 響應式畫面
監聽螢幕尺寸變化,讓畫面自適應
window.addEventListener('resize', () => { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); });
|
完整範例程式碼
參考資源:
https://ithelp.ithome.com.tw/users/20107572/ironman/1782
https://threejs.org/
https://medium.com/小彥彥的前端五四三/threejs-內建幾何模型-二-3df675ba8315
評論