angular8+threejs 入坑

项目情况:仿优诺的机房3d可视化管理系统
我的demo效果传送门

项目技术angular8(架构)threejs(3d场景操作)tweenjs(交互)

建模工具:blender(我用这个2.8)、3dsMax、maya

我用的模型格式:GLTF

用到的两个threejs库:three、three-full 、@tweenjs/tween.js(注意名字,tweenjs后面再说)

其实当有模型提供的前提下做3d场景并不是一个很难的事情,有了模型之后就是如何用threejs调用api来操作而已,所以不用太恐惧,模型网上有好多。

下面直接上代码吧

1、新建ng项目(不多说了);

2、安装库

npm i three //  用来调用threejs的docs的api
npm i three-full    //  用来调用threejs的examples的api
npm i @tweenjs/tween.js //  用来交互

注意:examples里面的api就等于别人封装好的组件然后官方拿来展示,所以这些api都不会存在three里面的,因此有人把他们集成到three-full这个库里面进行调用。

以下是我的使用例子 base.ts

import {
   Scene,
   AmbientLight,
   PointLight,
   WebGLRenderer,
   PerspectiveCamera,
   GridHelper,
   Color
} from 'three';
import {
   OrbitControls,
} from 'three-full';
import * as Stats from 'stats.js';
import * as TWEEN from '@tweenjs/tween.js';
import {
 FXAAShader, UnrealBloomPass, ShaderPass, FilmPass, OutlinePass, GeometryUtils, CopyShader, ColorifyShader, SepiaShader,
 OrbitControls, GLTFLoader, EffectComposer, RenderPass, SMAAShader, SMAAPass, ClearMaskPass, MaskPass,
} from 'three-full';

// three-full和tween以下代码没调用,先展示下调用方式


//  渲染器
export const RENDERER = new WebGLRenderer(); //  渲染器(去据此){ antialias: true }
export function initRenderer(doc) {
   RENDERER.setSize(
       doc.clientWidth,
       doc.clientHeight
   );
   RENDERER.shadowMap.enabled = true; // 辅助线
   doc.appendChild(RENDERER.domElement);


}


// 场景
export const SCENE = new Scene();
export function initScene() {
   SCENE.background = new Color(0xcccccc);
}

//  灯光
export function initLight() {
   const ambientLight = new AmbientLight(0xffffff, 0.2);    // 全局光
   ambientLight.position.set(10, 20, 55);   // 灯光
   SCENE.add(ambientLight);

   // 点光源
   const pointLight = new PointLight(0xffffff);
   pointLight.distance = 0;
   CAMERA.add(pointLight);
   SCENE.add(CAMERA);
}

//  相机
export let CAMERA;
export let CONTROLS;
export function initCamera(doc) {
   const d = {
       fov: 30, // 拍摄距离  视野角值越大,场景中的物体越小
       near: 1, //  最小范围
       far: 1000, //  最大范围
   };
   CAMERA = new PerspectiveCamera(
       d.fov,
       doc.clientWidth / doc.clientHeight,
       d.near,
       d.far)
       ;
   const p = {
       x: -20,
       y: 10,
       z: -10,
   };
   CAMERA.position.set(p.x, p.y, p.z);
   CAMERA.lookAt(0, 0, 0);
   CONTROLS = new OrbitControls(CAMERA, RENDERER.domElement);  // 控制镜头
}


//  网格
export function initGrid() {
   const gridHelper = new GridHelper(100, 50);
   SCENE.add(gridHelper);
}


//  性能检测
export const STATS = new Stats();
export function initStats(doc) {
   STATS.setMode(0);
   STATS.domElement.style.position = 'absolute';
   STATS.domElement.left = '0px';
   STATS.domElement.top = '0px';
   doc.appendChild(STATS.domElement);
}

//  动画混合器组(把模型的动画混合器都push到这里面,在canvas.ts里面更新动画   )
export const MIXER = [];

以上的代码是一个场景的基本属性,如果你看过threejs就知道了。

3、新建一个canva component ( ng g component canva )

html

<div id="canvas" #canvasFrame></div>

scss

#canvas {
      width: 100%;
      display: block;
      height: 100%;
      position: relative;
}

ts

import { Component, OnInit, ViewChild, ElementRef } from '@angular/core';
import {
  initRenderer,
  initCamera,
  initScene, initLight,
  initGrid, initStats,
  RENDERER, CAMERA, SCENE, CONTROLS, STATS, MIXER
} from '../../config/base';
import { ChassisService, LineService } from '../../importModels';
import {
  FXAAShader, UnrealBloomPass, ShaderPass, FilmPass, OutlinePass, GeometryUtils, CopyShader, ColorifyShader, SepiaShader,
  OrbitControls, GLTFLoader, EffectComposer, RenderPass, SMAAShader, SMAAPass, ClearMaskPass, MaskPass,
} from 'three-full';
import * as  TWEEN from '@tweenjs/tween.js';
import { Vector2, Group, Scene, SphereGeometry, ImageUtils, AnimationMixer } from 'three';
import { fromEvent } from 'rxjs';
import { PickupService } from '../../service/pickup.service';

@Component({
  selector: 'app-canva',
  templateUrl: './canva.component.html',
  styleUrls: ['./canva.component.scss']
})
export class CanvaComponent implements OnInit {
  @ViewChild('canvasFrame', { static: true }) canvasContainer: ElementRef;
  
   thing;
  constructor( ) { }
 

  ngOnInit() {
    this.init();
  }


  init() {

    initRenderer(this.canvasContainer.nativeElement);
    initCamera(this.canvasContainer.nativeElement);
    initScene();
    initLight();
    initGrid();
    initStats(this.canvasContainer.nativeElement);
   
    //  加载模型-star
    this.importantModel();
    //  加载模型-end
 
    //  渲染场景
    const delta = new Clock();
    const rendererOut = () => {
      
      requestAnimationFrame(rendererOut);
      RENDERER.render(SCENE, CAMERA);
      CONTROLS.update();
      STATS.update();
 if (MIXER) {
        MIXER.map(r => {
          r.update(delta.getDelta());
        });
      }
    };

    rendererOut();

  }

  

// 这个模型可以使用blender2.8(正处于beta版) 直接导出gltf
  importantModel() {
    const loader = new GLTFLoader();
    loader.load('assets/model/1.gltf', (gltf) => {
      console.log(gltf);
    
      gltf.scene.traverse((child) => {  // 遍历判断Mesh
        if (child.isMesh) {
          console.log(child);
          child.material.color = { r: 1, g: 2, b: 3 };    //  颜色
          child.material.metalness = 0.8;   //  金属度
          gltf.scene.background = 'rgba(0,0,0, 0.5)';
          gltf.scene.translateX(5);
          this.thing = gltf.scene;
          SCENE.add(this.thing);
        }
      });

    },
      undefined,
      (error) => {
        console.error(error);
      });
  }

}

因为不能上传文件所以你们自己导出一个或者随便下一个吧。

接下来可以了解下threejs在angular里面可以如果架构比较方便分模块编写:传送门

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,141评论 5 461
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,101评论 2 372
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 142,318评论 0 321
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,357评论 1 266
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,205评论 4 357
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,194评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,595评论 3 384
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,285评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,578评论 1 292
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,642评论 2 311
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,402评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,266评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,641评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,933评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,220评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,591评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,780评论 2 335