WEBGL探索之路 (二)--webgl场景构建

1,782 阅读6分钟

说明:今天主要演示环境搭建,几何体的组成方式

1.工具

   three.js: JavaScript 3D library,是一个是一个基于webgl的js库

   vuejs:环境为vue框架环境,vuecli的脚手架

   Visual Studio Code: 开发工具

    Chrome:浏览器

2.准备

 1.vue脚手架npm install -g @vue/cli vue create ThreejsExport这里就不做太多概述

 2.npm install three 安装three.js

 3.将src/components/HelloWord.vue里面的template内容清空,里面的div加个句柄       ref="container" css样式设置下;

<template>  <div class="hello" ref="container">      </div></template>
import * as Three from 'three'  //将安装好的three import进来
export default {  name: 'HelloWorld',  data(){  },  props: {    msg: String  },  methods:{     //基本结构大概是这样    init() {    },    run() {         }  },  mounted(){    this.run()  }.hello{  width: 100vw;  height: 100vh;}

3.开始

  先上代码构建个基础图形

    const container = this.$refs.container; //获取dom  
    const width = container.clientWidth;const height = container.clientHeight; //获取宽高
    const camera = new Three.PerspectiveCamera(70, width  / height , 0.01, 10); //相机    camera.position.z = 0.6;    
    const scene = new Three.Scene();   
    const geometry = new Three.BoxGeometry(0.2, 0.2, 0.2);   
    const material = new Three.MeshNormalMaterial();  
    const mesh = new Three.Mesh(geometry, material); 
    scene.add(mesh);  
    let animate = () => { requestAnimationFrame(animate);mesh.rotation.x += 0.01 ;mesh.rotation.y += 0.02;renderer.render(scene, camera)      }      
    animate(); 先创建一个简单的三维物体

下面就一步步来介绍制作流程

一个webgl模型大概有几部分组成

:container 存放场景 dom

:scence对象,就是所谓的场景

4.网络模型 

  1. THREE.BoxGeometry(60, 40, 40)立方体对象(长宽高);BoxGeometry后面还有三个参数widthSegments, heightSegments, deptSegments是对应长宽高的分段,在使用线模式({wireframe:true})进行渲染的时候,可以看到效果如下先上代码

  2. const geometry = new Three.BoxGeometry(0.2, 0.2, 0.2,1,1,1); //三对称面都一条线 var material = new Three.MeshBasicMaterial({ wireframe : true }); material.color = new Three.Color('green');//要想生活过得去先来点绿 // const material = new Three.MeshNormalMaterial(); const mesh = new Three.Mesh(geometry, material); scene.add(mesh);

    下面我们改下里面的参数

    const geometry = new Three.BoxGeometry(0.2, 0.2, 0.2,2,2,1);(width, height, dept, widthSegments, heightSegments, depthSegments)//改成221长宽高分别被截为2段,2段,1段 
    

  3. 材质material:THREE.MeshLambertMaterial (内容太多,后面单开一张来介绍)

  4. 相机camera:Three.PerspectiveCamera(fov, aspect, near, far)  透视相机,该类型的相机使用透视矩阵;              就是常见投影                                                                                                                  Fov – 相机的视锥体的垂直视野角

    Aspect – 相机视锥体的长宽比           

    Near – 相机视锥体的近平面

    Far – 相机视锥体的远平面

    部分继承相机基类。.aspect相机视锥体的长宽比,一般都是画布canvas的宽高比。默认是1(正方形).far相机视锥体远平面。默认是2000.filmGauge胶片尺寸,默认35mm镜头。这个参数不影响投影变换矩阵,除非.filmOffset设定为一个非0.fileOffset水平偏移,单位是mm,默认值是0.fov默认50度,从下到上。相机视野角。.isPerspectiveCamera用来测试该类或者派生类是否是透视相机。默认是true。不能该表,主要内部优化用。.near默认0.1,视锥体近平面值.view视锥体规范窗口/空的。使用.setViewOffset设定,清空使用.clearViewOffset..zoom读写相机的缩放比方法:部分方法继承相机基类的方法.clearViewOffset().setViewOffset()设定后,可以通过.clearViewOffset()清空.getEffectiveFOV()返回当前FOV的值(度),相机zoom值会有影响。.getFilmHeight()返回画面高度。如果视野角FOV <= 1(垂直模式),结果和fileGauge一样。.getFileWidth()返回画面宽度。如果视野角FOV >=1(宽屏模式),结果和fileGauge一样。.getFocalLength()返回当前FOV的焦距长.setFocalLendth(focalLength)用focalLength设定FOV,默认是35mm焦距。.setViewOffset(fullWidth,fullHeight,x,y,width,height)fullWidth-多屏的完整宽度fullHeight-多屏的完整高度x-子相机水平偏移值y-子相机垂直偏移值width-子相机的显示宽度height-子相机的显示高度举个例子:我想界面显示3X2的6个画面,依次是ABCDEFG,每个单独的画面的尺寸是1920X1080,+---+---+---+| A | B | C |+---+---+---+| D | E | F |+---+---+---+可以这样来写:var w =1920;var h =1080;var fullWidth = w *3;var fullHeight = h *2;// A camera.setViewOffset( fullWidth, fullHeight, w *0, h *0, w, h );// B camera.setViewOffset( fullWidth, fullHeight, w *1, h *0, w, h );// C camera.setViewOffset( fullWidth, fullHeight, w *2, h *0, w, h );// D camera.setViewOffset( fullWidth, fullHeight, w *0, h *1, w, h );// E camera.setViewOffset( fullWidth, fullHeight, w *1, h *1, w, h );// F camera.setViewOffset( fullWidth, fullHeight, w *2, h *1, w, h );.updateProjectionMatrix()相机有任何参数改变,都必须调用一下这个函数.toJSON返回相机的json格式
    

      正交相机OrthographicCamera正交相机重在表现物体的实际尺寸,没有近大远小的效          果;一般是用在制图、建模上面。

      构造函数:

var camera = new THREE.OrthographicCamera(left, right, top, bottom, near, far);

      参数:

left — 可被渲染空间的左平面,比这个左侧边界更远的对象不会被渲染
right — 可被渲染空间的右平面
top — 可被渲染空间的最上面
bottom — 可被渲染空间的最下面
near — 基于相机所在的位置,从这一点开始渲染场景
far — 基于相机所在的位置,一直渲染到场景中的这一点
这六个参数分别代表正交相机拍摄到的空间的六个面的位置,这六个面围成一个长方体,我们称为视景体 (Frustum)。只有在视景体内部的物体才可能显示在屏幕上,而视景体外的物体会在显示之前被裁减掉。属性:.zoom — 获取和设置相机缩放因子。.left, .right, .top, .bottom, .near, .far — 相机视椎体左面,右面,上面,下面,前面,后面。方法:.setViewOffset( fullWidth, fullHeight, x, y, width, height )fullWidth — 多视图设置的全宽fullHeight — 多视图设置的全高x — 副摄像头的水平偏移y — 副摄像头的垂直偏移width — 副摄像头的宽度height — 副摄像头的高度该方法用于在一个较大的视椎体中设置视图偏移。这对于多窗口或多监视器/多机设置是有用的。

  太多了,相机到后面也单独开一章。

5.渲染器renderer

this.render = function ( scene, camera, renderTarget, forceClear ) {
        // 前面是一些参数的校验,这里省略
        // 1.reset caching for this frame
        ......
        // 2.update scene graph

        if ( scene.autoUpdate === true ) scene.updateMatrixWorld();

        // 3.update camera matrices and frustum

        if ( camera.parent === null ) camera.updateMatrixWorld();

        .....

        // 4\. init WebGLRenderState

        currentRenderState = renderStates.get( scene, camera );
        currentRenderState.init();

        scene.onBeforeRender( _this, scene, camera, renderTarget );
        // 5.视景体矩阵计算,为相机的投影矩阵与相机的世界矩阵的逆矩阵的叉乘?
        _projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
        _frustum.setFromMatrix( _projScreenMatrix );

        _localClippingEnabled = this.localClippingEnabled;
        _clippingEnabled = _clipping.init( this.clippingPlanes, _localClippingEnabled, camera );
       // 6.WebGLRenderList 的初始化
        currentRenderList = renderLists.get( scene, camera );
        currentRenderList.init();

        projectObject( scene, camera, _this.sortObjects );

        ......

        // 7\. shadow 的绘制

        if ( _clippingEnabled ) _clipping.beginShadows();

        var shadowsArray = currentRenderState.state.shadowsArray;

        shadowMap.render( shadowsArray, scene, camera );

        currentRenderState.setupLights( camera );

        if ( _clippingEnabled ) _clipping.endShadows();

        //

        if ( this.info.autoReset ) this.info.reset();

        if ( renderTarget === undefined ) {

            renderTarget = null;

        }

        this.setRenderTarget( renderTarget );

        // 8.背景的绘制

        background.render( currentRenderList, scene, camera, forceClear );

        // 9.render scene

        var opaqueObjects = currentRenderList.opaque;
        var transparentObjects = currentRenderList.transparent;

        if ( scene.overrideMaterial ) {
                        // 10.强制使用场景的材质 overrideMaterial 来统一 render 物体。
            var overrideMaterial = scene.overrideMaterial;

            if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera, overrideMaterial );
            if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera, overrideMaterial );

        } else {
                        // 11.分别对 opaque 和 transparent 的物体进行 render
            // opaque pass (front-to-back order)

            if ( opaqueObjects.length ) renderObjects( opaqueObjects, scene, camera );

            // transparent pass (back-to-front order)

            if ( transparentObjects.length ) renderObjects( transparentObjects, scene, camera );

        }

        // Generate mipmap if we're using any kind of mipmap filtering
                .....

        // Ensure depth buffer writing is enabled so it can be cleared on next render

        state.buffers.depth.setTest( true );
        state.buffers.depth.setMask( true );
        state.buffers.color.setMask( true );

        state.setPolygonOffset( false );

        scene.onAfterRender( _this, scene, camera );

        ......
        currentRenderList = null;
        currentRenderState = null;

    };

render() 是渲染的核心,粗略地看它做了大概以下的事情。

  1. reset caching for this frame。
  2. update scene graph。
  3. update camera matrices and frustum。
  4. init WebGLRenderState。
  5. 视景体矩阵计算,为相机的投影矩阵与相机的世界矩阵的逆矩阵的叉乘。
  6. WebGLRenderList 的初始化。
  7. shadow 的绘制。
  8. 背景的绘制。
  9. render scene。
  10. 如果overrideMaterial,则强制使用场景的材质 overrideMaterial 来统一 render 物体。
  11. 分别对 opaque 和 transparent 的物体进行 render。

6.动画animate

let animate = () => {        requestAnimationFrame(animate)        mesh.rotation.x += 0.01        mesh.rotation.y += 0.02        renderer.render(scene, camera)      }      animate();

window.requestAnimationFrame()方法 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

下面改下动画参数看下效果

let animate = () => {        requestAnimationFrame(animate)        //mesh.rotation.x += 0.01        mesh.rotation.y += 0.2        renderer.render(scene, camera)      }      animate();

旋转的方向是跟根据设置三维空间的方向,左手笛卡尔坐标系。

总结:

相关联的内容太多,总是容易发散,下一篇重点讲下相机

期待您的关注点赞

WEBGL探索之路 (二)

--webgl场景构建 WEBGL探索之路 (一)--认识webgl

reactVR