最近在搗鼓 Three.js 相關, 以爲作一個微場景是一個很不錯的選擇,結果作下來發現效果還能夠。html
下面放幾張不一樣角度的效果圖git
配置較差的設備上預覽效果不是很理想, 待優化中
接下來大體說明一下實現過程github
首先你要清楚本身想實現的效果, 就須要一張效果圖, 而後才能根據效果圖來建模。
我在網上找到一張本身喜歡的風格的建築圖作參考,明確本身大概要實現的效果,但結果不必定要如出一轍。canvas
而後根據效果圖,畫出本身想要的模型的平面圖,方便模型中物體的精肯定位segmentfault
Three.js來構建一個相似的微場景, 仍是比較簡單的。基本上就是在 場景 中 打好燈光 和 攝像機位置, 而後往 場景 添加各類幾何圖形,經過幾個圖形和材質的組合,構建出不一樣的模型和場景。對開發者的空間想象力有必定的要求。
var scene, camera var renderer var width, width var cars = [] // var stats var config = { isMobile: false, background: 0x282828 } width = window.innerWidth height = window.innerHeight scene = new THREE.Scene() // 新建一個場景 camera = new THREE.PerspectiveCamera(45, width / height, 1, 5000) // 新建一個透視攝像機, 並設置 視場, 視野長寬比例, 可見遠近範圍 camera.position.set(330,330,330) // 設置攝像機的位置 camera.lookAt(scene.position) // 設置攝像機觀察的方向 scene.add(camera) renderer = new THREE.WebGLRenderer({antialias: true}) // 新建一個渲染器, 渲染器用來輸出最終結果 renderer.setSize(width, height) // 設置渲染的尺寸, 在這裏是瀏覽器尺寸 renderer.setClearColor(config.background) // 設置背景的顏色 renderer.shadowMap.enabled = true // 設置是否開啓投影, 開啓的話, 光照會產生投影 renderer.shadowMap.type = THREE.PCFSoftShadowMap // 設置投影類型, 這邊的柔和投影 document.body.appendChild(renderer.domElement) // renderer.domElement 是渲染器用來顯示結果的 canvas 標籤 // checkUserAgent() // buildAuxSystem() // buildLightSystem() // buildbuilding() // buildRoad() // buildStaticCars() // buildMovingCars() loop() function loop () { cars.forEach(function(car){ carMoving(car) }) renderer.render(scene, camera) // 渲染器開始渲染, scene 和 camera 是必須參數, 由於場景裏有動畫, 因此放在 loop 裏循環 requestAnimationFrame(loop) }
在3D世界裏, 光 是很是重要的元素, 直接影響到場景內物體的顯示效果, 一般會經過組合使用幾種不一樣的 光 來調出本身滿意的效果。在個人案例中,我分別在PC端和非PC端使用了不一樣的 光, 主要是出於性能考慮
if (!config.isMobile) { // PC端 var directionalLight = new THREE.DirectionalLight( 0xffffff , 1.1); // 新建一個平行光, 平行光照射到的每一個點的強度都同樣 directionalLight.position.set( 300, 1000, 500 ); directionalLight.target.position.set( 0, 0, 0 ); directionalLight.castShadow = true; // 開啓平行光的投影 // 下面是設置投影的效果 var d = 300; directionalLight.shadow.camera = new THREE.OrthographicCamera( -d, d, d, -d, 500, 1600 ); // 投影的可視範圍 directionalLight.shadow.bias = 0.0001; directionalLight.shadow.mapSize.width = directionalLight.shadow.mapSize.height = 1024; // 投影的精度 scene.add(directionalLight) // 再添加一個環境光, 目的是爲了調和平行光的投影區域, 防止投影過分黑 var light = new THREE.AmbientLight( 0xffffff, 0.3 ) scene.add( light ) }else{ // 非PC端 // 只添加一個天空光, 天空光從正上方往下照, 能夠照出明暗對比, 可是不產生陰影 var hemisphereLight = new THREE.HemisphereLight( 0xffffff, 1.6) scene.add( hemisphereLight) }
Three.js中,經過 幾何體(geometry) 和 材質(material) 來生成一個可視物體,我舉2個例子簡單說明一下
在個人案例中, 樹圍繞在建築周圍, 因此須要一個 單體樹, 還有樹的座標(能夠經過平面圖得出), 而後根據座標,在每一個位置放置一個樹
// 種樹函數 function addTrees () { // 樹的座標 var treesPosition = [ [-110, -110], [-90, -110],[-70, -110],[-50, -110],[-30, -110],[ -10, -110],[10, -110],[30, -110],[50, -110],[70, -110],[90, -110], [-110, 110], [-110, 90],[-110, 70],[-110, 50],[-110, 30],[ -110, 10],[-110, -10],[-110, -30],[-110, -50],[-110, -70],[-110, -90], [ 110, 110], [90, 110], [70, 110], [50, 110], [30, 110],[-30, 110], [-50, 110], [-70, 110], [-90, 110], [ 110, -110], [ 110, -90], [ 110, -70], [ 110, -50], [ 110, -30], [ 110, -10], [ 110, 10], [ 110, 30], [ 110, 50], [ 110, 70], [ 110, 90], ] treesPosition.forEach(function (elem) { var x = elem[0], y = 1, z = elem[1] var tree = createTree(x, y, z) scene.add(tree) }) } // 單體樹 function createTree (x, y, z) { var x = x || 0 var y = y || 0 var z = z || 0 var tree = new THREE.Object3D() // 新建一個空對象用來放 樹幹 和 樹葉 部分 var treeTrunkGeometry = new THREE.BoxGeometry(2,16,2) // 樹幹 var treeTrunk = utils.makeMesh('lambert', treeTrunkGeometry, 0x8a613a) treeTrunk.position.y = 8 // 樹幹 y 軸位置 tree.add(treeTrunk) // 樹幹添加到空對象中 var treeLeafsGeometry = new THREE.BoxGeometry(8, 8, 8) // 樹葉 var treeLeafs = utils.makeMesh('lambert', treeLeafsGeometry, 0x9c9e5d) treeLeafs.position.y = 13 // 樹葉 y 軸的位置 tree.add( treeLeafs) // 樹葉添加到空對象中 tree.position.set(x, y, z) return tree // 返回 樹 = 樹幹 + 樹葉 對象 }
汽車是場景中惟一運動的元素, 也是相對複雜的物體, 除了車身的構建, 還須要開放 前進 , 後退, 轉彎 等方法, 方便之後實現運動效果, 因此我單獨封裝
function Car (color) { // 能夠自定義車身的顏色, 默認隨機 var colors = [0x2cbab2, 0x47a700, 0xd60000, 0x087f87, 0x37ad0e, 0x4d4d4d, 0xce7e00, 0xe0a213, 0x87bcde] var index = Math.floor(Math.random() * colors.length) this.color = color || colors[index] this.mesh = new THREE.Object3D() this.wheels = [] this.startAngle = 0 var that = this addBody() // 添加車身到 this.mesh addWindows() // 添加車窗到 this.mesh addLights() // 添加車燈到 this.mesh addWheels() // 添加車輪到 this.mesh ... } Car.prototype = { // 設置車的位置 setPosition: function (x,y,z) { this.mesh.position.set(x,y,z) }, // 前進, 實現無論車旋轉的角度怎樣, 車都能按車頭的方向前進 forward: function (speed) { var speed = speed || 1 this._moving(speed, true) }, // 後退, 實現無論車旋轉的角度怎樣, 車都能按車尾的方向後退 backward: function (speed) { var speed = speed || 1 this._moving(speed, false) }, // 左轉 turnLeft: function (angle, speed) { this._turn(angle, true, speed) }, // 右轉 turnRight: function (angle, speed) { this._turn(angle, false, speed) }, _turn: function (angle, direction, speed) { var direction = direction ? 1 : -1 if (speed) { if(this.startAngle < angle) { this.mesh.rotation.y += speed this.startAngle += speed if (angle - this.startAngle < speed) { var originAngle = this.mesh.rotation.y - this.startAngle this.mesh.rotation.y = originAngle + angle this.startAngle = 0 return } } } else { this.mesh.rotation.y += angle * direction } }, _moving: function (speed, direction) { var rotation = this.mesh.rotation.y var direction = direction ? 1 : -1 var xLength = speed * Math.cos(rotation) * direction, zLength = speed * Math.sin(rotation) * direction this.mesh.position.x += xLength this.mesh.position.z -= zLength this._rotateWheels(speed) }, _rotateWheels: function (speed) { this.wheels.forEach(function (elem) { elem.rotation.z -= 0.1*speed }) } }
// 生成汽車 function buildStaticCars () { var carsPosition = [ [-84, 82, 1.5], [-58, 82, 1.5], [-32, 82, 1.5] , [84, 82, 1.5] ] carsPosition.forEach(function (elem) { var car = new Car() var x = elem[0], z = elem[1], r = elem[2] car.setPosition(x, 0, z) car.mesh.rotation.y = r * Math.PI scene.add(car.mesh) }) }
做爲本身入門Three.js的一個案例, 仍是以實現基本效果爲目標, 最終效果和指望的仍是有必定的差距, 因此將來還將不斷探索Three.js的一些高級技巧。 瀏覽器
大概就到這裏了, 歡迎starapp