Angular+Three.JS開發環境搭建

最近想在Angular項目裏引入Three.Js來實現一個功能,發現網上對於這二者結合使用的材料比較少,特此記錄一下方便本身之後使用。css

開發環境搭建

1.安裝 Three.jshtml

cnpm i three --save
    cnpm i @types/three --save-dev
複製代碼

2.安裝 OrbitControl前端

cnpm i three-orbitcontrols-ts --save 
複製代碼

3.組件中引用npm

import * as THREE from 'three';
    import { OrbitControls } from 'three-orbitcontrols-ts';
複製代碼

4.基本繪製bash

首先給定一個區域用於顯示 app

而後配合下面代碼建立Three.js的基本要素,進行綁定,就繪製出一個簡單的三維立方體。

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import * as THREE from 'three';
import { OrbitControls } from 'three-orbitcontrols-ts';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit{
  title = 'ng-three';

  @ViewChild('Three') three:ElementRef;
  private renderer:any = new THREE.WebGLRenderer();
  private width;
  private height = 500;
  private scene = new THREE.Scene();
  private camera:any;

  ngOnInit(){
    this.width =  this.three.nativeElement.offsetWidth;

    //建立背景
    this.renderer.setSize(this.width,this.height);
    this.renderer.setClearColor(0xFFFFFF);

    //綁定DOM
    this.three.nativeElement.append(this.renderer.domElement);

    //建立一個具備透視效果的攝像機
    this.camera = new THREE.PerspectiveCamera(45,this.width/this.height,0.1,1000)

    //設置攝像機的位置,並對準場景中心
    this.camera.position.x = 10
    this.camera.position.y = 10
    this.camera.position.z = 30
    this.camera.lookAt(this.scene.position)

    //建立一個長寬高均爲4個單位的正方體
    var cubeGeometry = new THREE.BoxGeometry(4,4,4)

    //建立材質
    var cubMaterial = new THREE.MeshBasicMaterial({
      color: 0xff0000
    })

    //建立一個Mesh,將材質包裹到立方體上
    var cube = new THREE.Mesh(cubeGeometry,cubMaterial)
    cube.name = '弟弟'
    
    //設置立方體的位置
    cube.position.x = 0
    cube.position.y = 0
    cube.position.z = 0

    this.scene.add(cube)

    //渲染
    this.renderer.render(this.scene,this.camera)
  }
}
複製代碼

這樣,就成功繪製出了一個紅色的小方塊 dom

一些

座標系

Three.js座標系爲右手笛卡爾座標系 函數

透視攝像機

THREE.PerspectiveCamera(fov, aspect, near, far)性能

參數 描述
fov 攝像機視野角度,默認50
aspect 渲染界面的寬高比值,也就是咱們繪圖區域的 width/height
near 指定距離攝像機多近的距離開始渲染,默認0.1
far 指定攝像機能看到多遠,太小則可能場景的遠處不被渲染,過大則會影響性能,默認1000

本例中,設置以下

this.camera = new THREE.PerspectiveCamera(45,this.width/this.height,0.1,1000)學習

動畫

bulingbuling動起來~

添加函數

animate(){
    requestAnimationFrame(this.animate.bind(this));
    this.cube.rotation.z += 0.01;
    this.cube.rotation.y += 0.01;
    this.renderer.render(this.scene,this.camera)
  }
複製代碼

在ngOnInit函數中調用便可,注意requestAnimationFrame的寫法,這個問題搞了好久。

而後調用animate函數便可。

對於Angular來講,這裏要改寫一下cube的聲明。 private cube:any;

對於旋轉 cube.rotation 正值是逆時針旋轉,負值是順時針旋轉。

陰影

對於三維繪圖來講,陰影能很大程度的加強效果,Three.js考慮到性能默認關掉了陰影。

開啓渲染器的陰影功能,並設置陰影的類型 柔和投影

this.renderer.shadowMap.enabled = true
    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
複製代碼

修改剛纔建立的cube,讓其能夠產生陰影

this.cube.castShadow = true
複製代碼

建立燈光

//添加聚光燈,該燈光類型能夠產生陰影
    var spotLight = new THREE.SpotLight(0xffffff)
    spotLight.position.set(30,60,40)
    //開啓該聚光燈的投影效果
    spotLight.castShadow = true
    //設置該燈光的陰影質量
    spotLight.shadow.mapSize.width = 1024
    spotLight.shadow.mapSize.height = 1024
    //場景添加該燈光
    this.scene.add(spotLight)
複製代碼

建立一個平板接收投影

var planeGeometry = new THREE.PlaneGeometry(60, 60, 1, 1)
    //該平板的材質不能是 THREE.MeshBasicMaterial,由於它不受光照的影響
    var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff })
    var plane = new THREE.Mesh(planeGeometry, planeMaterial)
    //該平板接收其餘物體的投影
    plane.receiveShadow = true

    plane.rotation.x = -0.5 * Math.PI
    plane.position.set(0, -4, -10)
    this.scene.add(plane)
複製代碼

效果以下:

控制和交互

添加控制器便可,更多設置可看three-orbitcontrols-ts

const controls = new OrbitControls(this.camera,this.renderer.domElement)
複製代碼

點擊事件:Three.js中沒有DOM的層級,且在三維環境中,想一想就以爲麻煩。

好在官方提供瞭解決方案,簡單來講就是把點擊座標轉化成繪圖區域的座標系,而後從攝像機向改座標發射一條光線,光線找到的物體(第一個)就是你所點擊的物體。Angular寫法以下:

[html]

<div #Three class="main" (click)="onDocumentMouseDown($event)">
複製代碼

[JS]

onDocumentMouseDown(event) {
    event.preventDefault();
    let vector = new THREE.Vector3((event.offsetX / this.width) * 2 - 1, -(event.offsetY / this.height) * 2 + 1, 0.5);
    vector = vector.unproject(this.camera);

    let raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize());
    let intersects = raycaster.intersectObjects(this.scene.children);

    if (intersects.length > 0) {
      console.log(intersects[0].object.name + " 不存在");
      intersects[0].object.visible = false
    }
  }
複製代碼

而後點擊目標物體,就會消失並輸出name屬性了

這裏注意下vector的值,對於渲染區域,採用的是 event.offsetX / this.width

完整代碼

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import * as THREE from 'three';
import { OrbitControls } from 'three-orbitcontrols-ts';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'ng-three';

  @ViewChild('Three') three: ElementRef;
  private renderer: any = new THREE.WebGLRenderer();
  private width;
  private height = 500;
  private scene = new THREE.Scene();
  private camera: any;

  private cube: any;

  ngOnInit() {
    this.width = this.three.nativeElement.offsetWidth;

    //建立背景
    this.renderer.setSize(this.width, this.height);
    this.renderer.setClearColor(0xFFFFFF);

    // 開啓渲染器的陰影功能,並設置陰影的類型 柔和投影
    this.renderer.shadowMap.enabled = true
    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap

    //綁定DOM
    this.three.nativeElement.append(this.renderer.domElement);

    //建立一個具備透視效果的攝像機
    this.camera = new THREE.PerspectiveCamera(45, this.width / this.height, 0.1, 1000)

    //設置攝像機的位置,並對準場景中心
    this.camera.position.x = 10
    this.camera.position.y = 10
    this.camera.position.z = 30
    this.camera.lookAt(this.scene.position)

    //建立一個長寬高均爲4個單位的正方體
    var cubeGeometry = new THREE.BoxGeometry(4, 4, 4)

    //建立材質
    var cubMaterial = new THREE.MeshBasicMaterial({
      color: 0xff0000
    })

    //建立一個Mesh,將材質包裹到立方體上
    this.cube = new THREE.Mesh(cubeGeometry, cubMaterial)
    this.cube.name = '弟弟'

    //設置使其可產生陰影
    this.cube.castShadow = true
    //設置立方體的位置
    this.cube.position.x = 0
    this.cube.position.y = 0
    this.cube.position.z = 0

    this.scene.add(this.cube)

    //添加聚光燈,該燈光類型能夠產生陰影
    var spotLight = new THREE.SpotLight(0xffffff)
    spotLight.position.set(30, 60, 40)
    //開啓該聚光燈的投影效果
    spotLight.castShadow = true
    //設置該燈光的陰影質量
    spotLight.shadow.mapSize.width = 1024
    spotLight.shadow.mapSize.height = 1024
    //場景添加該燈光
    this.scene.add(spotLight)

    var planeGeometry = new THREE.PlaneGeometry(60, 60, 1, 1)
    //該平板的材質不能是 THREE.MeshBasicMaterial,由於它不受光照的影響
    var planeMaterial = new THREE.MeshLambertMaterial({ color: 0xffffff })
    var plane = new THREE.Mesh(planeGeometry, planeMaterial)
    //該平板接收其餘物體的投影
    plane.receiveShadow = true

    plane.rotation.x = -0.5 * Math.PI
    plane.position.set(0, -4, -10)
    this.scene.add(plane)
    //渲染
    this.renderer.render(this.scene, this.camera)
    this.animate();

    const controls = new OrbitControls(this.camera, this.renderer.domElement)
  }

  animate() {
    requestAnimationFrame(this.animate.bind(this));
    this.cube.rotation.z += 0.01;
    this.cube.rotation.y += 0.01;
    this.renderer.render(this.scene, this.camera)
  }

  onDocumentMouseDown(event) {
    event.preventDefault();
    let vector = new THREE.Vector3((event.offsetX / this.width) * 2 - 1, -(event.offsetY / this.height) * 2 + 1, 0.5);
    vector = vector.unproject(this.camera);

    let raycaster = new THREE.Raycaster(this.camera.position, vector.sub(this.camera.position).normalize());
    let intersects = raycaster.intersectObjects(this.scene.children);

    if (intersects.length > 0) {
      console.log(intersects[0].object.name + " 不存在");
      intersects[0].object.visible = false
    }
  }
}
複製代碼

最後

本文內容比較短淺,由於剛接觸Three.js尚未開始學習,主要目的仍是爲本身記錄一下在Angular環境下如何入門。後續學習中的心得會不斷更新,感謝閱讀。前端學習的道路漫長,與君共勉!

相關文章
相關標籤/搜索