Предварительное исследование Three.js — создание микросцены

дизайн three.js

Недавно я возился с Three.js, и я думаю, что это хороший выбор для создания микро-сценария После этого я обнаружил, что эффект в порядке.

Ниже несколько фото с разных ракурсов

微场景-斜视
微场景-正面
微场景-背面

адрес предварительного просмотраScrewknot.GitHub.IO/три JS — отвратительно...  

Адрес источникаGitHub.com/screwknot/dayhou…

Эффект предварительного просмотра на устройствах с плохой конфигурацией не очень хорош, требует оптимизации

Процесс реализации

Далее мы кратко опишем процесс внедрения.

Готов к работе

Прежде всего, вам нужно знать эффект, которого вы хотите добиться, вам нужна визуализация, а затем вы можете моделировать в соответствии с визуализацией. Я нашел архитектурный чертеж моего любимого стиля в Интернете для справки и четко обозначил эффект, которого хотел бы достичь, но результаты не обязательно должны быть точно такими же.

效果图

Затем в соответствии с визуализацией нарисуйте план этажа модели, которую вы хотите, чтобы облегчить точное позиционирование объектов в модели.

平面图

развивать

Сравнительно просто построить аналогичный микросценарий с помощью 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)
    }

Настроить систему освещения

В трехмерном мире,СветлыйЭто очень важный элемент, который напрямую влияет на эффект отображения объектов в сцене и обычно использует несколько различныхСветлыйдля достижения желаемого эффекта. В моем случае я использовал разные источники света на стороне ПК и на стороне без ПК, в основном из соображений производительности.

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 визуальный объект создается с помощью геометрии и материала, я приведу два примера, чтобы кратко объяснить

Дерево

树

В моем случае дерево окружает здание, поэтомуМонолитное дерево, и координаты дерева (которые можно получить из плана этажа), а затем разместить дерево в каждом месте в соответствии с координатами

// 种树函数
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 в будущем. .

Вот и все, добро пожаловать, звезда