说明:今天主要演示环境搭建,几何体的组成方式
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.网络模型
-
THREE.BoxGeometry(60, 40, 40)立方体对象(长宽高);BoxGeometry后面还有三个参数widthSegments, heightSegments, deptSegments是对应长宽高的分段,在使用线模式({wireframe:true})进行渲染的时候,可以看到效果如下先上代码
-
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段
-
材质material:THREE.MeshLambertMaterial (内容太多,后面单开一张来介绍)
-
相机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() 是渲染的核心,粗略地看它做了大概以下的事情。
- reset caching for this frame。
- update scene graph。
- update camera matrices and frustum。
- init WebGLRenderState。
- 视景体矩阵计算,为相机的投影矩阵与相机的世界矩阵的逆矩阵的叉乘。
- WebGLRenderList 的初始化。
- shadow 的绘制。
- 背景的绘制。
- render scene。
- 如果overrideMaterial,则强制使用场景的材质 overrideMaterial 来统一 render 物体。
- 分别对 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();
旋转的方向是跟根据设置三维空间的方向,左手笛卡尔坐标系。
总结:
相关联的内容太多,总是容易发散,下一篇重点讲下相机
期待您的关注点赞