技術之路

站在代碼以外看技術

Three.js 初探 - 微場景制造

比來在搗鼓 Three.js 相幹, 認為做一個微場景是一個很不錯的選擇,成果做上去發明後果還可以。

上面放幾張分歧角度的後果圖

《Three.js 初探 - 微場景制造》
《Three.js 初探 - 微場景制造》
《Three.js 初探 - 微場景制造》

預覽地址

源碼地址

設置裝備擺設較差的設備上預覽後果不是很幻想, 待優化中

完成進程

接上去大致解釋一下完成進程

預備任務

起首你要清晰本身想完成的後果, 就須要一張後果圖, 然後能力依據後果圖來建模。
我在網上找到一張本身愛好的作風的修建圖做參考,明白本身也許要完成的後果,但成果紛歧定要如出壹轍。

《Three.js 初探 - 微場景制造》

然後依據後果圖,畫出本身想要的模子的立體圖,便利模子中物體的準確定位

《Three.js 初探 - 微場景制造》

開辟

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個例子簡略解釋一下

《Three.js 初探 - 微場景制造》

在我的案例中, 樹環繞在修建四周, 所以須要一個 單體樹, 還有樹的坐標(可以經由過程立體圖得出), 然後依據坐標,在每壹個地位放置一個樹

// 種樹函數
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 // 前往 樹 = 樹幹 + 樹葉 對象
}
汽車

《Three.js 初探 - 微場景制造》

汽車是場景中獨壹活動的元素, 也是絕對龐雜的物體, 除車身的構建, 還須要開放 進步 , 撤退退卻轉彎 等辦法, 便利今後完成活動後果, 所以我零丁封裝

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的一些高等技能。

 

轉自: http://www.geek720.com/

點贊

揭櫫評論

電子郵件地址不會被公開。 必填項已用*標注