threejs基础

701 阅读6分钟

1、threejs技术的发展

​ threejs是JavaScript编写的WebGL第三方库,是一款运行在浏览器中的 3D 引擎。所以介绍threejs我们需要从下面两个知识点进行说明。

​ (1)什么是openGL。

​ (2)什么是webGL。

opneGL:是Khronos Group开发维护的一个规范,它主要为我们定义了用来操作图形和图片的一系列函数的API,需要注意的是OpenGL本身并非API。GPU的硬件开发商则需要提供满足OpenGL规范的实现,这些实现通常被称为“驱动”,它们负责将OpenGL定义的API命令翻译为GPU指令。

webGL(全写Web Graphics Library):是一种3D绘图协议,这种绘图技术标准允许把JavaScript和OpenGL ES 2.0结合在一起,通过增加OpenGL ES 2.0的一个JavaScript绑定,WebGL可以为HTML5 Canvas提供硬件3D加速渲染,这样Web开发人员就可以借助系统显卡来在浏览器里更流畅地展示3D场景和模型了,还能创建复杂的导航和数据视觉化。WebGL完美地解决了现有的Web交互式三维动画的两个问题:第一,它通过HTML脚本本身实现Web交互式三维动画的制作,无需任何浏览器插件支持;第二,它利用底层的图形硬件加速功能进行的图形渲染,是通过统一的、标准的、跨平台的OpenGL接口实现的。(其他的web3D技术:Cult3D、Viewpoint、Atmosphere等等都需要插件)。

graph LR
    A[openGL] --> B(webGL)
    B --> C(threejs)

常见js 3D引擎(Babylon.js、Three.js、Turbulenz等等)。

2、threejs基础知识

(1)基础对象

  • 常量(Constants)
  • 相机(Cameras)
  • 核心模块(Core)
  • 灯光(Lights)
  • 加载器(Loaders)
  • 材质(Materials)
  • 数学工具库(Math)
  • 物体/模型
  • 渲染器(Renderers)
  • 渲染器 / WebGL
  • 场景(Scenes)
  • 纹理(Textures)
  • 动画(Animation)
  • 几何模型(Geometries)
  • 辅助对象(Helpers)

(2)常用对象的关系[场景(scene)、相机(camera)、物体、光照(lights)、渲染器(rendner)]

graph LR
    A[Object3D]
    A -->  |继承| C[相机/camera]
    A -->  |继承| O[物体]
    A -->  |继承| L[灯光/lights]
    A -->  |继承| S[场景/scene]
    
    C --> CS[立体相机/StereoCamera]
    C --> CC[立方相机/CubeCamera]
    C --> CO[正交相机/OrthographicCamera]
    C --> CP[透视相机/PerspectiveCamera]
    
    O --> OL[线/Line]
    O --> OM[网格/Mesh]
    O --> OP[点/Points]
    O --> OG[组/Group]
    
    L--> LA[环境光/AmbientLight]
    L--> LD[平行光/DirectionalLight]
    L--> Lp[点光源/PointLight]
    L--> Ls[聚光灯/SpotLight]
   
  • Object3D是Three.js中大部分对象的基类,提供了一系列的属性和方法来对三维空间中的物体进行操纵

Object3D的常用属性和方法:

  • .rotation 物体局部旋转,以弧度来表示。
  • scale : Vector3 物体局部缩放,默认值Vector3 (1,1,1)。
  • userData:object 用于存储自定义数据对象。
  • lookAt(Vector3 ) 旋转物体使其面朝场景的某一点。
  • remove(object3D...) 从当前子对象中移除对象。
  • rotateX(float)、rotateY(float)、rotateZ(float) 绕局部空间的X、Y、Z轴旋转
  • 场景(scene)

场景允许你在什么地方、摆放什么东西来交给three.js来渲染,这是你放置物体、灯光和摄像机的地方。

初始化场景对象:

new THREE.Scene();
  • 相机(camera)

​ 摄像机的抽象基类。在构建新摄像机时,应始终继承此类。我们这里主讲透视相机。

​ 透视相机(PerspectiveCamera:这一投影模式被用来模拟人眼所看到的景象,它是3D场景的渲染中使用得最普遍的投影模式。

构造函数:

new THREE.PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )
fov — 摄像机视锥体垂直视野角度
aspect — 摄像机视锥体长宽比
near — 摄像机视锥体近端面
far — 摄像机视锥体远端面
  • 物体

物体及我们所说的模型,有三种创建模型的方式:

(1)根据threejs的几何体对象创建。

(2)自己利用程序绘制几何体创建。

(3)外部3D模型导入。

在创建模型过程中,我们会涉及到另外两个常用的对象,材质和纹理。

材质:材质描述了对象objects的外观。它们的定义方式与渲染器无关, 因此,如果您决定使用不同的渲染器,不必重写材质。threejs定义了许多不同的材质对象:

​ 基础网格材质、基础线条材质、Phong网格材质、着色器材质等等。

  • 光照(lights)

光源是场景中十分重要的一部分,没有光源,渲染的场景将不可见。threejs中定义了下列几种光源:

(1)环境光(AmbientLight)

new THREE.AmbientLight( color : Integer, intensity : Float )
color - (参数可选)颜色的rgb数值。缺省值为 0xffffff。
intensity - (参数可选)光照的强度。缺省值为 1

(2)平行光(DirectionalLight)

(3)点光源(PointLight)

(4)聚光灯(SpotLight)

(5)半球光(HemisphereLight)

.castShadow属性表示是否投射阴影。其中 AmbientLight、HemisphereLight的castShadow是undefined。如果需要模型有阴影,除了设置光源的 castShadow为true,还需要设置渲染器需要阴影效果:

renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 默认的是,没有设置的这个清晰 THREE.PCFShadowMap
  • 渲染器(rendner)

threejs有以下几种渲染器:

(1)WebGLRenderer

this.renderer = new THREE.WebGLRenderer({
// 在 css 中设置背景色透明显示渐变色
alpha: true,
// 开启抗锯齿
antialias: true
});
// 定义渲染器的尺寸;在这里它会填满整个屏幕
this.renderer.setSize(this.WIDTH, this.HEIGHT);
this.container.appendChild(this.renderer.domElement);

(2)CSS2DRenderer

(3)CSS3DRenderer

(4)SVGRenderer

3、3D场景的创建流程

graph LR
    A[初始化场景] --> B(初始化相机)
    B --> C(添加灯光)
    C --> D(添加模型)
    D --> E(渲染器)

4、demo源码分析

import './BackgroundImage.css';
import React, { Component } from 'react';
import * as THREE from 'three';
import * as dat from 'dat.gui';
import Orbitcontrols from 'three-orbitcontrols';
import * as Stats from 'stats.js'


class BackgroundImage extends Component {
    componentDidMount() {
        this.container = document.getElementById('container');
        this.width = this.container.clientWidth;
        this.height = this.container.clientHeight;
        this.initScene();
        this.initCamera();
        this.initLight();
        this.initModal();
        this.initRenderer();

        this.controls = new Orbitcontrols(this.camera, this.renderer.domElement);
        this.initGui();
        this.initStats();
        this.animate();
    }

    componentWillUnmount() {
        this.renderer.forceContextLoss();
        this.renderer.dispose();
        window.cancelAnimationFrame(this.animationID);
        this.scene.dispose();
        this.controls.dispose();
    }

    //初始化场景
    initScene = () => {

        this.scene = new THREE.Scene();
        // 在场景中添加雾的效果,参数分别代表‘雾的颜色’、‘开始雾化的视线距离’、刚好雾化至看不见的视线距离’
        this.scene.fog = new THREE.Fog(0x090918, 1, 600);
    }

    //初始化相机
    initCamera = () => {
        /**
     * PerspectiveCamera 透视相机
     * @param fieldOfView 视角
     * @param aspectRatio 纵横比
     * @param nearPlane 近平面
     * @param farPlane 远平面
     */
        this.camera = new THREE.PerspectiveCamera(60, this.width / this.height, 1, 10000);
        this.camera.position.set(0, 10, 100);
        this.camera.lookAt(new THREE.Vector3(0, 0, 0));
    }


    //光源
    initLight = () => {
        let ambientLight = new THREE.AmbientLight(0x0c0c0c);

        let spotLight1 = new THREE.SpotLight(0xffffff);
        spotLight1.position.set(-400, -400, -400);

        let spotLight2 = new THREE.SpotLight(0xffffff);
        spotLight2.position.set(0, 0, 400);

        this.scene.add(ambientLight);
        this.scene.add(spotLight2);
    }

    //模型
    initModal = () => {
        //材质
        let material = new THREE.MeshPhongMaterial();
        material.map = new THREE.TextureLoader().load(require('./Earth.png'));
        //几何体
        let sphereGeometry = new THREE.SphereGeometry(40, 50, 50);

        //模型  
        this.sphere = new THREE.Mesh(sphereGeometry, material);
        this.scene.add(this.sphere);
    }

    //渲染器
    initRenderer = () => {
        // 开启抗锯齿
        // 在 css 中设置背景色透明显示渐变色
        this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
        // 定义渲染器的尺寸;在这里它会填满整个屏幕
        this.renderer.setSize(this.width, this.height);
        this.container.appendChild(this.renderer.domElement);
    }

    //动画刷新
    animate = () => {
        this.animateID = requestAnimationFrame(this.animate);
        this.controls.update();
        this.stats.update();
        //this.stats.update();
        if (this.sphere) {
            this.sphere.rotateY(0.001);
        }
        this.renderer.render(this.scene, this.camera);
    }


    initGui = () => {
        let objThis = this;
        let guiControls = new function () {
            this.clearAlpha = objThis.renderer.getClearAlpha();
            this.clearColor = objThis.renderer.getClearColor().getStyle();
        };

        let gui = new dat.GUI();
        gui.add(guiControls, 'clearAlpha', 0, 1).onChange(function (e) {
            objThis.renderer.setClearAlpha(e);
        });
        gui.addColor(guiControls, 'clearColor').onChange(function (e) {
            objThis.renderer.setClearColor(new THREE.Color(e), objThis.renderer.getClearAlpha());
        });
    }

    //性能插件
    initStats = () => {
        this.stats = new Stats();
        this.container.appendChild(this.stats.dom);
    }


    render() {
        return (
            <div id='container'></div>
        )
    }

}

export default BackgroundImage

#container{
    position:absolute;
    width:100%;
    top:0;
    bottom:0;
    overflow: hidden;
    background: url("./bgc-map.jpg") center no-repeat;
    -webkit-background-size: cover;
    background-size: cover;
}

5、demo的gitee地址:gitee.com/conzhangL/t…

更多内容,请查看我个人博客:conzhangl.gitee.io/hexoblog/