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环境下如何入门。后续学习中的心得会不断更新,感谢阅读。前端学习的道路漫长,与君共勉!

相关文章
相关标签/搜索