最近有three網友,問我要不要學習blender,其實我感覺學習一下也無妨,不過花大量時間精通,尚可不必,術業有專攻給別人留一條路吧,哈哈。那我我們就是用ThreeBSP和uv貼圖的知識來製作一個定製化的快遞櫃,先上圖,線上案例請點選部落格原文。
下面我們來講解一下這樣一個櫃子的製作。
1. 主角是一個JSON
這樣一個快遞櫃的核心是JSON資料的建立,有了jSON資料,我們就可以通過迴圈遍歷出櫃子,櫃門和uv對映關係。那面下面來看看我們的JSON資料(部分程式碼)。
var doorArray = [ [94, 10, -176, 196, false], [94, 10, -76, 196, false], [94, 10, 76, 196, false], [94, 10, 176, 196, false], [46, 15, 0, 186, false], [46, 60, 0, 147, false], [46, 21, 0, 105.5, true], [46, 10, 0, 89, true], [46, 10, 0, 78, true], [46, 20, 0, 62, true], [46, 20, 0, 41, true], [46, 20, 0, 20, true] ]
他是以一個陣列的形式表現的,每一個陣列代表一個櫃子資料,每一個陣列中的第一項為當前櫃子寬度,第二項為高度,第三項為中心x位置,第四項而中心y位置,第五項為櫃子是否能開啟(因為有的地方為操作皮膚等)。
2. ThreeBSP繪製櫃子的整體架構。
說完核心,我們在看看櫃子的整體框架。下面是櫃子的側面圖,通過側面圖我們可以很清晰的看出我們做了什麼
其實加的不多,就是在上面加了一個簷,下面加了兩個底座,還有就是在每個小快遞櫃中掏出一個洞。
我們看程式碼
var texture = new THREE.TextureLoader().load('/static/images/base/cabinet.jpg') let pubMate = new THREE.MeshNormalMaterial(); let frameGeom = new THREE.BoxGeometry(450, 200, 50); let frameMesh = new THREE.Mesh(frameGeom, pubMate); frameMesh.position.y = 106; let footShape = new THREE.Shape(); footShape.moveTo(0, 2); footShape.lineTo(8, -2); footShape.lineTo(8, -4); footShape.lineTo(0, -4); footShape.lineTo(0, 0); footShape.lineTo(-12, 0); footShape.lineTo(-12, 2); footShape.lineTo(0, 2); let footExtrudeSettings = { steps: 5, depth: 450, bevelEnabled: false }; let footGeom = new THREE.ExtrudeGeometry(footShape, footExtrudeSettings); let footMesh = new THREE.Mesh(footGeom, pubMate); let footMesh1 = footMesh.clone(); footMesh1.rotation.y = -Math.PI / 2; footMesh1.position.x = 225; footMesh1.position.y = 4; footMesh1.position.z = 25; let footMesh2 = footMesh.clone(); footMesh2.rotation.y = Math.PI / 2; footMesh2.position.x = -225; footMesh2.position.y = 4; footMesh2.position.z = -25; let headGeom = new THREE.BoxGeometry(450, 5, 20); let headMesh = new THREE.Mesh(headGeom, pubMate); headMesh.position.z = 23; headMesh.position.y = 206 - 2.5; let framebsp = new ThreeBSP(frameMesh); let foot1bsp = new ThreeBSP(footMesh1); let foot2bsp = new ThreeBSP(footMesh2); let headbsp = new ThreeBSP(headMesh); res = framebsp.union(foot1bsp).union(foot2bsp).union(headbsp); for(var i=0; i<doorArray.length; i++) { let geom = new THREE.BoxGeometry(doorArray[i][0]-1, doorArray[i][1]-1, 50); let mesh = new THREE.Mesh(geom, pubMate); mesh.position.set(doorArray[i][2], doorArray[i][3], 4) let meshbsp = new ThreeBSP(mesh); res = res.subtract(meshbsp); } let cabinetGeom = res.toGeometry(); let cabinetMate = new THREE.MeshPhongMaterial({color: 0xD8C513, specular: 0xD8C513, shininess: 10}); let cabinetMesh = new THREE.Mesh(cabinetGeom, cabinetMate); cabinetMesh.position.y = 106; scene.add(cabinetMesh);
這裡就是在框架BoxGeometry的基礎上加了兩個底座ExtrudeGeometry,和一個簷BoxGeometry,然後遍歷減去小櫃子。掌握好各自的空間位置,製作其實並不難。
3. 櫃子的統一貼圖
將一張圖作為貼圖,貼到所有的mesh上,如最上面圖的效果,因為上節課已經大致的說了關於uv的一點知識。
for(var i=0; i<doorArray.length; i++) { let a0 = doorArray[i][0]; let a1 = doorArray[i][1]; let a2 = doorArray[i][2]; let a3 = doorArray[i][3]; let x1 = ((a2 - a0 / 2) + 223) / 446; let x2 = ((a2 + a0 / 2) + 223) / 446; let y1 = ((a3 - a1 / 2) - 10) / 191; let y2 = ((a3 + a1 / 2) - 10) / 191; doorMesh.geometry.faceVertexUvs[0][8] = [new THREE.Vector2(x1, y2), new THREE.Vector2(x1, y1), new THREE.Vector2(x2, y2)]; doorMesh.geometry.faceVertexUvs[0][9] = [new THREE.Vector2(x1, y1), new THREE.Vector2(x2, y1), new THREE.Vector2(x2, y2)]; }
上面已經說過,這裡的a0是櫃子的寬,a1是櫃子的高,a2是櫃子中心x的座標值,a3是櫃子中心y的座標值。因為櫃子整體x的範圍是[-223, 223],y的範圍的[10, 201]。經過換算x1是紋理x座標的最小值,x2是紋理x座標的最大值,y1是紋理y座標的最小值,y2是紋理y座標的最大值,最後設定陣列索引為8和9小三角面的uv對映(因為我們要設定的面為長方體的左面,就是8和9控制的面)。
最後加上一點點開櫃子的動畫就大功告成了。
轉載請註明地址:郭先生的部落格