最近想在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;
對於三維繪圖來講,陰影能很大程度的加強效果,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環境下如何入門。後續學習中的心得會不斷更新,感謝閱讀。前端學習的道路漫長,與君共勉!