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