当前位置: 首页 > news >正文

上海整站优化群晖 搭建wordpress

上海整站优化,群晖 搭建wordpress,自建服务器做网站要备案,wordpress .ds_store场景、相机和渲染器 Three.js整个系统主要包含场景Scene、相机Camera和WebGL渲染器WebGLRenderer三大块#xff0c;其中场景又包含模型和光源。WebGL渲染器的主要作用就是把相机对应场景渲染出来#xff0c;显示在网页Cnavas画布上。 Three.js源码 Three.js各个构造函数对应…场景、相机和渲染器 Three.js整个系统主要包含场景Scene、相机Camera和WebGL渲染器WebGLRenderer三大块其中场景又包含模型和光源。WebGL渲染器的主要作用就是把相机对应场景渲染出来显示在网页Cnavas画布上。 Three.js源码 Three.js各个构造函数对应的源码位于three.js-master中src文件中three.js-master\build目录下的three.js文件是通过src目录下的所有代码块打包而来。 想了解学习3D引擎Three.js整个系统的原理自然需要Three.js引擎three.js-master\src目录下的所有源码。 JavaScript和WebGL基础 Three.js是通过JavaScript语言对WebGL API和着色器代码封装后得到的3D引擎阅读Three.js源码自然需要有一定的JavaScript和WebGL基础。 了解Three.js每个API对应构造函数是如何封装构造函数的参数是如何设置属性的对象的属性和方法是如何封装的对象的方法是如何改变对象属性的类与类之间的继承关系这些都需要你对JavaScript的语言有很深的了解。 three.js-master\src\renderers目录下都是与WebGL渲染器WebGLRenderer相关的代码块WebGL渲染器代码主要对WebGL API和着色器代码进行了封装。 three.js-master\src\renderers\shaders目录下ShaderChunk和ShaderLib文件主要是一系列具有特定功能的着色器GLSL代码块。 开发者编程常用的WebGL渲染器对应的源码就是renderers目录下WebGLRenderer.js文件该文件会调用renderers目录下shader和webgl两个文件中的代码。 类封装案例 比如Mesh.js文件部分源码 // 引入Mesh.js依赖的js文件 ... import { Vector3 } from ../math/Vector3.js; import { Matrix4 } from ../math/Matrix4.js; import { Object3D } from ../core/Object3D.js; ... // 声明一个构造函数Mesh function Mesh( geometry, material ) {// 通过call方法Meshlz可以批量继承Object3D类的方法和属性// 网格模型Mesh的基类是Object3DObject3D.call( this );this.type Mesh;// 如果构造函数没有参数系统设置属性默认// 如果系统有参数直接把参数设置为属性对应的属性值this.geometry geometry ! undefined ? geometry : new BufferGeometry();this.material material ! undefined ? material : new MeshBasicMaterial( { color: Math.random() * 0xffffff } );} 源码阅读技巧 编辑器查找关键词比如查找src目录下的那个.js文件封装了WebGL APIgl.drawArrays() 比如查找一个对象的方法方法.cross()。 src目录下源码和three.js文档目录是一一对应关系比如src目录下math文件中都是Three.js文档Math分类下API对应的源码文件。 渲染器.render()方法和WebGL APIgl.drawArrays() 如果你有原生WebGL基础阅读Threejs源码将会很容易理解一些对象的方法和属性比如 Three.js渲染器的渲染方法.render()执行该方法就相当于执行WebGL 绘制方法gl.drawArrays()。 在原生WebGL代码中执行绘制方法gl.drawArrays()就会在Cnvas画布上绘制一帧图片自然Threejs的渲染方法.render()同理周期性执行 绘制方法gl.drawArrays()可以更新帧缓冲区数据也就是更新Canvas画布显示图像.render()方法同理可以实现一样的效果。 // 渲染函数 function render() {renderer.render(scene, camera); //执行渲染操作mesh.rotateY(0.01);//每次绕y轴旋转0.01弧度requestAnimationFrame(render);//请求再次执行渲染函数render渲染下一帧 } render();WebGLBufferRenderer.js文件封装了原生WebGL方法gl.drawArrays()。 // WebGLBufferRenderer.js文件源码 // 提供了一个render方法在WebGLRenderer.js中会调用 function WebGLBufferRenderer( gl, extensions, info ) {...function render( start, count ) {// 封装了原生WebGL APIgl.drawArrays()gl.drawArrays( mode, start, count );info.update( count, mode );}...this.render render; } WebGL渲染器源码文件WebGLRenderer.js中的渲染方法.render()调用了WebGLBufferRenderer.js文件封装的原生WebGL方法gl.drawArrays()。 gl.drawArrays——WebGLBufferRenderer.js——renderBufferDirect——renderObject——renderObjects——render import {WebGLBufferRenderer} from ./webgl/WebGLBufferRenderer.js; bufferRenderer new WebGLBufferRenderer(_gl, extensions, info); var renderer bufferRenderer;this.renderBufferDirect function(camera, fog, geometry, material, object, group) { ... renderer.render(drawStart, drawCount); ... }function renderObject(object, scene, camera, geometry, material, group) { _this.renderBufferDirect(camera, scene.fog, geometry, material, object, group); }function renderObjects(renderList, scene, camera...) {...renderObject(object, scene, camera, geometry, material, group);... }this.render function(scene, camera...) {...renderObjects(opaqueObjects, scene, camera);... }WebGL渲染器WebGLRenderer 想要了解Three.js系统的原理简单点说就是了解WebGL渲染器渲染解析场景和相机的原理如果想深入了解渲染器的渲染原理不是短时间内能够做到的本节课主要目的是假设你有一定的原生WebGL基础然后对Three.js的渲染器进行简单介绍后面的课程会进行深入的介绍。 .domElement属性 如果想通过WebGL渲染一个三维场景需要HTML的Canvas画布元素实现通过渲染器构造函数WebGLRenderer创建一个渲染器对象 如果构造函数参数没有设置canvas对象系统会自动创建一个Cnavas元素。 通过canvas元素返回WebGL上下文gl对象才能调用相关的WebGL API //通过getElementById()方法获取canvas画布 var canvasdocument.getElementById(webgl); //通过方法getContext()获取WebGL上下文 var glcanvas.getContext(webgl); ... gl.enableVertexAttribArray(aposLocation); ... gl.drawArrays(gl.LINE_LOOP,0,4); ...WebGLRenderer.js源码对.domElement属性的相关封装 function WebGLRenderer(parameters) {...parameters parameters || {};//如果canvas画布没有通过参数对象parameters的canvas属性设置通过API创建一个var _canvas parameters.canvas ! undefined ? parameters.canvas : document.createElementNS(http://www.w3.org/1999/xhtml, canvas),...把_canvas赋值给WebGL渲染器对象的domElement属性this.domElement _canvas;}通过渲染器.domElement属性可以把Three.js的canvas画布插入到html任何一个元素中可以在整个body页面上全局显示也可以插入一个div元素中局部显示注意canvas画布尺寸设置。 // 创建渲染器对象var renderer new THREE.WebGLRenderer();renderer.setSize(width, height);//body元素中插入Threejs创建的包含canvas对象document.body.appendChild(renderer.domElement);.setSize(width, height)方法 WebGLRenderer.js源码对.setSize()方法的相关封装 ... // pixelRatio像素比率 _pixelRatio 1, ... this.setSize function(width, height, updateStyle) { ..._canvas.width width * _pixelRatio;_canvas.height height * _pixelRatio;if (updateStyle ! false) {_canvas.style.width width px;_canvas.style.height height px;} ... };全屏设置也就是canvas画布宽高度和窗口尺寸一样 var width window.innerWidth; //窗口宽度var height window.innerHeight; //窗口高度var renderer new THREE.WebGLRenderer();// 设置渲染尺寸本质就是设置canvas元素宽高度renderer.setSize(width, height);局部渲染通过.setSize()方法设置canvas画布的宽高度像素 var width window.innerWidth; //窗口宽度var height window.innerHeight; //窗口高度var renderer new THREE.WebGLRenderer();// 设置渲染尺寸本质就是设置canvas元素宽高度renderer.setSize(300, 300);帧缓冲区的相关封装 学习Three.js与帧缓冲区相关的封装首先要了解WebGL中帧缓冲区的概念帧缓冲区包含颜色缓冲区、深度缓冲区、模板缓冲区颜色缓冲区存储片元的颜色数据也就是像素数据深度缓冲存储片元的深度数据用于WebGL渲染流程中的深度测试环节被遮挡的片元会被剔除不会显示在canvas画布上。 渲染器方法.clear(color,depth,stencil) 原生WebGL方法gl.clear()用来清除帧缓冲区数据 // 清除颜色缓冲区数据 gl.clear(gl.COLOR_BUFFER_BIT) // 清除深度缓冲区数据 gl.clear(gl.DEPTH_BUFFER_BIT) // 清除模板缓冲区数据 gl.clear(gl.STENCIL_BUFFER_BIT)// 清除帧缓冲区的颜色、深度和模板缓冲中数据 gl.clear(gl.DEPTH_BUFFER_BIT | gl.COLOR_BUFFER_BIT | gl.STENCIL_BUFFER_BIT);WebGLRenderer.js源码对渲染器方法.clear()的封装 this.clear function(color, depth, stencil) {// “” 和 “|” 是位运算操作符var bits 0;if (color undefined || color) bits | _gl.COLOR_BUFFER_BIT;if (depth undefined || depth) bits | _gl.DEPTH_BUFFER_BIT;if (stencil undefined || stencil) bits | _gl.STENCIL_BUFFER_BIT;_gl.clear(bits);};渲染器方法.clearDepth() 清除帧缓冲区中深度缓冲区中的片元深度数据 renderer.clearDepth()// WebGLRenderer.js源码 this.clearDepth function() {this.clear(false, true, false); };渲染器属性.autoClear Three.js渲染器默认情况下本次执行render方法之前会把上次执行render方法后帧缓冲区中的数据清除 autoClear默认值true设置为false执行render方法的时候不会自动清除上次渲染帧缓冲区中的数据 renderer.autoClear false;renderers目录下渲染器相关代码块介绍 前面说过想了解Threejs的渲染器是如何解析场景和相机对象然后进行渲染的除了需要了解场景和相机对象的结构外还需要阅读渲染器文件WebGLRenderer.js源码来了解渲染解析过程。 从this.render开始阅读 渲染器渲染器的时候会把场景和相机作为参数然后调用.render()方法所以想了解Threejs渲染器的解析过程自然需要在WebGLRenderer.js文件中查找到.render()方法封装的源码可以通过查找关键词this.render找到。 renderer.render(scene, camera);由于Threejs渲染器是很复杂的render函数中会调用其它经过多层封装的函数自然第一次阅读render函数的源码基本大多数代码都不是很好理解。 材质Material和着色器shader——shaders目录 Three.js渲染器解析场景中网格模型材质Material的时候会调用材质对象对应的顶点着色器和片元着色器代码然后经过编译处理后在GPU上执行。 着色器代码文件目录是系three.js-master\src\renderers\shadersshaders目录下有两个着色器代码的文件ShaderChunk和ShaderLib。 ShaderChunk目录下的着色器代码文件.glsl都是具有特定功能的模块ShaderLib目录下的着色器文件会通过#include ShaderChunk中着色器文件名调用ShaderChunk目录下特定功能的着色器代码块构建出来具有具有特定功能的顶点着色器文件和片元着色器文件。 点材质PointsMaterial顶点着色器文件points_vert.glsl、片元着色器文件points_frag.glsl基础网格材质MeshBasicMaterial顶点着色器文件meshbasic_vert.glsl、片元着色器文件meshbasic_frag.glsl高光网格材质MeshPhongMaterial顶点着色器文件meshphong_vert.glsl、片元着色器文件meshphong_frag.glsl ShaderChunk.js用来获得ShaderChunk和ShaderLib文件中的着色器代码 ShaderLib.js设置好点、线、网格材质对应的uniforms变量值、顶点着色器代码、片元着色器代码 UniformsLib.js、UniformsUtils.js着色器中uniform变量相关的对象 调用webgl目录下函数 WebGLRenderer.js会通过import和from关键字引入webgl目录下的函数然后通过new关键字实例化返回一个对象webgl目录下文件中封装的函数创建对象的方式有多种比如工厂模式、构造函数模式、原型模式… 构造函数模式比如WebGLTextures.js…工厂模式比如WebGLGeometries.js、WebGLObjects、WebGLAttributes… webgl目录下封装的函数创建的对象具有一些方法和属性用于完成特定的功能。 - WebGLGeometries.js解析几何体对象Geometry或BufferGeometry - WebGLLights解析光源对象 - WebGLAttributes.js解析BufferAttribute对象也就是说从BufferAttribute对象提取顶点数据把顶点数据传入创建的顶点缓冲区 - WebGLShader.js创建着色器对象 - WebGLProgram.js创建程序对象 - WebGLUniforms.js: unifomr变量传值相关 - WebGLInfo.js记录渲染器的渲染信息比如渲染了多少帧frame ...webgl渲染器代码模块的方法和属性 以WebGLInfo.js为例分析WebGLInfo.js是一个工厂函数通过new关键词实例化该函数返回一个具有特定属性和方法的对象。 function WebGLInfo( gl ) {var memory {...};var render {frame: 0,calls: 0,triangles: 0,...};// update函数function update( count, mode, instanceCount ) {...}// reset函数function reset() {...}return {// WebGLInfo对象属性memory、render、programs、autoResetmemory: memory,render: render,programs: null,autoReset: true,// WebGLInfo对象方法update、resetreset: reset,update: update};}渲染器文件WebGLRenderer.js中调用WebGLInfo对象的方法和属性 import {WebGLInfo} from ./webgl/WebGLInfo.js; ... info new WebGLInfo(_gl); ... _this.info info; ... if (this.info.autoReset) this.info.reset();判断对象类型的属性 Threejs渲染器解析Threejs对象的时候需要判断它是什么类型所以通过Threejs构造函数创建对象的时候会初始化设置一些属性用于判断对象的类型比如type属性获得对象构造函数的名字isMesh、isLine、isGroup等属性用来判断一个对象是那种对象以便于Threejs渲染器进行归类处理。 Three.js封装WebGL顶点数据 如果已有一定的WebGL基础自然都有顶点数据的概念比如顶点位置、顶点法向量、顶点UV坐标、顶点颜色等等。 如果直接使用WebGL编写程序肯定比较麻烦所以对WebGL进行一定的封装这样的话对于开发者肯定会更有好因此这节课来讲解Threejs是如何对象对WebGL中顶点数据进行封装和组织。 相比很多Geometry和BufferGeometry有什么区别本节课也可以回答你。 原生WebGL代码 /**创建顶点位置数据数组data,Javascript中小数点前面的0可以省略**/ var datanew Float32Array([.5,.5,.5,-.5,.5,.5,-.5,-.5,.5,.5,.5,.5,-.5,-.5,.5,.5,-.5,.5, //面1.5,.5,.5,.5,-.5,.5,.5,-.5,-.5,.5,.5,.5,.5,-.5,-.5,.5,.5,-.5, //面2.5,.5,.5,.5,.5,-.5,-.5,.5,-.5,.5,.5,.5,-.5,.5,-.5,-.5,.5,.5, //面3-.5,.5,.5,-.5,.5,-.5,-.5,-.5,-.5,-.5,.5,.5,-.5,-.5,-.5,-.5,-.5,.5,//面4-.5,-.5,-.5,.5,-.5,-.5,.5,-.5,.5,-.5,-.5,-.5,.5,-.5,.5,-.5,-.5,.5,//面5.5,-.5,-.5,-.5,-.5,-.5,-.5,.5,-.5,.5,-.5,-.5,-.5,.5,-.5,.5,.5,-.5 //面6 ]); /**创建顶点颜色数组colorData**/ var colorData new Float32Array([1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0,//红色——面11,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0,//红色——面21,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0,//红色——面31,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0,//红色——面41,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0,//红色——面51,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,0,0 //红色——面6 ]); /***顶点法向量数组normalData**/ var normalData new Float32Array([0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,//z轴正方向——面11,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,//x轴正方向——面20,1,0,0,1,0,0,1,0,0,1,0,0,1,0,0,1,0,//y轴正方向——面3-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,//x轴负方向——面40,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,//y轴负方向——面50,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1,0,0,-1//z轴负方向——面6 ]); BufferAttribute对象 Three.js通过BufferAttribute对象对顶点数据进行了封装该对象.array属性的值就是一个存储了一系列顶点数据的数组。 BufferAttribute对象可以表示一组顶点位置数据可以表示一组顶点法向量数据可以表示一组顶点颜色数据… //类型数组创建顶点数据 var vertices new Float32Array([0, 0, 0, //顶点1坐标50, 0, 0, //顶点2坐标0, 100, 0, //顶点3坐标0, 0, 10, //顶点4坐标0, 0, 100, //顶点5坐标50, 0, 10, //顶点6坐标 ]); // 创建属性缓冲区对象表示一组顶点坐标 // 参数3表示类型化数组vertices中的顶点数据3个为一组表示一个顶点的xyz坐标 var attribue new THREE.BufferAttribute(vertices, 3); console.log(查看顶点数据,attribue.array); console.log(attribue.itemSize,attribue.itemSize);可以通过构造函数BufferAttribute参数设置.array和.itemSize属性也可以直接设置这两个属性的值。 var attribue new THREE.BufferAttribute(); // 设置顶点数据 attribue.array vertices; // 设置多少个元素为一组与WebGL的一个方法gl.vertexAttribPointer()第二个参数相关。 // 比如UV坐标只有xy需要设置itemSize2比如顶点坐标只设置xyz使用系统默认值也可以只设置itemSize2 attribue.itemSize 3;Three.js构造函数BufferAttribute的名字由Buffer和attribute两个单词组成顶点数据需要传入WebGL APIgl.createBuffer()创建顶点缓冲区中这是BufferAttribute类名Buffer部分的来源顶点数据在着色器代码中对应了attribute变量比如attribute vec4 position;声明了一个顶点位置变量对应缓冲区中的顶点位置数据attribute vec4 normal;声明了一个顶点法向量变量对应顶点缓冲区中的顶点颜色数据这是BufferAttribute类名Attribute部分的来源BufferAttribute对象可以翻译为顶点缓冲属性对象。 缓冲几何体对象BufferGeometry 一个几何体对象BufferGeometry可以理解为所有种类顶点数据的一个集合BufferGeometry对象.attributes的属性包含了所有顶点数据通过BufferGeometry.attributes.position设置或返回顶点位置数据通过BufferGeometry.attributes.uv设置或返回顶点UV坐标数据通过BufferGeometry.attributes.normal设置或返回顶点法向量数据等等。.attributes.position、.attributes.uv、.attributes.normal的属性值都是包含顶点数据的BufferAttribute对象 var geometry new THREE.BufferGeometry(); //声明一个缓冲几何体对象//类型数组创建顶点位置position数据 var vertices new Float32Array([0, 0, 0, //顶点1坐标50, 0, 0, //顶点2坐标0, 100, 0, //顶点3坐标0, 0, 10, //顶点4坐标0, 0, 100, //顶点5坐标50, 0, 10, //顶点6坐标 ]); // 创建属性缓冲区对象 var attribue new THREE.BufferAttribute(vertices, 3); //3个为一组作为一个顶点的xyz坐标 // 设置几何体attributes属性的位置position属性 geometry.attributes.position attribue;//类型数组创建顶点颜色color数据 var colors new Float32Array([1, 0, 0, //顶点1颜色0, 1, 0, //顶点2颜色0, 0, 1, //顶点3颜色1, 1, 0, //顶点4颜色0, 1, 1, //顶点5颜色1, 0, 1, //顶点6颜色 ]); // 设置几何体attributes属性的颜色color属性 //3个为一组,表示一个顶点的颜色数据RGB geometry.attributes.color new THREE.BufferAttribute(colors, 3);创建特定几何形状的缓冲类型API的基类都是BufferGeometry,比如BoxBufferGeometry,这些API本质上都是通过一定的算法自动化生成各种几何形状对应的顶点数据。 //创建一个缓冲区类型立方体 var geometry new THREE.BoxBufferGeometry(100, 100,100); console.log(几何体所有顶点数据,geometry.attributes); console.log(几何体顶点位置数据,geometry.attributes.position); console.log(几何体顶点法向量数据,geometry.attributes.normal); console.log(几何体顶点索引数据,geometry.index);BufferGeometry的顶点索引属性.index 如果你了解WebGL的顶点索引绘制肯定对顶点索引数据并不陌生这样的话自然更容易理解该属性BufferGeometry.index的属性值和.attributes.position一样都是包含顶点数据的BufferAttribute对象。 // 顶点索引数组var indexes new Uint8Array([0,1,2,3,4,5,6,7,0,4,1,5,2,6,3,7]);//创建缓冲区对象var indexesBuffergl.createBuffer();//绑定缓冲区对象gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexesBuffer);//索引数组indexes数据传入缓冲区gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indexes,gl.STATIC_DRAW);Geometry和BufferGeometry区别 几何体对象Geometry和缓冲几何体对象BufferGeometry表达的信息是一样的都是对几何体顶点数据的封装只是数据结构不同也就是属性不同。Geometry和BufferGeometry可以相互转化Three.js渲染器解析几何体会从几何体对象提取顶点数据传入WebGL顶点缓冲区如果解析的是BufferGeometry对象直接访问.attributes属性提取顶点数据就可以如果几何体Geometry对象Three.js渲染器会先把Geometry转化BufferGeometry对象然后提取顶点数据关于Three.js是如何解析几何体传入顶点缓冲区然后与顶点着色器中的attribute变量绑定在后面的课程会详细介绍Threejs渲染器是如何解析几何体的本节课主要是讲解Threejs是如何封装顶点数据的不详细讲解Three.js如何解析顶点封装后的得到的几何体对象。 Geometry数据结构 //创建一个立方体几何对象Geometry var geometry new THREE.BoxGeometry(100, 100, 100); console.log(geometry); console.log(几何体顶点位置数据,geometry.vertices); console.log(三角面数据,geometry.faces);解析几何体提取顶点数据 上节课讲解了如何把WebGL的顶点数据封装为Three.js的几何体对象这节课就来讲解Three.js的渲染器在渲染的时候如何解析几何体对象提取顶点数据然后调用WebGL API创建顶点缓冲区并把创提取的顶点数据传入创建的顶点缓冲区。 本章节的内容是给大家讲解Three.js渲染器是如何解析场景和渲染器对象的本节课讲解解析的一个环节也就是Threejs如何解析几何体并创建相应的顶点缓冲区。 原生WebGL 原生WebGL通过gl.createBuffer()创建一个顶点缓冲区对象用来存储顶点位置、顶点颜色、顶点法向量等数据。如果你理解了这一段代码自然就很容易理解Three.js的几何体对象和相应的缓冲区。 // 顶点位置数据 var datanew Float32Array([0.5,0.5,0.3...]);// 创建缓冲区buffer传入顶点位置数据data var buffergl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER,buffer); gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW); gl.vertexAttribPointer(aposLocation,3,gl.FLOAT,false,0,0);整体解析过程简介 Three.js渲染器解析几何体对象会从几何体对象提取顶点数据传入WebGL顶点缓冲区的时候如果解析的是BufferGeometry对象直接访问.attributes属性提取顶点数据就可以比如获得顶点位置数据通过.attributes.position.array获得顶点数据。如果Three.js渲染器解析的几何体是Geometry对象会先把Geometry对象转化为BufferGeometry对象然后再解析。 顶点颜色属性Geometry.colors 点模型Points、线模型Line对象与网格模型Mesh对象的几何体结构Geometry略有不同。 Geometry.colors属性包含的顶点颜色数据在点模型Points、线模型Line中使用几何体对象的该属性在网格模型Mesh中不起作用。 网格模型Mesh使用几何体对象三角面Face3的顶点颜色属性Face3.vertexColors设置颜色。 Geometry转化为BufferGeometry 通过BufferGeometry.setFromObject(object)方法可以把参数可以把object模型对象的几何体geometry转化为BufferGeometry点模型Points和线模型Line使用一套解析转化规则网格模型Mesh使用一种转化规则。 对于网格模型的几何体Geometry转化为BufferGeometry的时候需要先把Geometry对象转化为直接几何体对象DirectGeometry然后再转化为BufferGeometry对象。 相关函数 WebGLAttributes.js、WebGLGeometries.js和WebGLObjects.js是工厂函数执行这三个函数都会返回一个具有特定方法的对象WebGLObjects.js会调用WebGLGeometries对象的方法WebGLGeometries.js会调用WebGLAttributes对象的方法。 WebGLAttributes.js .update(BufferAttribute)方法 解析BufferAttribute对象也就是说从BufferAttribute对象的.array属性提取顶点数据把顶点数据传入WebGL顶点缓冲区对gl.createBuffer()、gl.、bufferData()等WebGL API进行了封装。 WebGLGeometries.js .get()方法 参数:.get(Object,Object.geometry) 如果Object.geometry是BufferGeometry直接返回如果是Geometry会转化为BufferGeometry点线模型和网格模型的Geometry转化规则不同所以参数需要传入Object代码需要判断Object是Points和Line还是Mesh。 .update(BufferGeometry)方法 通过BufferGeometry的.index和.attributes属性获得包含顶点数据的BufferAttribute对象然后BufferAttribute作为参数调用WebGLAttributes.update()方法提取顶点数据并传入顶点缓冲区。 WebGLObjects.js .update(Object)方法 从模型对象Object提取几何体数据也就是模型的几何体属性 Object.geometry然后调用WebGLGeometries.get()方法并把Object和Object.geometry作为参数直接get方法后返回BufferGeometry然后调用WebGLGeometries.update()方法把BufferGeometry作为参数。 WebGLRenderer.js 场景中遍历获得的对象object如果是Points、Line或Mesh模型调用WebGLObjects.update()方法并把object作为参数。 WebGLRenderer.js import {WebGLAttributes} from ./webgl/WebGLAttributes.js; import {WebGLGeometries} from ./webgl/WebGLGeometries.js; import {WebGLObjects} from ./webgl/WebGLObjects.js;var attributes, geometries, objects;attributes new WebGLAttributes(_gl); // WebGLAttributes作为WebGLGeometries参数 geometries new WebGLGeometries(_gl, attributes, info); // WebGLGeometries作为WebGLObjects参数 objects new WebGLObjects(geometries, info);function projectObject(object, camera, sortObjects) {...else if (object.isMesh || object.isLine || object.isPoints) {var geometry objects.update(object);}...// 递归算法遍历对象var children object.children;for (var i 0, l children.length; i l; i) {projectObject(children[i], camera, sortObjects);} } // 渲染方法中调用projectObject this.render function(scene, camera, renderTarget, forceClear) {...// 递归遍历场景对象对于其中的点、线和网格模型需要解析它们的几何体提取顶点数据并传入顶点缓冲区projectObject(scene, camera, _this.sortObjects);... }Threejs层级模型的封装和解析 通过前面课程的学习你对Threejs的层级模型应该有一定的认识具体说也就是根节点场景对象Scene和它的子孙对象构成的树结构。 基类Object3D 基类Object3D封装了用于构建树结构的方法.add()和属性.children 继承 场景对象Scene、组对象Group基类是Object3DObject3D也是网格Mesh、点Points、线Line等模型对象的基类。 可能有些学员会思考组对象Group和Object3D有什么区别有些时候会混合使用其实看一下源码就能明白Group的基类是Object3D基本没有扩展增加属性和方法功能上没有什么区别主要是Group更语义化看到名字就知道什么意思。 网格模型对象作为场景对象的子对象 var mesh new THREE.Mesh(geometry, material); scene.add(mesh);点光源添加到场景中 var point new THREE.PointLight(0xffffff); point.position.set(400, 200, 300); scene.add(point);网格模型插入组对象组对象插入场景中。 var leftEyeMesh new THREE.Mesh(); var rightEyeMesh new THREE.Mesh();var headGroup new THREE.Group(); headGroup.add(leftEyeMesh, rightEyeMesh);scene.add(headGroup); console.log(查看场景子对象,scene.children); console.log(查看headGroup的子对象,headGroup.children);递归算法 如果像你想通过阅读WebGLRenderer.js源码了解Threejs渲染器是如何解析遍历场景对象Scene的所有子孙对象的需要了解一下递归算法如果你有数据结构和算法方面的基础自然很熟悉如果没有相关的基础最后学习了解一下。 树结构 树结构节点名字基类Object3D的.name属性给节点命名递归遍历树结构递归遍历方法.traverse( callback )查找树节点.getObjectByName(name)递归遍历对象的子对象返回与参数设置名字一致的对象 WebGL渲染器函数projectObject WebGLRenderer.js源码,在render函数中会调用projectObject函数 function projectObject(object, camera, sortObjects) { ... // 封装了递归算法可以用来遍历树结构对象比如场景对象Scenevar children object.children;// 递归算法遍历场景对象for (var i 0, l children.length; i l; i) {projectObject(children[i], camera, sortObjects);} }WebGLRenderer.js源码,projectObject函数会递归遍历场景对象进行分类 this.renderfunction(){...// 遍历场景对象并对场景对象中的节点进行分类比如光源对象比如模型对象projectObject(scene, camera, _this.sortObjects);... } projectObject函数对象递归遍历到的子对象节点进行分类处理然后WebGL渲染器在对象分类好的不同对象进行渲染器解析关于进一步的渲染解析过程这里不讲解。 // currentRenderList对象用于存储点、线和和网格模型的对象 var currentRenderList renderLists new WebGLRenderLists(); currentRenderList renderLists.get(scene, camera); // currentRenderList对象用于存储光源、点精灵等对象 var currentRenderStaterenderStates new WebGLRenderStates(); currentRenderState renderStates.get(scene, camera);// 对象Scene的后代节点对象进行分类处理 function projectObject(object, camera, sortObjects) { ...if (visible) {// 判断对象是不是光源对象是的话插入WebGL渲染状态对象的state属性中if (object.isLight) {//光源信息插入到currentRenderState对象的.state.lightsArray属性中currentRenderState.pushLight(object);}// 判断对象是不是精灵对象else if (object.isSprite) {//光源信息插入到currentRenderState对象的.state.spritesArray属性中currentRenderState.pushSprite(object);}else if (object.isMesh || object.isLine || object.isPoints) {// 把模型对象相关信息批量存储到currentRenderList对象currentRenderList.push(object, geometry, material, _vector3.z, null);}} }本地矩阵.materix和世界矩阵.matrixWorld 一个对象的本地矩阵.materix包含了该对象的旋转、平移和缩放变换本地矩阵是平移矩阵、缩放矩阵和旋转矩阵的乘积。 一个对象的世界矩阵.matrixWorld是该对象本地矩阵及其所有所有祖宗对象本地矩阵的乘积或者每一个对象的世界矩阵是对象本地矩阵和父对象的世界矩阵的乘积。 Object3D Object3D是网格Mesh、点Points、线Line等模型对象的基类组对象Group也是Object3D对象的基类。 Object3D封装本地矩阵.matrix、位置.position、缩放.scale、角度.rotation等属性封装了旋转相关方法.rotateX()、.rotateZ()平移相关方法.translateX()、.translateZ()。 四元数属性.quaternion和角度属性.rotation 两个属性表达的含义是一样的都是旋转相关的信息都会被转化为旋转矩阵。 方法改变属性 对于Three.js一些对象的属性可以直接设置属性值也可以通过方法改变属性值。 执行旋转方法.rotateZ()查看查看角度属性.rotation属性值欧拉对象z属性的变化 // 一个网格模型对象基类是Object3D var mesh new THREE.Mesh() // 绕z轴旋转 mesh.rotateZ(Math.PI) console.log(查看角度属性值的变化,mesh.rotation); console.log(查看四元数属性变化,mesh.quaternion);执行平移方法.translateX()查看查看位置.position属性值x分量变化 // 一个网格模型对象基类是Object3D var mesh new THREE.Mesh() // 沿着x轴平移100 mesh.translateX(100) console.log(查看位置属性的变化,mesh.position);更新本地矩阵属性.updateMatrix() 执行Object3D的.updateMatrix()方法可以提取位置.position、缩放.scale和四元数.quaternion的属性值转化为变换矩阵设置本地矩阵属性.matrix。 // Object3D.js源码 updateMatrix: function () {// 把位置、四元数、缩放属性转化为平移、旋转、缩放矩阵三个矩阵的乘积是本地矩阵this.matrix.compose( this.position, this.quaternion, this.scale );this.matrixWorldNeedsUpdate true; }, // Matrix4.js源码 // 通过属性值设置变换矩阵 compose: function ( position, quaternion, scale ) {// 四元数矩阵转化为旋转矩阵然后改变本地矩阵this.makeRotationFromQuaternion( quaternion );// 缩放属性转化为缩放矩阵然后改变本地矩阵this.scale( scale );// 位置属性转化为平移矩阵然后改变本地矩阵this.setPosition( position );return this; }, // 一个网格模型对象基类是Object3D var mesh new THREE.Mesh() // 缩放网格模型 mesh.scale.set(900,900,900) // 位置、角度、缩放属性值更新到矩阵属性matrix mesh.updateMatrix(); console.log(查看本地矩阵属性matrix,mesh.matrix.elements);更新世界矩阵属性.updateMatrixWorld() 调用.updateMatrixWorld()方法首先会更新对象的本地矩阵属性然后更新对象的世界矩阵属性。 .updateMatrixWorld()方法封装了递归算法会遍历对象的所有子对象对象本身和 // Object3D.js源码 updateMatrixWorld: function ( force ) {// 更新对象的本地矩阵属性if ( this.matrixAutoUpdate ) this.updateMatrix();...if ( this.parent null ) {// 如果一个对象没有父对象也就是树结构对象的根节点对象世界矩阵就等于本地矩阵this.matrixWorld.copy( this.matrix );} else {// 更新对象的世界矩阵父对象的世界矩阵和对象本地矩阵的乘积this.matrixWorld.multiplyMatrices( this.parent.matrixWorld, this.matrix );}...// 通过递归算法遍历一个对象的所有子对象执行与根对象一样的操作更新本地和世界矩阵属性var children this.children;for ( var i 0, l children.length; i l; i ) {children[ i ].updateMatrixWorld( force );} },WebGL渲染器 场景对象的.autoUpdate属性默认值是true执行.render()方法的时候scene.updateMatrixWorld()默认执行也就是说执行 Threejs执行渲染器渲染方法的时候场景对象所有子孙对象的世界矩阵属性和本地矩阵属性才会更新。 // WebGLRenderer.js源码 this.renderfunction(){...// WebGL渲染器中执行场景对象的updateMatrixWorld()方法更新场景和场景所有子孙对象的本地矩阵if (scene.autoUpdate true) scene.updateMatrixWorld();... } 世界坐标和本地坐标 位置属性.position表示本地坐标也就是说该对象相对父对象的偏移位置。通过Object3D的.getWorldPosition()方法可以返回一个模型的世界坐标是模型对象相对坐标原点的位置坐标也就是该对象位置属性.position及其所有祖宗对象的.position相加。 var worldPosition new THREE.Vector3(); mesh.getWorldPosition(worldPosition) console.log(世界坐标,worldPosition); console.log(本地坐标,mesh.position);// Object3D.js源码 getWorldPosition: function ( target ) {if ( target undefined ) {console.warn( THREE.Object3D: .getWorldPosition() target is now required );target new Vector3();}this.updateMatrixWorld( true );通过矩阵对象setFromMatrixPosition方法从世界矩阵中提取平移矩阵分量然后转化为position属性return target.setFromMatrixPosition( this.matrixWorld ); },场景子对象具体的分类结果 Three.js渲染器执行渲染方法.render()时候会遍历场景对象然后对场景对象的后代进行分类然后把同类的对象进行集中存储然后再渲染分类好的对象。 学习本节课需要简单阅读下WebGLRenderLists.js和WebGLRenderStates.js源码文件渲染器文件WebGLRenderer.js封装的projectObject函数和.render()方法 WebGLRenderLists.js 执行WebGLRenderLists对象的.get()方法会返回一个对象返回对象具有.opaque和.transparent属性.push、.init和.sort方法。 .opaque和.transparent属性的值是数组对象用来存储场景下所有的点线网格模型。 .push方法执行该方法会把模型对象相关的信息插入到.opaque或.transparent属性值数组中 // WebGLRenderLists.js源码 // object点Points、线Line或网格Mesh模型对象 // geometryWebGLObjects.js的封装的.update()方法返回的BufferGeometry // material模型对象的材质属性值比如点材质、高光网格材质等等 function push( object, geometry, material, z, group ) {var opaque [];var transparent [];...// 一个模型对象的相关信息构成的对象作为.opaque属性或.transparent属性的元素renderItem {id: object.id,object: object,geometry: geometry,material: material,program: material.program,renderOrder: object.renderOrder,z: z,group: group};...// 判断材质是否开启透明如果开启则把renderItem对象归类到透明数组transparent中// 如果transparent是flase则把renderItem对象插入到不透明数组opaque中// transparent false执行opaque.push( renderItem )// transparent true执行transparent.push( renderItem )( material.transparent true ? transparent : opaque ).push( renderItem ); }// WebGLRenderer.js源码 var renderLists new WebGLRenderLists(); currentRenderList renderLists.get(scene, camera);// 调用对象的方法 function projectObject(object, camera, sortObjects) {...else if (object.isMesh || object.isLine || object.isPoints) {// 把模型对象相关信息批量存储到currentRenderList对象currentRenderList.push(object, geometry, material, _vector3.z, null);}... }// 访问对象属性中的数据 this.renderfunction(){// material.transparentfalse对象材质未开启透明var opaqueObjects currentRenderList.opaque;// material.transparenttrue对象材质开启透明var transparentObjects currentRenderList.transparent; }WebGLRenderStates.js 执行WebGLRenderStates对象的.get()方法会返回一个对象返回对象具有.state属性.pushLight、.pushSprite等方法。 .state属性类似.opaque和.transparent属性用来存储场景中的部分对象.state.lightsArray属性存储所有光源对象.state.spritesArray属性存储所有精灵模型对象。 .pushLight()、.pushSprite()等方法类似WebGLRenderLists的.push方法.pushLight()方法用于光源对象归类把参数光源对象存储到.state.lightsArray属性数组中.pushSprite()方法用于精灵模型对象归类参数精灵模型对象存储到.state.spritesArray属性数组中 // WebGLRenderStates.js源码var lightsArray [];var spritesArray [];// 插入光源数组函数function pushLight( light ) {lightsArray.push( light );} // 精灵function pushSprite( shadowLight ) {spritesArray.push( shadowLight );}// 存储光源对象、精灵模型对象....var state {lightsArray: lightsArray,shadowsArray: shadowsArray,spritesArray: spritesArray,};// 返回值return {state: state,pushLight: pushLight,pushSprite: pushSprite};// WebGLRenderer.js源码 var renderStates new WebGLRenderStates(); currentRenderState renderStates.get(scene, camera);// 调用对象的方法 function projectObject(object, camera, sortObjects) {...if (object.isLight) {//光源信息插入到currentRenderState对象的.state.lightsArray属性中currentRenderState.pushLight(object);}// 判断对象是不是精灵对象else if (object.isSprite) {//光源信息插入到currentRenderState对象的.state.spritesArray属性中currentRenderState.pushSprite(object);}... }// 访问对象属性中的数据 this.renderfunction(){var spritesArray currentRenderState.state.spritesArray; }function initMaterial(material, fog, object) {var lights currentRenderState.state.lights;var shadowsArray currentRenderState.state.shadowsArray; }projectObject函数 阅读WebGLRenderer.js源码封装的projectObject函数了解分类过程。 projectObject函数对象递归遍历到的子对象节点进行分类处理然后WebGL渲染器在对象分类好的不同对象进行渲染器解析关于进一步的渲染解析过程这里不讲解。 // 对象Scene的后代节点对象进行分类处理 function projectObject(object, camera, sortObjects) { ...if (visible) {// 判断对象是不是光源对象是的话插入WebGL渲染状态对象的state属性中if (object.isLight) {//光源信息插入到currentRenderState对象的.state.lightsArray属性中currentRenderState.pushLight(object);}// 判断对象是不是精灵对象else if (object.isSprite) {//光源信息插入到currentRenderState对象的.state.spritesArray属性中currentRenderState.pushSprite(object);}else if (object.isMesh || object.isLine || object.isPoints) {// 把模型对象相关信息批量存储到currentRenderList对象currentRenderList.push(object, geometry, material, _vector3.z, null);}} }WebGLObjects的.update()方法 执行projectObject函数会调用WebGLObjects.js的封装的.update()方法调用改变方法会解析点线面网格模型对象的几何体Geometry数据如果是Geometry会转化为BufferGeometry,然后从BufferGeometry提取顶点数据并传入创建的顶点缓冲区更多详细内容参考本章第5节“5.解析几何体提取顶点数据”。 执行WebGLObjects.js的封装的.update()方法的返回值是BufferGeometry. 执行完push方法currentRenderList.transparent或currentRenderList.opaque数组元素的geometry属性值是BufferGeometry。 // 对象Scene的后代节点对象进行分类处理 function projectObject(object, camera, sortObjects) {var geometry objects.update(object);var material object.material;// 把模型对象相关信息批量存储到currentRenderList对象currentRenderList.push(object, geometry, material, _vector3.z, null); }WebGLRenderer.js的.render()方法 WebGLRenderer.js渲染方法.render()先调用projectObject函数对场景的后代对象进行分类然后调用renderObjects函数渲染场景中所有的点、线、网格模型对象。 gl.drawArrays——WebGLBufferRenderer.js——renderBufferDirect——renderObject——renderObjects——render // WebGLRenderer.js源码 // currentRenderList对象用于存储点、线和和网格模型的对象 var renderLists new WebGLRenderLists(); currentRenderList renderLists.get(scene, camera); // currentRenderList对象用于存储光源、点精灵等对象 var renderStates new WebGLRenderStates(); currentRenderState renderStates.get(scene, camera);this.renderfunction(){// 场景的后代对象分类...// 遍历场景对象并对场景对象中的节点进行分类比如光源对象比如模型对象projectObject(scene, camera, _this.sortObjects);...// 渲染模型对象// material.transparentfalse对象材质未开启透明var opaqueObjects currentRenderList.opaque;// material.transparenttrue对象材质开启透明var transparentObjects currentRenderList.transparent;// 渲染材质未开启透明的模型对象集合renderObjects(opaqueObjects, scene, camera);// 渲染材质开启透明的模型对象集合renderObjects(transparentObjects, scene, camera); } 点线网格模型和绘制模式 Three.js点Points、线Line、网格Mesh模型都有几何体geometry数据对于这些不同类别的模型对象Threejs渲染的时候调用WebGL绘制函数gl.drawArrays()或gl.drawElements()的时候系统设置的绘制模式不同。 学习本节课的内容需要先简单阅读以下源码 WebGLRenderer.js源码封装的的.renderBufferDirect()方法WebGLBufferRenderer.js源码WebGLIndexedBufferRenderer.js源码 WebGLRenderer.js WebGLRenderer.js封装了WebGL绘制函数gl.drawArrays( mode, start, count ); .setMode()方法,设置gl.drawArrays();的绘制模式mode。 .render()方法封装了WebGL APIgl.drawArrays(); WebGLIndexedBufferRenderer.js WebGLIndexedBufferRenderer.js封装了WebGL绘制函数gl.drawElements(); .setMode()方法,设置gl.drawElements();的绘制模式mode。 .render()方法封装了WebGL APIgl.drawElements(); .renderBufferDirect()方法 执行.renderBufferDirect()方法会调用WebGL绘制函数gl.drawArrays()或gl.drawElements()绘制顶点数据渲染点Points、线Line、网格Mesh模型的时候在执行绘制函数之前需要根据模型的类别设置绘制函数的绘制模式mode。 import {WebGLBufferRenderer} from ./webgl/WebGLBufferRenderer.js; import {WebGLIndexedBufferRenderer} from ./webgl/WebGLIndexedBufferRenderer.js; bufferRenderer new WebGLBufferRenderer(_gl, extensions, info); indexedBufferRenderer new WebGLIndexedBufferRenderer(_gl, extensions, info); this.renderBufferDirect function(camera, fog, geometry, material, object, group) { ...var renderer bufferRenderer;// 如果存在顶点索引数据把渲染器设置为WebGLIndexedBufferRendererif (index ! null) {attribute attributes.get(index);renderer indexedBufferRenderer;renderer.setIndex(attribute);}...if (object.isMesh) {// wireframe默认falseif (material.wireframe true) {// 开启材质线框显示效果使用线绘制模式gl.LINESstate.setLineWidth(material.wireframeLinewidth * getTargetPixelRatio());renderer.setMode(_gl.LINES);} else {// 网格模型对象具有drawMode属性默认值为TrianglesDrawModeswitch (object.drawMode) {case TrianglesDrawMode:renderer.setMode(_gl.TRIANGLES);break;case TriangleStripDrawMode:renderer.setMode(_gl.TRIANGLE_STRIP);break;case TriangleFanDrawMode:renderer.setMode(_gl.TRIANGLE_FAN);break;}}} else if (object.isLine) {var lineWidth material.linewidth;if (lineWidth undefined) lineWidth 1; // Not using Line*Materialstate.setLineWidth(lineWidth * getTargetPixelRatio()); if (object.isLineSegments) {// LineSegments模型对象renderer.setMode(_gl.LINES);} else if (object.isLineLoop) {// LineLoop模型对象renderer.setMode(_gl.LINE_LOOP);} else {// Line模型对象renderer.setMode(_gl.LINE_STRIP);}} else if (object.isPoints) {// 点模型对象Pointsrenderer.setMode(_gl.POINTS);}else if (object.isPoints) {// 点模型对象使用点绘制模式renderer.setMode(_gl.POINTS);}...// 设置好绘制模式后调用WebGLRenderer.js封装的render函数相当于执行gl.drawArrays();renderer.render(drawStart, drawCount); }光源对象分类 THree.js渲染函数会通过调用projectObject函数递归遍历场景对象然后对场景对象的后代进行分类这种光源全部划分为一类存入currentRenderState.state.lightsArray属性中作为属性值数组的元素。 projectObject函数 阅读WebGLRenderer.js源码封装的projectObject函数了解分类过程递归遍历场景所有object.isLighttrue的对象全部归类为currentRenderState.state.lightsArray属性中。 // 对象Scene的后代节点对象进行分类处理 function projectObject(object, camera, sortObjects) {// 判断对象是不是光源对象是的话插入WebGL渲染状态对象的state属性中if (object.isLight) {//光源信息插入到currentRenderState对象的.state.lightsArray属性中currentRenderState.pushLight(object);} }光源对象封装 点光源PointLight、平行光DirectionalLight、环境光AmbientLight等所有种类光源对象的基类都是Light,点光源PointLight具有isPointLight属性平行光DirectionalLight具有isDirectionalLight属性环境光AmbientLight具有isAmbientLight属性这些光源对象也会继承基类Light的属性isLight。 var PointLight new THREE.PointLight() // 点光源PointLight属性isPointLight默认值true console.log(PointLight.isPointLight); // 点光源PointLight继承基类Light的isLight属性 console.log(PointLight.isLight);WebGLLights.js WebGLLights.js文件封装了光源分类的具体函数和属性。new实例化WebGLLights工厂函数返回一个对象具有.set()方法和.state属性.state 具有一些属性对应光源对象的分类。 WebGLRenderState.js import { WebGLLights } from ./WebGLLights.js;function WebGLRenderState() { ... var lights new WebGLLights();function setupLights( camera ) {// 调用WebGLLights对象的set方法对已经分类的光源对象进行二次分类lights.setup( lightsArray, shadowsArray, camera );}var state {// 存储所有光源对象的数组lightsArray: lightsArray,shadowsArray: shadowsArray,// 存储所有精灵模型对象的数据spritesArray: spritesArray,// 属性值是WebGLLights对象具有state属性state属性又具有directional、point等属性用来分别存储方向光、点光源等类型光源lights: lights }; ... }WebGLRenderer.js 调用.setupLights方法对象光源进行二次分类分类结果存储到currentRenderState.state.lights属性 this.renderfunction(){projectObject(scene, camera, _this.sortObjects);currentRenderState.setupLights(camera); }function initMaterial(material, fog, object) {var lights currentRenderState.state.lights; }function setProgram(camera, fog, material, object) {var lights currentRenderState.state.lights; } 材质对象Material对应的着色器Shader代码 前面课程中讲解过每一种材质对象都对应着一个着色器代码这节课来讲解Three.js渲染器解析材质对象Material如何获得对应的着色器代码和uniforms对象。 点材质PointsMaterial顶点着色器文件points_vert.glsl、片元着色器文件points_frag.glsl基础网格材质MeshBasicMaterial顶点着色器文件meshbasic_vert.glsl、片元着色器文件meshbasic_frag.glsl高光网格材质MeshPhongMaterial顶点着色器文件meshphong_vert.glsl、片元着色器文件meshphong_frag.glsl 学习本节课的内容需要先简单阅读以下源码 WebGLPrograms.js源码\renderers\shaders目录下的着色器源码文件和js源码文件 材质对象封装 通过材质对象的.type属性可以判断材质对象是哪种材质对象一个材质对象具有一个惟一的type类型。 JavaScript语法 把字符串作为属性名访问对象的属性。 var shaderIDs {MeshBasicMaterial: basic,MeshLambertMaterial: lambert,MeshPhongMaterial: phong,};// 查看shaderIDs对象MeshBasicMaterial属性的值console.log(shaderIDs.MeshBasicMaterial);console.log(查看属性值,shaderIDs[ MeshBasicMaterial ]);shaders目录简介 着色器代码文件目录是three.js-master\src\renderers\shadersshaders目录下有两个着色器代码的文件ShaderChunk和ShaderLib。 ShaderChunk目录下的着色器代码文件.glsl都是具有特定功能的模块ShaderLib目录下的着色器文件会通过#include ShaderChunk中着色器文件名调用ShaderChunk目录下特定功能的着色器代码块构建出来具有具有特定功能的顶点着色器文件和片元着色器文件。 点材质PointsMaterial顶点着色器文件points_vert.glsl、片元着色器文件points_frag.glsl基础网格材质MeshBasicMaterial顶点着色器文件meshbasic_vert.glsl、片元着色器文件meshbasic_frag.glsl高光网格材质MeshPhongMaterial顶点着色器文件meshphong_vert.glsl、片元着色器文件meshphong_frag.glsl ShaderChunk.js用来获得ShaderChunk和ShaderLib文件中的着色器代码 ShaderLib.js设置好点、线、网格材质对应的uniforms变量值、顶点着色器代码、片元着色器代码 UniformsLib.js、UniformsUtils.js着色器中uniform变量对应的值 WebGLRenderer.js 通过WebGLPrograms对象的方法.getParameters()返回一个parameters对象返回的parameters对象的shaderID属性保留了材质对象类型type的信息通过材质对象信息可以在ShaderLib对象中获得材质对象对应的着色器代码。 import {ShaderLib} from ./shaders/ShaderLib.js; import {WebGLPrograms} from ./webgl/WebGLPrograms.js; programCache new WebGLPrograms(_this, extensions, capabilities);function initMaterial(material, fog, object) {// 返回一个parameters对象具有shaderID属性通过shaderID的属性值可以获得材质对象对应的着色器代码。var parameters programCache.getParameters(material, lights.state, shadowsArray, ...object);// 通过shaderID键对应的值作为ShaderLib对象的键名获得相应的值uniforms对象、定点着色器代码、片元着色器代码var shader ShaderLib[parameters.shaderID];materialProperties.shader {name: material.type,uniforms: UniformsUtils.clone(shader.uniforms),vertexShader: shader.vertexShader,fragmentShader: shader.fragmentShader};// 处理着色器代码、编译着色器代码、返回程序对象programprogram programCache.acquireProgram(material, materialProperties.shader, parameters, code); }WebGLPrograms.js 构造函数WebGLPrograms封装了.getParameters()、.getProgramCode()、.acquireProgram()等方法和.programs属性。 function WebGLPrograms( renderer, extensions, capabilities ) {var shaderIDs {MeshDepthMaterial: depth,MeshDistanceMaterial: distanceRGBA,MeshNormalMaterial: normal,MeshBasicMaterial: basic,MeshLambertMaterial: lambert,MeshPhongMaterial: phong,MeshToonMaterial: phong,MeshStandardMaterial: physical,MeshPhysicalMaterial: physical,LineBasicMaterial: basic,LineDashedMaterial: dashed,PointsMaterial: points,ShadowMaterial: shadow }; this.getParameters function (material, lights,...){// 通过材质对象.type值从shaderIDs提取相应的属性值var shaderID shaderIDs[ material.type ];var parameters {// 该属性用于判断材质对象shaderID: shaderID,precision: precision,vertexColors: material.vertexColors,numSpotLights: lights.spot.length,numRectAreaLights: lights.rectArea.length,numHemiLights: lights.hemi.length,};return parameters; } }处理着色器shader代码 通过ShaderLib[parameters.shaderID]返回的着色器代码还需要一定的自动化处理才能正式作为和原生WebGL中一样的顶点、片元着色器代码使用。 学习本节课的内容需要先简单阅读以下源码 WebGLShader.js源码WebGLProgram.js源码WebGLPrograms.js源码 WebGLShader.js——WebGLProgram.js——WebGLPrograms.js——WebGLRenderer.js WebGLRenderer.js 通过WebGLPrograms对象的方法.getParameters()返回一个parameters对象返回的parameters对象的shaderID属性保留了材质对象类型type的信息通过材质对象信息可以在ShaderLib对象中获得材质对象对应的着色器代码。 import {ShaderLib} from ./shaders/ShaderLib.js; import {WebGLPrograms} from ./webgl/WebGLPrograms.js; programCache new WebGLPrograms(_this, extensions, capabilities);function initMaterial(material, fog, object) {// 返回一个parameters对象具有shaderID属性通过shaderID的属性值可以获得材质对象对应的着色器代码。var parameters programCache.getParameters(material, lights.state, shadowsArray, ...object);// 通过shaderID键对应的值作为ShaderLib对象的键名获得相应的值uniforms对象、定点着色器代码、片元着色器代码var shader ShaderLib[parameters.shaderID];materialProperties.shader {name: material.type,uniforms: UniformsUtils.clone(shader.uniforms),vertexShader: shader.vertexShader,fragmentShader: shader.fragmentShader};// 处理着色器代码、编译着色器代码、返回程序对象programprogram programCache.acquireProgram(material, materialProperties.shader, parameters, code); }WebGLPrograms.js 构造函数WebGLPrograms封装了.getParameters()、.getProgramCode()、.acquireProgram()等方法和.programs属性。 .getParameters()方法和ShaderLib.js封装的ShaderLib对象组合使用获得一个材质对象对应的着色器代码和uniforms对象。.acquireProgram()方法调用WebGLProgram构造函数编译顶点和片元着色器代码并创建返回对应的程序对象Program。 function WebGLPrograms( renderer, extensions, capabilities ) { ...this.acquireProgram function ( material, shader, parameters, code ) {program new WebGLProgram( renderer, extensions, code, material, shader, parameters );programs.push( program );return program;}; } ...WebGLProgram.js WebGLProgram.js封装了gl.attachShader()、gl.deleteShader()、gl.createProgram()、gl.linkProgram()等原生WebGL API。 阅读WebGLProgram.js的源码需要对JavaScript语言的正则表达式和字符串的处理方法有一定的理解。 批量处理着色器代码把着色器代码块中预先编写的表示光源数量的符号替换为具体的数字 // 替换灯的数量 function replaceLightNums( string, parameters ) { // NUM_DIR_LIGHTS等字符出现在了.glsl文件着色器代码中需要替换为具体的数字才能作为着色器代码使用。return string.replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ).replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ).replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ).replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ).replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); }把.glsl文件中#include common等字符串替换为相应.glsl文件中的代码字符串 function parseIncludes( string ) { // “#include 着色模块名字”对应的正则表达式var pattern /^[ \t]*#include ([\w\d.])/gm;function replace( match, include ) {var replace ShaderChunk[ include ];return parseIncludes( replace );} // 参数string也就是着色器代码字符串执行replace方法return string.replace( pattern, replace );}顶点着色器前缀字符串prefixVertex会和.glsl文件提供的顶点着色器代码拼接在一起。 通过WebGLPrograms.js封装的.getParameters()方法可以从材质对象提取相关的信息返回一个包含材质信息的对象parameters比如透明度、材质颜色等信息着色器前缀会从parameters对象提取材质信息信息合成着色器代码。 // JavaScript数组对象的.join()方法会把数组中的字符串元素拼接成一个字符串返回 prefixVertex [precision parameters.precision float;,// precision highp float;precision parameters.precision int;,// precision highp int;parameters.map ? #define USE_MAP : ,parameters.envMap ? #define USE_ENVMAP : ,// 材质vertexColors属性默认值THREE.NoColors查看\src\Three.js 可以知道是0// 如果材质属性设置了顶点渲染属性值是THREE.VertexColors着色器代码插入#define USE_COLOR // 如果材质属性没设置顶点渲染着色器代码中不会出现带预定义parameters.vertexColors ? #define USE_COLOR : ,parameters.flatShading ? #define FLAT_SHADED : ,//#define FLAT_SHADED 是否平面着色// 声明顶点着色器代码用到的uniform变量uniform mat4 modelMatrix;,//模型矩阵uniform mat4 modelViewMatrix;,uniform mat4 projectionMatrix;,//投影矩阵uniform mat4 viewMatrix;,//视图矩阵uniform mat3 normalMatrix;,uniform vec3 cameraPosition;,// 声明顶点着色器代码用到的顶点变量attributeattribute vec3 position;,//顶点位置数据attribute vec3 normal;,//顶点法向量数据attribute vec2 uv;,//顶点UV坐标数据\n].filter( filterEmptyLine ).join( \n );片元着色器前缀字符串会和.glsl文件提供的顶点着色器代码拼接在一起。 prefixFragment [ // 片元精度定义precision parameters.precision float;,precision parameters.precision int;,parameters.map ? #define USE_MAP : ,parameters.envMap ? #define USE_ENVMAP : , // 视图矩阵 相机矩阵uniform mat4 viewMatrix;,uniform vec3 cameraPosition;, ].filter( filterEmptyLine ).join( \n );顶点和片元着色器代码字符串的处理流程 function WebGLProgram( renderer,..., shader, parameters ) {// 预定义var defines material.defines;// \shaders\ShaderLib目录下.glsl文件中顶点着色器、片元着色器代码字符串var vertexShader shader.vertexShader;var fragmentShader shader.fragmentShader;// 把顶点着色器中“#include 着色模块名字”字符串替换为相应的着色器代码vertexShader parseIncludes( vertexShader );// 把着色器代码中表示各种光源数量的字符串替换为表示该类光源对象数量的数字vertexShader replaceLightNums( vertexShader, parameters );// 把着色器代码中表示剪裁平面数量相关的字符串替换为具体的数字vertexShader replaceClippingPlaneNums( vertexShader, parameters );// 片元着色器代码和顶点着色器代码一样要进行类似的处理fragmentShader parseIncludes( fragmentShader );fragmentShader replaceLightNums( fragmentShader, parameters );fragmentShader replaceClippingPlaneNums( fragmentShader, parameters );vertexShader unrollLoops( vertexShader );fragmentShader unrollLoops( fragmentShader );// .glsl文件中着色器代码字符串经过处理后与对应的着色器前缀字符串拼接var vertexGlsl prefixVertex vertexShader;var fragmentGlsl prefixFragment fragmentShader;// 处理完成的着色器代码进行编译处理创建并返回对应的程序对象var glVertexShader WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );var glFragmentShader WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl );var program gl.createProgram();gl.attachShader( program, glVertexShader );gl.attachShader( program, glFragmentShader );gl.linkProgram( program ); }WebGLProgram对象的属性 this.name shader.name; this.id programIdCount ; this.code code; this.usedTimes 1; this.program program; // WebGLShader函数返回值顶点着色器对象 this.vertexShader glVertexShader; // WebGLShader函数返回值片元着色器对象 this.fragmentShader glFragmentShader;WebGLShader.js WebGLShader.js封装了gl.createShader()、gl.shaderSource()、gl.compileShader()等原生WebGL API。 执行WebGLShader函数可以返回一个gl.createShader()创建的着色器对象。 // WebGLShader.js源码 function WebGLShader( gl, type, string ) {// type是gl.VERTEX_SHADER创建顶点着色器对象// type是gl.FRAGMENT_SHADER创建片元着色器对象var shader gl.createShader( type );// 引入顶点或片元着色器源代码stringgl.shaderSource( shader, string );// 编译顶点或片元着色器gl.compileShader( shader );// 返回着色器对象return shader; } WebGLProgram.js源码中会调用WebGLShader函数创建一个着色器对象。 function WebGLProgram( renderer,... material, shader, parameters ) {// 创建顶点着色器对象var glVertexShader WebGLShader( gl, gl.VERTEX_SHADER, vertexGlsl );// 创建一个片元着色器对象var glFragmentShader WebGLShader( gl, gl.FRAGMENT_SHADER, fragmentGlsl ); }着色器字符串处理—材质属性、光源数量 材质的部分属性控制着着色器部分代码的生成.glsl文件一些字符串表示某种光源对象的数量需要替换为表示该光源的数量的数字。 WebGLLights.js 把光源对象按照种类进行分类比如点光源一组、方向光源一组。 WebGLRenderer.js 通过WebGLPrograms对象的方法.getParameters()返回一个parameters对象获得材质对象、渲染器对象、光源数量的信息。 import {WebGLPrograms} from ./webgl/WebGLPrograms.js; programCache new WebGLPrograms(_this, extensions, capabilities); function initMaterial(material, fog, object) {// 获得currentRenderState对象.lights属性的值WebGLLights对象var lights currentRenderState.state.lights;// 参数lights.state:WebGLLights对象的state属性包含了各类光源分类的集合可以获得每一种光源的数量var parameters programCache.getParameters(material, lights.state, shadowsArray, ...object);}WebGLPrograms.js 从材质对象、currentRenderState.state.lights提取信息设置为parameters对象属性的值。 // 参数lightscurrentRenderState.state.lights.state this.getParameters function ( material, lights... ) { // parameters参数 // 着色器id号、precision精度、贴图map、vertexColors顶点渲染、numPointLights点光源数量... // 从不同的对象提取数据 shaderID、材质对象、灯光对象、渲染器对象var parameters {// JavaScript语法!nulltrue !200false// map为null!! material.map返回false否则truemap: !! material.map,envMap: !! material.envMap,// 获得材质vertexColors属性的值默认是常量THREE.NoColors也就是0视频中错误纠正// 查看\src\Three.js 文件可以知道THREE.NoColors、THREE.VertexColors表示的值vertexColors: material.vertexColors,...// 获得光源对象数量numDirLights: lights.directional.length,numPointLights: lights.point.length,numSpotLights: lights.spot.length,numRectAreaLights: lights.rectArea.length,numHemiLights: lights.hemi.length,...};return parameters; };WebGLProgram.js .glsl文件着色器字符串插入代码 prefixVertex [ ...parameters.map ? #define USE_MAP : ,parameters.envMap ? #define USE_ENVMAP : ,// 材质vertexColors属性默认值THREE.NoColors查看\src\Three.js 可以知道是0// 如果材质属性设置了顶点渲染属性值是THREE.VertexColors着色器代码插入#define USE_COLOR // 如果材质属性没设置顶点渲染着色器代码中不会出现带预定义parameters.vertexColors ? #define USE_COLOR : , ... ].filter( filterEmptyLine ).join( \n );批量处理着色器代码把着色器代码块中预先编写的表示光源数量的符号替换为具体的数字 // 替换灯的数量 function replaceLightNums( string, parameters ) { // NUM_DIR_LIGHTS等字符出现在了.glsl文件着色器代码中需要替换为具体的数字才能作为着色器代码使用。return string.replace( /NUM_DIR_LIGHTS/g, parameters.numDirLights ).replace( /NUM_SPOT_LIGHTS/g, parameters.numSpotLights ).replace( /NUM_RECT_AREA_LIGHTS/g, parameters.numRectAreaLights ).replace( /NUM_POINT_LIGHTS/g, parameters.numPointLights ).replace( /NUM_HEMI_LIGHTS/g, parameters.numHemiLights ); }数学相关 # Three.js向量为了让大家深入了解Three.js的Math模块API本节课对向量内容展开讲解。 ### Three.js向量相关API - 二维向量[Vector2](http://www.yanhuangxueyuan.com/threejs/docs/index.html#api/math/Vector2) - 三维向量[Vector3](http://www.yanhuangxueyuan.com/threejs/docs/index.html#api/math/Vector3) - 四维向量[Vector4](http://www.yanhuangxueyuan.com/threejs/docs/index.html#api/math/Vector4)几维向量就有几个分量二维向量Vector2有x和y两个分量也就是Vector2对象具有x和y两个属性三维向量Vector3有xyz三个分量四维向量Vector4有xyzw四个分量。在Threejs中一些对象属性值会使用这些向量对象来表示比如几何体Geometry的顶点UV坐标需要表示一个二维坐标所以uv坐标使用Vector2对象表示几何体Geometry的顶点位置坐标在在三维空间笛卡尔坐标系中中坐标需要xyz三个分量所以顶点坐标使用Vector3对象表示;Three.js模型对象的缩放属性可以在xyz三个方向上进行缩放也就是说有三个分量需要表达自然使用Vector3对象。控制台查看Threejs对象位置、缩放属性的值 JavaScript var mesh new THREE.Mesh() mesh.position.set(100,20,330);//设置网格模型的位置 console.log(对象位置属性,mesh.position); console.log(对象缩放属性,mesh.scale);控制台查看几何体相关数据的表示方式 var geometry new THREE.BoxGeometry(50,25,25);//立方体 console.log(几何体顶点位置坐标数据,geometry.vertices); console.log(几何体三角形,geometry.faces); console.log(几何体UV坐标,geometry.faceVertexUvs[0]);创建向量对象 通过向量构造函数创建向量对象查看向量对象的结构。 // 创建一个三维向量xyz分量分别为3, 5, 4 var v1 new THREE.Vector3(3, 5, 4) console.log(向量x分量, v1.x); // 重置向量的y分量 v1.y 80; // 重新设置三个分量 v1.set(2,4,8);克隆.clone()和复制.copy() 对象执行克隆方法.clone()返回一个新的对象和原来对象属性的值一样。 var p1 new THREE.Vector3(10,20,15); var v1 p1.clone(); console.log(查看克隆的结果,v1);执行.copy()方法向量p1三个分量xyz的值覆盖向量p2三个分量 var p1 new THREE.Vector3(10,42,28); var p2 new THREE.Vector3(); p2.copy(p1); console.log(查看复制的结果,p2);向量和标量 如果你有一定的数学基础肯定对向量和标量有一定的概念比如说空间中一个点的位置是标量两个点构成一个有方向的量是向量在Three.js中不一定要严格区分向量和标量的概念THREE.Vector3对象既可以表示一个顶点位置比如网格模型Mesh的位置坐标也可以表示一个有方向的向量比如顶点的法向量或光线的方向。虽然Vector字面意思是向量但是THREE.Vector3对象表示的是向量还是标量要看它的具体含义。 向量减法.sub()和向量长度.length() 通过.sub()方法可以对两个向量进行减法运算比如两个表示顶点坐标的Vector3对象进行减法运算返回一个新的Vector3对象就是两个点构成的向量。 直接执行p1.sub(p2)会改变p1所以先克隆然后再执行减法运算p1.clone().sub(p2)。 向量对象执行.length()方法会返回向量的长度。 已知直线两个顶点的坐标计算直线的长度. // 点1坐标 var p1 new THREE.Vector3(10,8,12); // 点2坐标 var p2 new THREE.Vector3(20,30,-10); // .clone()方法克隆p1直接执行向量减法.sub()会改变p1向量本身 // .sub()向量减法运算 // .length()返回向量的长度 var L p1.clone().sub(p2).length(); console.log(两点之间距离,L);点乘.dot() 向量的.dot()方法用来计算两个向量的点积计算光线和几何体顶点夹角几何体体积等等都会用到该方法。 已知三角形三个顶点的坐标计算其中一个顶点对应角度余弦值。 // 三角形的三个点坐标p1p2p3 var p1 new THREE.Vector3(0,0,0);// 点1坐标 var p2 new THREE.Vector3(20,0,0);// 点2坐标 var p3 new THREE.Vector3(0,40,0);// 点3坐标// p1p2两个点确定一个向量 var v1 p1.clone().sub(p2); // p1p3两个点确定一个向量 var v2 p1.clone().sub(p3); // .dot()计算两个向量点积 .length()计算向量长度 // 返回三角形顶点p1对应夹角余弦值 var CosineValue v1.dot( v2 ) /(v1.length()*v2.length()) console.log(三角形两条边夹角余弦值,CosineValue); // .acos()反余弦函数返回结果是弧度 console.log(三角形两条边夹角,Math.acos(CosineValue)*180/Math.PI);叉乘.cross() .crossVectors()和.cross()都是向量对象的叉乘计算方法功能一样只是使用的细节有些不同向量对象叉乘的结果仍然是向量对象。 计算向量v1和v2的叉乘结果 // 声明一个向量对象用来保存.crossVectors()方法结果var v3 new THREE.Vector3();v3.crossVectors(v1,v2)向量v2直接执行.cross()方法叉乘的结果会覆盖向量v2的xyz分量 v2.cross(v1)克隆v2避免叉乘后改变原来的v2变量。 var v3 v2.clone();v3.cross(v1)已知三角形的三个顶点p1, p2, p3的坐标值利用三个顶点的坐标计算三角形的面积 //三角形面积计算 function AreaOfTriangle(p1, p2, p3){var v1 new THREE.Vector3();var v2 new THREE.Vector3();// 通过两个顶点坐标计算其中两条边构成的向量v1 p1.clone().sub(p2);v2 p1.clone().sub(p3);var v3 new THREE.Vector3();// 三角形面积计算v3.crossVectors(v1,v2);var s v3.length()/2;return s }Three.js矩阵 如果你有线性代数的基础自然对象矩阵并不陌生如果对大学所学的线性代数具体知识已经忘记了也没有关系Three.js的矩阵库对常见的矩阵运算都进行了封装如果不是为了封装或扩展3D引擎只是开发一些常见3D项目你只需要有一些基本的概念会调用矩阵对象Matrix相关的方法就可以。 Three.js矩阵相关API 3x3矩阵Matrix34x4矩阵Matrix4 创建向量对象 通过矩阵对象的elements属性可以访问矩阵元素的具体指如果创建的时候构造函数没有设置具体的值构造函数实例化的时候会自动设置一个默认值。 4x4矩阵Matrix4 // 创建一个4x4矩阵对象 var mat4 new THREE.Matrix4() // 默认值单位矩阵 // 1, 0, 0, 0, // 0, 1, 0, 0, // 0, 0, 1, 0, // 0, 0, 0, 1 console.log(查看矩阵对象默认值, mat4.elements);属性elements和方法.set() 需要通过Matrix4对象表示的一个4x4矩阵 | 1 0 0 5 || 0 1 0 3 || 0 0 1 9 || 0 0 0 1 |通过.set()方法重置矩阵的元素值执行.set()方法本质上改变的就是矩阵elements属性值这里注意set方法的参数顺序是按行设置一个矩阵的元素值。 mat4.set(1, 0, 0, 5,0, 1, 0, 3,0, 0, 1, 9,0, 0, 0, 1 )通过elements属性重置矩阵的元素值elements的属性值是一个矩阵对象里面的元素按列设置一个矩阵的元素值。 mat4.elements[1, 0, 0, 0,0, 1, 0, 0,0, 0, 1, 0,5, 3, 9, 1 ]转置矩阵.transpose() 矩阵对象执行方法.transpose()按照一定算法改变的自身.elements属性值。 // 创建一个4X4矩阵 var mat4 new THREE.Matrix4(); mat4.set(1,0,0,5,0,1,0,3,0,0,1,9,0,0,0,1); //转置矩阵 mat4.transpose(); console.log(查看mat4的转置矩阵,mat4.elements);为了不改变原矩阵可以先执行克隆.clone方法返回一个和原矩阵完全一样的矩阵。 // 创建一个4X4矩阵 var mat4 new THREE.Matrix4(); mat4.set(1,0,0,5,0,1,0,3,0,0,1,9,0,0,0,1); // 先克隆避免改变原矩阵mat4 var mat4T mat4.clone() //转置矩阵 mat4T.transpose(); console.log(查看mat4的转置矩阵,mat4T.elements);.multiplyScalar() 矩阵的每个元素乘以.multiplyScalar()方法的参数。 // 创建一个4X4矩阵 var mat4 new THREE.Matrix4(); mat4.set(1,0,0,5,0,1,0,3,0,0,1,9,0,0,0,1); // 矩阵乘以标量 mat4.multiplyScalar(10); console.log(查看矩阵乘以标量后的结果, mat4.elements);矩阵乘法 c.multiplyMatrices(a,b):参数中两个矩阵相乘axb结果保存在c中a.multiply(b):矩阵相乘axb结果保存在aa.premultiply(b):矩阵相乘bxa结果保存在a // 矩阵乘法运算 var mat41 new THREE.Matrix4() mat41.set(1,2,0,0,0,0,0,8,0,3,5,0,0,0,0,0) var mat42 new THREE.Matrix4() mat42.set(1,2,0,0,0,2,3,0,7,0,0,8,0,3,5,0) // mat43保存计算结果 var mat43 new THREE.Matrix4() // 矩阵乘法运算mat41xmat42 mat43.multiplyMatrices(mat41,mat42) console.log(查看mat43, mat43.elements);逆矩阵getInverse 计算逆矩阵需要注意矩阵首先是可逆的如果矩阵不可逆执行该方法会报错 计算可逆矩阵的逆矩阵 var mat4 new THREE.Matrix4(); // 可逆矩阵 mat4.elements[1,0,0,0,0,1,0,0,0,0,1,0,5,3,9,1] // mat4I用来保存mat4逆矩阵计算结果 var mat4I new THREE.Matrix4(); mat4I.getInverse(mat4, true); // 控制台查看矩阵的逆矩阵 console.log(mat4I, mat4I.elements);不可逆矩阵报错 var mat4 new THREE.Matrix4(); // 不可逆矩阵 mat4.elements [0,0,0,0,0,0,0,0,0,0,0,0,5,3,9,1]; // mat4I用来保存mat4逆矩阵计算结果 var mat4I new THREE.Matrix4(); mat4I.getInverse(mat4, true); // 控制台查看矩阵的逆矩阵 console.log(mat4I, mat4I.elements); 行列式.determinant() 通过.determinant()方法计算矩阵行列式的值 var mat new THREE.Matrix4() mat.set(1,2,0,0,0,2,3,0,7,0,0,8,0,3,5,0) // 计算矩阵行列式的值 var determinant mat.determinant() console.log(行列式, determinant);对象属性值 相关矩阵属性可以参考网格模型Mesh的基类Object3D。 var mesh new THREE.Mesh(); console.log(本地矩阵, mesh.matrix); console.log(世界矩阵, mesh.matrixWorld); console.log(模型视图矩阵, mesh.modelViewMatrix); // .normalMatrix属性值是3X3矩阵Matrix3 console.log(法线矩阵, mesh.normalMatrix);相机对象的投影矩阵属性.projectionMatrix和视图矩阵属性.matrixWorldInverse。 // 创建相机对象 var camera new THREE.OrthographicCamera(-20, 20, 10, -10, 1, 1000); console.log(视图矩阵, camera.matrixWorldInverse); console.log(投影矩阵, camera.projectionMatrix);Three.js旋转、平移、缩放矩阵 在WebGL中对一个对象进行平移、旋转或缩放本质就是对对象的顶点坐标进行平移、旋转、缩放矩阵变换。 关键词 在学习本节课之前最好对旋转、平移、缩放等变换矩阵有一定的了解可以学习WebGL相关教程或图形学书籍。 变换矩阵旋转矩阵缩放矩阵平移矩阵 平移矩阵 平移矩阵T表示一个顶点坐标沿着X、Y、Z轴分别平移Tx、Ty、Tz | 1 0 0 Tx || 0 1 0 Ty || 0 0 1 Tz || 0 0 0 1 |一个点的坐标是(x,y,z),假设沿着X、Y、Z轴分别平移Tx、Ty、Tz毫无疑问平移后的坐标是(xTx,yTy,zTz)。 矩阵和表示顶点坐标的向量进行乘法运算 | 1 0 0 Tx | | x | | xTx || 0 1 0 Ty | x | y | | yTy || 0 0 1 Tz | | z | | zTz || 0 0 0 1 | | 1 | | 1 |缩放矩阵 比如一个几何体的所有顶点坐标沿着X、Y、Z轴分别缩放矩阵Sx、Sy、Sz倍可以用如下矩阵S表示。 | Sx 0 0 0 || 0 Sy 0 0 || 0 0 Sz 0 || 0 0 0 1 |顶点坐标缩放变换 | Sx 0 0 0 | | x | | x*Sx || 0 Sy 0 0 | x | y | | y*Sy || 0 0 Sz 0 | | z | | z*Sz || 0 0 0 1 | | 1 | | 1 |旋转矩阵 绕x轴旋转α度对应的旋转矩阵Rx | 1 0 0 0 | | x | | x || 0 cosα -sinα 0 | x | y | | cosα*y-sinα*z || 0 sinα cosα 0 | | z | | sinα*ycosα*z || 0 0 0 1 | | 1 | | 1 |绕y轴旋转α度对应的旋转矩阵Ry | cosα 0 -sinα 0 | | x | | cosα*xsinα*z || 0 1 0 0 | x | y | | y || sinα 0 cosα 0 | | z | | -sinα*xcosα*z || 0 0 0 1 | | 1 | | 1 |绕z轴旋转α度对应的旋转矩阵Rz | cosα -sinα 0 0 | | x | | cosα*x-sinα*y || sinα cosα 0 0 | x | y | | sinα*xcosα*y || 0 0 1 0 | | z | | z || 0 0 0 1 | | 1 | | 1 |创建变换矩阵 直接通过矩阵对象的elements属性或.set()方法设置常见变换矩阵的元素比较麻烦比如设置一个缩放矩阵Three.js的4x4矩阵Matrix4对常见变换矩阵的设置封装了一些方法。 绕x轴旋转.makeRotationX(theta) 绕y轴旋转.makeRotationY(theta) 绕z轴旋转.makeRotationZ(theta) 缩放.makeScale(Sx,Sy,Sz) 平移.makeTranslation(Tx,Ty,Tz) 剪切.makeShear 平移矩阵创建案例 | 1 0 0 5 || 0 1 0 3 || 0 0 1 9 || 0 0 0 1 |.set()方法设置平移矩阵 var T new THREE.Matrix4() // set方法设置平移矩阵 T.set(1, 0, 0, 5,0, 1, 0, 3,0, 0, 1, 9,0, 0, 0, 1 )makeTranslation方法设置平移矩阵 var T new THREE.Matrix4() // 顶点坐标沿着X、Y、Z轴分别平移5,3,9 T.makeTranslation(5,3,9) console.log(查看平移矩阵, T.elements);向量矩阵变换.applyMatrix4() .applyMatrix4()是三维向量Vector3的一个方法 var T new THREE.Matrix4() // 创建一个平移矩阵顶点坐标沿着X、Y、Z轴分别平移5,3,9 T.makeTranslation(5, 3, 9) // 三维向量表示一个顶点坐标 var v1 new THREE.Vector3(10,10,10); // 向量进行矩阵变换 var v2 v1.clone().applyMatrix4(T); console.log(查看平移后坐标, v2);多次变换 顶点坐标经过多次变换可以把多个变换矩阵进行乘法运算然后再和表示顶点的坐标进行变换。 模型矩阵M、平移矩阵T、缩放矩阵S、旋转矩阵R、绕X轴旋转矩阵Rx、绕X轴旋转矩阵Ry、绕X轴旋转矩阵Rz 旋转矩阵 R RxRyRz 模型矩阵M RST 顶点V1执行模型矩阵变换V2 M*V1 顶点进行两次平移变换代码 // 创建平移矩阵T1x轴平移100 var T1 new THREE.Matrix4().makeTranslation(100, 0, 0) // 创建平移矩阵T2y轴平移100 var T2 new THREE.Matrix4().makeTranslation(0, 100, 0)// 两个变换矩阵相乘表示顶点先后经过两次 var M new THREE.Matrix4() M.multiplyMatrices(T2,T1) // 三维向量表示一个顶点坐标 var v1 new THREE.Vector3(10, 10, 10); // 向量进行矩阵变换 var v2 v1.clone().applyMatrix4(M); console.log(查看平移后坐标, v2);Object3D本地矩阵属性.matrix 通过前面知识的学习应该都知道对象Object3D的位置.position、缩放.scale、角度.rotation等属性这些属性本质上都是矩阵变换。Object3D对象的.translateX()、.translateZ()等平移方法会改变.position属性的值Object3D对象的.rotateX()、.rotateZ()等旋转方法会改变.rotation属性的值Three.js渲染模型解析的时候Three.js会解析Object3D对象位置.position、缩放.scale、角度.rotation属性对应的平移、旋转、缩放矩阵相乘转化为本地矩阵.matrix的属性值。 执行旋转方法.rotateZ()查看查看角度属性.rotation属性值欧拉对象z属性的变化 // 一个网格模型对象基类是Object3D var mesh new THREE.Mesh() // 绕z轴旋转 mesh.rotateZ(Math.PI) console.log(查看角度属性值的变化,mesh.rotation);执行平移方法.translateX()查看查看位置.position属性值x分量变化 // 一个网格模型对象基类是Object3D var mesh new THREE.Mesh() // 沿着x轴平移100 mesh.translateX(100) console.log(查看位置属性的变化,mesh.position);查看位置.position、缩放.scale、角度.rotation等属性对本地矩阵属性.matrix的影响。 Three.js渲染的时候会把模型的矩阵值传递给着色器对顶点进行矩阵变换具体细节这里不展开讲解。 // 一个网格模型对象基类是Object3D var mesh new THREE.Mesh() // 缩放网格模型 mesh.scale.set(900,900,900) // 位置、角度、缩放属性值更新到矩阵属性matrix mesh.updateMatrix() console.log(查看本地矩阵属性matrix,mesh.matrix.elements);投影矩阵、视图矩阵 学习本节课之前最好对图形学中视图矩阵和投影矩阵有一定了解同时对于Three.js的正投影相机OrthographicCamera、透视投影相机PerspectiveCamera有一定了解。 关键词投影矩阵、视图矩阵、正投影、透视投影、视点、目标观察点、上方向、正投影相机、透视投影相机 如果你对图形学中视图矩阵、投影矩阵相关内容比较了解有助于本节课的学习如果不了解的话可以根据关键词去检索一下相关的内容去学习补充。 相机对象属性.matrixWorldInverse和.projectionMatrix 正投影相机PerspectiveCamera和透视投影相机OrthographicCamera的基类是相机Camera,相机对象Camera具有视图矩阵属性.matrixWorldInverse和投影矩阵属性.projectionMatrix。 相机对象本质就是视图矩阵和投影矩阵顶点坐标经过平移旋转缩放模型变换以后还需要经过视图、投影变换才能显示到画布上。 Matrix4方法正投影.makeOrthographic() 正投影公式 \begin{bmatrix} \frac{2}{right-left} 0 0 -\frac{rightleft}{right-left} \ 0 \frac{2}{top-bottom} 0 -\frac{topbottom}{top-bottom} \ 0 0 -\frac{2}{far-near} -\frac{farnear}{far-near} \ 0 0 0 1 \end{bmatrix} 矩阵对象Matrix4的方法.makeOrthographic()封装了正投影的算法该方法用来创建一个正投影矩阵在正投影相机对象OrthographicCamera中会调用该方法更新相机对象的投影矩阵属性.projectionMatrix 方法参数.makePerspective( left,right,top,bottom,near,far) 正投影相机OrthographicCamera 正投影相机OrthographicCamera类封装调用了矩阵对象Matrix4的正投影矩阵变换方法.makeOrthographic()。执行该方法用来改变正投影相机对象的投影矩阵属性.projectionMatrix。 // OrthographicCamera.js源码 this.projectionMatrix.makeOrthographic( left, right, top, bottom, this.near, this.far )构造函数PerspectiveCamera(left,right,top,bottom,near,far) 正投影相机设置例子 var width window.innerWidth; //窗口宽度 var height window.innerHeight; //窗口高度 var k width / height; //窗口宽高比 var s 150; //三维场景显示范围控制系数系数越大显示的范围越大 //创建相机对象 var camera new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000); camera.position.set(200, 300, 200); //设置相机位置 camera.lookAt(scene.position); //设置相机方向(指向的场景对象)Matrix4方法透视投影矩阵.makePerspective() 透视投影公式 \begin{bmatrix} \frac{near}{right} 0 0 0 \ 0 \frac{near}{top} 0 0 \ 0 0 -\frac{farnear}{far-near} -\frac{2farnear}{far-near} \ 0 0 -1 0 \end{bmatrix} 矩阵对象Matrix4的方法.makePerspective()封装了透视投影的算法该方法用来创建一个透视投影矩阵在透视投影相机对象PerspectiveCamera中会调用该方法更新相机对象的投影矩阵属性.projectionMatrix。 方法参数.makePerspective( left,right,top,bottom,near,far) 透视投影相机PerspectiveCamera 透视投影相机PerspectiveCamera类封装调用了矩阵对象Matrix4的透视投影矩阵变换方法.makePerspective()。执行该方法用来改变透视投影相机对象的投影矩阵属性.projectionMatrix。 // PerspectiveCamera.js源码 this.projectionMatrix.makePerspective(...);构造函数PerspectiveCamera(fov,aspect,near,far) 透视投影相机使用例子 var width window.innerWidth; //窗口宽度 var height window.innerHeight; //窗口高度 /**透视投影相机对象*/ var camera new THREE.PerspectiveCamera(60, width / height, 1, 1000); camera.position.set(200, 300, 200); //设置相机位置 camera.lookAt(scene.position); //设置相机方向(指向的场景对象)Matrix4方法.lookAt() 矩阵对象Matrix4的.lookAt()方法对图形学中投影矩阵算法进行了封装也就是通过给定的参数生成变换矩阵视图矩阵和模型矩阵一样会用于场景中对象的平移旋转等变换该方法通常用于构建相机对象的视图矩阵.matrixWorldInverse属性。 参数.lookAt(eye,center, up ) 三个参数都是三维向量对象Vector3eye是视点也就是观察位置center表示被观察的位置up表示向上的方向。 Object3D方法.lookAt(x,y,z) Object3D类封装了矩阵对象Matrix4的.lookAt()方法得到一个新的方法.lookAt(x,y,z),参数表示xyz是相机的目标观察点。 通过Object3D对象的.lookAt(x,y,z)方法可以改变自身的四元数属性.quaternion,四元数属性.quaternion和对象角度属性rotaion一样表示对象的旋转变换可以转化为旋转矩阵进而改变对象的本地矩阵属性.matrix和世界矩阵属性.matrixWorld。 // Object3D.js源码 // .lookAt()方法计算得到的旋转矩阵对象m1改变对象的四元数属性.quaternion this.quaternion.setFromRotationMatrix( m1 );相机对象 透视投影相机PerspectiveCamera和正投影相机OrthographicCamera的基类是相机对象Camera相机对象的基类是Object3D所以相机对象会继承Object3D的.lookAt(x,y,z)方法勇于改变自身的矩阵属性。 Object3D → Camera → PerspectiveCamera Object3D → Camera → OrthographicCamera 相机对象的视图矩阵属性matrixWorldInverse字面意思是世界矩阵逆矩阵的意思这可以看书来相机对象的视图矩阵属性就是自身世界矩阵matrixWorld的逆矩阵。 设置相机对象的位置属性和lookAt方法本质就是改变自身的视图矩阵属性matrixWorldInverse。 var camera new THREE.OrthographicCamera(...); //设置相机位置 camera.position.set(200, 300, 200); //设置相机对象的观察目标的位置 camera.lookAt(scene.position);Three.js包围盒 包围盒是一种计算一系列顶点最优包围空间的算法比如一个不规则几何体的所有顶点坐标都被包围在一个最小的长方体盒子中需要一个算法来求解这个最小的长方体包围盒。Three.js封装与包围盒算法相关的三个API分别是三维包围盒Box3、包围球Sphere、包围矩形Box2。 包围矩形Box2 包围矩形对象Box2表示一个矩形区域使用min和max两个属性来描述该矩形区域属性值都是二维向量对象Vector2通过Box2的构造函数可以直接设置min和max属性值也可以通过Box2的一些方法来设置。 描述一个矩形区域需要通过xy坐标来表示X范围[Xmin,Xmax],Y范围[Ymin,Ymax],.min属性值是Vector2(Xmin, Ymin),.max属性值是Vector2(Xmax, Ymax). 通过参数设置min和max属性 // 参数1对应min属性值参数2对应max属性值 var box2 new THREE.Box2(new THREE.Vector2(0, 0), new THREE.Vector2(25, 25)) console.log(box2,box2); box2.minnew THREE.Vector2(0, 0); box2.maxnew THREE.Vector2(25, 25);设置min和max属性值 box2.minnew THREE.Vector2(0, 0); box2.maxnew THREE.Vector2(25, 25);包围盒Box3 包围盒Box3表示三维长方体包围区域使用min和max两个属性来描述该包围区域Box3的min和max属性值都是三维向量对象Vector3。 描述一个长方体包围盒需要通过xyz坐标来表示X范围[Xmin,Xmax],Y范围[Ymin,Ymax],Z范围[Zmin,Zmax],.min属性值是Vector3(Xmin, Ymin, Zmin),.max属性值是Vector3(Xmax, Ymax, Zmin). var box3 new THREE.Box3() console.log(box3,box3); box3.min new THREE.Vector3(-10, -10,0); box3.max new THREE.Vector3(100, 20,50);包围球Sphere 包围球Sphere是一个球形的包围区域通过球心坐标.center和半径.radius两个属性来描述. // 创建一个包围球对象球心默认坐标原点半径默认0. var sphere new THREE.Sphere() console.log(sphere, sphere); // 设置球心坐标 sphere.centernew THREE.Vector3(-10, -10,0); // 设置包围球半径 sphere.radius20;Box3方法.setFromPoints() 包围盒Box3方法.setFromPoints()用来计算一系列顶点集合的最小包围盒参数是表示顶点坐标的三维向量Vector3作为元素构成的数组对象。 // 通过球体API创建一个几何体本质上就是一系列沿着球面分布的顶点 var geometry new THREE.SphereGeometry(50, 100, 100); // 创建一个包围盒对象Box3 var box3 new THREE.Box3() // 计算点集geometry.vertices的包围盒 box3.setFromPoints(geometry.vertices); console.log(box3, box3);几何体方法.computeBoundingBox() 几何体Geometry调用Box3的方法.setFromPoints()封装了一个方法.computeBoundingBox()用来计算几何体的包围盒属性.boundingBox。 几何体包围盒属性.boundingBox默认值为空null执行.computeBoundingBox()方法才会计算该几何体的包围盒Box3然后赋值给.boundingBox属性。 几何体包围球属性.boundingSphere使用方式和包围盒属性.boundingBox一样。 var geometry new THREE.SphereGeometry(50, 100, 100); //球体 // .computeBoundingBox()方法计算.boundingBox的属性值 geometry.computeBoundingBox(); console.log(包围盒属性, geometry.boundingBox); // 包围球相关属性和计算方法和包围盒一样 geometry.computeBoundingSphere(); console.log(包围球属性, geometry.boundingSphere);几何体居中方法center() 在空间坐标系中把几何体居中也就是几何体对应的包围盒中心平移到坐标原点。 // 几何体的中心默认与坐标原点重合 var geometry new THREE.BoxGeometry(50, 50, 50); // 几何体沿着x轴平移50几何体的顶点坐标变化 geometry.translate(50, 0, 0); // 居中偏移的几何体居中 geometry.center();Box3方法.expandByObject() 获得层级模型的包围盒一个层级模型可能包含多个子孙后代具体点说比如一个Group对象有多个网格模型Mesh作为子对象。 加载一个层级模型并计算它的包围盒 var loader new THREE.ObjectLoader(); loader.load(group.json, function(group) {scene.add(group);//加载返回的模型对象插入场景var box3 new THREE.Box3()// 计算层级模型group包围盒box3.expandByObject(group)console.log(查看包围盒box3, box3); })Box3方法.expandByScalar() 包围盒整体尺寸放大 // 缩放包围盒尺寸放大1.5倍 box3.expandByScalar(1.5)Box3方法.getSize() 返回包围盒具体的长宽高尺寸 var v3 new THREE.Vector3() // 获得包围盒长宽高尺寸结果保存在参数三维向量对象v3中 box3.getSize(v3) console.log(查看返回的包围盒尺寸, v3);Box3方法.getCenter() 计算返回包围盒几何中心 // 计算一个层级模型对应包围盒的几何体中心 var center new THREE.Vector3() box3.getCenter(center) console.log(查看几何体中心坐标, center);Sphere方法.getBoundingSphere() 包围盒Box3和包围球Sphere可以相互等价转化通过包围盒对象来计算包围球对象 var sphere new THREE.Sphere() // 计算包围盒box3对应的包围球 box3.getBoundingSphere(sphere) console.log(查看通过box3重置的sphere, sphere);欧拉对象Euler和四元数Quaternion 欧拉对象和四元数主要用来表达对象的旋转信息。 关键词欧拉Euler、四元数Quaternion、矩阵Matrix4 欧拉对象Euler 构造函数Euler(x,y,z,order) 参数xyz分别表示绕xyz轴旋转的角度值角度单位是弧度。参数order表示旋转顺序,默认值XYZ也可以设置为YXZ、YZX等值 // 创建一个欧拉对象表示绕着xyz轴分别旋转45度0度90度 var Euler new THREE.Euler( Math.PI/4,0, Math.PI/2);设置欧拉对象的 var Euler new THREE.Euler(); Euler.x Math.PI/4; Euler.y Math.PI/2; Euler.z Math.PI/4;四元数Quaternion 四元数对象Quaternion使用x、y、z和w四个分量表示。 var quaternion new THREE.Quaternion(); console.log(查看四元数结构,quaternion); console.log(查看四元数w分量,quaternion.w);四元数方法.setFromAxisAngle() 四元数的方法.setFromAxisAngle(axis, angle)通过旋转轴axis和旋转角度angle设置四元数数据也就是x、y、z和w四个分量。 绕单位向量Vector3(x,y,z)表示的轴旋转θ度 k sinθ/2 q( xk , yk , z*k, cosθ/2) var quaternion new THREE.Quaternion(); // 旋转轴new THREE.Vector3(0,1,0) // 旋转角度Math.PI/2 quaternion.setFromAxisAngle(new THREE.Vector3(0,1,0),Math.PI/2) console.log(查看四元数结构,quaternion);四元数乘法.multiply() 对象的一个旋转可以用一个四元数表示两次连续旋转可以理解为两次旋转对应的四元数对象进行乘法运算。 // 四元数q1、q2分别表示一个旋转两个四元数进行乘法运算相乘结果保存在q2中 // 在q1表示的旋转基础在进行q2表示的旋转操作 q1.quaternion.multiply( q2 );欧拉、四元数和矩阵转化 欧拉对象、四元数对象和旋转矩阵可以相关转化都可以表示旋转变换。 Matrix4.makeRotationFromQuaternion(q) 通过矩阵对象Matrix4的.makeRotationFromQuaternion(q)方法可以把四元数转化对应的矩阵对象。 quaternion.setFromEuler(Euler) 通过欧拉对象设置四元数对象 Euler.setFromQuaternion(quaternion) 四元数转化为欧拉对象 Object3D Object3D对象角度属性.rotation的值是欧拉对象Euler,四元数属性.quaternion的值是四元数对象Quaternion。 执行Object3D对象旋转方法会同时改变对象的角度属性和四元数属性。四元数属性和位置.position、缩放属性.scale一样会转化为对象的本地矩阵属性.matrix,本地矩阵属性值包含了旋转矩阵、缩放矩阵、平移矩阵。 Object3D对象角度属性.rotation和四元数属性.quaternion是相互关联的一个改变会同时改变另一个。 // 一个网格模型对象基类是Object3D var mesh new THREE.Mesh() // 绕z轴旋转 mesh.rotateZ(Math.PI)console.log(查看角度属性rotation,mesh.rotation); console.log(查看四元数属性quaternion,mesh.quaternion);.rotateOnAxis(axis, angle)表示绕绕着任意方向某个轴axis旋转一定角度angle绕X、Y和Z轴旋转对应的方法分别是rotateX()、rotateY()和rotateZ(),绕着XYZ特定轴旋转的方法是基于.rotateOnAxis()方法实现的。 // Object3D.js源码 rotateOnAxis: function () {var q1 new Quaternion(); // 旋转轴axis旋转角度anglereturn function rotateOnAxis( axis, angle ) { // 通过旋转轴和旋转角度设置四元数的xyzw分量q1.setFromAxisAngle( axis, angle ); // Object3D对象的四元数属性和四元数q1相乘this.quaternion.multiply( q1 );return this;};}(),几何计算相关API Threejs封装了一些和几何计算相关的API比如线段Line3、三角形Triangle、射线Ray、平面Plane… 线段Line3 通过起始点定义一条线段。 // 创建一个线段对象Line3 var line3 new THREE.Line3(); // 线段起点坐标 line3.start new THREE.Vector3(0, 0, 0); // 线段终点坐标 line3.end new THREE.Vector3(10, 10, 10);计算线段中点或者说计算两点的中点 // 创建一个三维向量对象表示线段中点 var center new THREE.Vector3(); // 执行getCenter方法计算线段中点结果保存到参数 line3.getCenter(center) console.log(查看线段中点, center);计算线段的距离或者说计算两点之间的距离 // 计算线段长度 var L line3.distance(); console.log(查看线段距离, L); // 计算线段长度平方 var L2 line3.distanceSq(); console.log(查看线段距离平方, L2);可以通过向量对象Vector3的.distanceTo()方法计算两点之间距离 // 线段起点坐标 var p1 new THREE.Vector3(0, 0, 0); // 线段终点坐标 var p2 new THREE.Vector3(10, 10, 10); // Vector3的方法distanceTo()计算两点之间距离 var length p1.distanceTo(p2) console.log(两点之间距离, length);射线Ray // 创建射线对象Ray var ray new THREE.Ray() // 设置射线起点 ray.origin new THREE.Vector3(1,0,3); // 设置射线方向 ray.direction new THREE.Vector3(1,1,1).normalize();通过射线Ray的.intersectTriangle()方法判断射线和一个三角形区域是否相交,如果相交返回交点坐标不相交返回null。 // 三角形三个点坐标 var p1 new THREE.Vector3(20, 0, 0); var p2 new THREE.Vector3(0, 0, 10); var p3 new THREE.Vector3(0, 30, 0); // 相交返回交点不相交返回null var result ray.intersectTriangle(p1,p2,p3) console.log(查看是否相交, result);通过射线Ray的.intersectsBox(Box3)方法判断射线和一个包围盒Box3是否相交通过射线Ray的.intersectsSphere(Sphere)方法判断射线和一个包围球Sphere是否相交… 三角形Triangle // 创建一个三角形对象 var Triangle new THREE.Triangle() // 三角形顶点1 Triangle.a new THREE.Vector3(20, 0, 0); // 三角形顶点2 Triangle.b new THREE.Vector3(0, 0, 10); // 三角形顶点3 Triangle.c new THREE.Vector3(0, 30, 0);通过三角形对象Triangle的.getArea()方法可以计算一个三角形区域的面积如果你想计算一个网格模型的表面就可以遍历网格模型对应几何体所有的三角形区域计算面积然后累加。 // .getArea()方法返回三角形面积 var S Triangle.getArea(); console.log(三角形面积, S);通过三角形对象Triangle的.getMidpoint()方法计算三角形重心封装的算法就是三个顶点坐标的算术平均值。 var Midpoint new THREE.Vector3(); // 计算三角形重心结果保存在参数Midpoint Triangle.getMidpoint(Midpoint); console.log(三角形重心, Midpoint);通过三角形对象Triangle的.getNormal()方法计算三角形法线方向封装的算法简单说就是两条边构成的向量叉乘后获得垂直三角形面的向量。 var normal new THREE.Vector3(); // 计算三角形法线方向结果保存在参数normal Triangle.getNormal(normal); console.log(三角形法线, normal);平面Plane 通过平面法线方向.normal和平面到坐标原点距离.constant来定义一个平面对象Plane // 创建一个平面对象Plane var plane new THREE.Plane(); // 设置平面法线方向 plane.normal new THREE.Vector3(0, 1, 0); // 坐标原点到平面的距离区分正负 plane.constant 30;执行平面对象方法.setFromCoplanarPoints(a,b,c)通过三个顶点坐标来设置一个平面对象Plane三个点按照逆时针顺序来确定平面对象的法向量normal方向。 // 创建一个平面对象Plane var plane new THREE.Plane(); // 三个点坐标 var p1 new THREE.Vector3(20, 0, 0); var p2 new THREE.Vector3(0, 0, 10); var p3 new THREE.Vector3(0, 30, 0); // 通过三个点定义一个平面 plane.setFromCoplanarPoints(p1,p2,p3); console.log(plane.normal, plane.normal); console.log(plane.constant, plane.constant);通过平面对象的.distanceToPoint(point)方法计算点到平面的垂线距离。 var point new THREE.Vector3(20, 100, 330); // 计算空间中一点到平面的垂直距离 var L plane.distanceToPoint(point); console.log(点到平面距离, L);着色器编程 Three.js自定义着色器Shader 学习Three.js的着色器的内容之前最好有一些WebGL的基础可以不深入了解但是要对WebGL渲染流程和着色器语言GLSL有一定的基本认知。如果你没有WebGL基础可以学习下本站的WebGL视频教程。 MeshPhongMaterial、PointsMaterial等three.js的材质材质对象本质上都是着色器代码 Three.js的WebGL渲染器在调用渲染方法render渲染场景的时候会根据材质的type值调用路径 src\renderers\shaders下的着色器代码编译后在GPU中执行。 Three.js提供了RawShaderMaterial和ShaderMaterial两个API用来辅助开发者自定义着色器代码。这个着色器API和其它的three.js的材质对象的基类一样都是Material会继承基类Material的属性和方法。 视频和源码 视频讲解和源码下载——Three.js视频教程进阶部分 ShaderMaterial ShaderMaterial构造函数的参数和其它材质对象构造函数一样是一个对象参数对象包含一些特定的属性执行构造函数参数对象的属性会转化为材质对象对应的属性。 ShaderMaterial顶点着色器和片元着色器属性 GPU的顶点着色器单元用来处理顶点位置、顶点颜色、顶点向量等等顶点数据片元着色器单元用来处理片元(像素)数据。一个WebGL程序的着色器代码包含顶点着色器和片元着色器顶点着色器代码运行在GPU的顶点着色器单元片元着色器代码运行在片元着色器单元。 ShaderMaterial对象具有两个用来设置自定义着色器代码的属性顶点着色器属性vertexShader和片元着色器属性fragmentShader顶点着色器属性vertexShader的属性值是顶点着色器代码字符串片元着色器属性fragmentShader的属性值是片元着色器代码字符串。 着色器代码编写 通过three.js的着色器材质构造函数ShaderMaterial编写着色器代码和原生WebGL中编写着色器代码语法上是一样的不同的地方在于更加方便有些代码不用自己写Three.js渲染器会帮你自动设置一些代码比如声明一些常见的变量通常来说在顶点着色器中把表示顶点的位置数据的变量position赋值给着色器内置变量gl_Position需要首先声明attribute vec3 position;,如果使用ShaderMaterial构造函数则不用程序员手动声明position变量Three.js渲染器后自动帮你拼接一段该代码具体的原理可以参考路径three.js-master\src\renderers\webgl\WebGLProgram.js下的WebGLProgram.js代码模块Threejs渲染器在渲染场景的时候从ShaderMaterial提取着色器代码后会拼接一段前缀字符串然后才会传入GPU中执行前缀包含一些常用的attribute变量和uniform变量。关于着色器材质对象ShaderMaterial的一些系统自动化处理的地方这里先不展开讲解后面会逐步讲解。 顶点着色器代码 script idvertexShader typex-shader/x-vertex// 使用ShaderMaterial类顶点位置变量position无需声明顶点着色器可以直接调用// attribute vec3 position;void main(){// 逐顶点处理顶点位置数据赋值给内置变量gl_Positiongl_Position vec4( position, 1.0 );} /script片元着色器代码 script idfragmentShader typex-shader/x-fragmentvoid main() {// 逐片元处理每个片元或者说像素设置为红色gl_FragColor vec4(1.0,0.0,0.0,1.0);} /script设置vertexShader和fragmentShader属性值 通过元素.textContent属性返回script标签中着色器代码字符串然后把着色器字符串赋值给ShaderMaterial材质对象对应的属性。 var material new THREE.ShaderMaterial({vertexShader: document.getElementById(vertexShader).textContent,fragmentShader: document.getElementById(fragmentShader).textContent, });ShaderMaterial和其它Three.js的材质一样作为网格模型或点线模型对象的参数使用。 var mesh new THREE.Mesh(geometry, material); //网格模型对象Mesh scene.add(mesh); //网格模型添加到场景中顶点数据自动化传递 在原生WebGL代码中如果顶点或片元着色器代码如果声明了一个变量比如顶点着色器中声明了一个顶点位置变量attribute vec3 position;需要通过WebGL API把JavaScript中的几何体顶点位置数据传递给顶点着色器中的顶点位置变量position这样的话CPU执行顶点着色器代码的时候才能够处理顶点数据。 使用ShaderMaterial API的好处就是这个过程Three.js渲染器系统会自动解析几何体对象Geometry中顶点位置、颜色、法向量等数据然后传递给着色器中的相应变量。具体的解析过程可以参考路径three.js-master\src\renderers\下的渲染器代码WebGLRenderer.js文件和路径three.js-master\src\renderers\webgl下面多个three.js文件。 WebGL渲染器在解析模型几何体中顶点数据的时候Geometry类型的几何体会自动转化为缓冲类型的几何体BufferGeometry, BufferGeometry几何体对象具有.attributes属性BufferGeometry.attributes具有顶点位置、顶点法向量、顶点uv坐标等属性对应着色器中相应的attribute变量。 可以通过BufferGeometry或GeometryAPI创建一个空的几何体然后手动设置顶点数据也可以使用一些立方体或其他几何体的API创建一个几何体API使用这些几何体API的时候会自动生成顶点的相关数据然后渲染的时候WebGL渲染器自动传递给着色器中声明的相应变量。关于几何体BufferGeometry和顶点相关知识这里不再展示详述有不理解的地方可以多学习原生WebGL教程和本站Threejs视频教程中关于几何体顶点讲解的章节。 var geometry new THREE.BufferGeometry(); //创建一个Buffer类型几何体对象 var vertices new Float32Array([0.6, 0.2, 0, //顶点1坐标0.7, 0.6, 0, //顶点2坐标0.8, 0.2, 0, //顶点3坐标-0.6, -0.2, 0, //顶点4坐标-0.7, -0.6, 0, //顶点5坐标-0.8, -0.2, 0, //顶点6坐标 ]); // 创建属性缓冲区对象 3个为一组表示一个顶点的xyz坐标 var attribue new THREE.BufferAttribute(vertices, 3); // 设置几何体attributes属性的位置属性 geometry.addAttribute( position, attribue );RawShaderMaterial 原生着色器材质对象RawShaderMaterial和着色器材质对象ShaderMaterial一样具有顶点着色器和片元着色器属性同样可以自动传递顶点数据区别在于着色器中使用的一些常见attribute或uniform变量原生着色器材质对象RawShaderMaterial需要程序员手动编写系统不会自动化添加变量声明的前缀。 顶点着色器代码自动声明顶点位置属性position变量。 script idvertexShader typex-shader/x-vertexattribute vec3 position;void main(){gl_Position vec4( position, 1.0 );} /script绘制模式 如果你对WebGL或OpenGL有一点了解应该都知道一系列的顶点数据可以通过绘制模式来控制渲染效果一个顶点数据可以渲染为一个点也可以使用线条模式把点连成线绘制出来也可以通过三角形模式每三个点绘制一个三角面来一系列三角形构成一个网格模型。 Three.js渲染器解析渲染的时候会根据模型的类型来判断如何渲染解析点模型Points的时候会启用点渲染模式解析线模型Line、LineLoop、LineSegments的时候会启用对应的线条绘制模式解析网格模型Mesh会启用三角形绘制模式。 点绘制模式在顶点着色器代码中可以通过设置内置变量gl_PointSize设置点的渲染大小如果直线或三角形绘制模式不需要内置变量gl_PointSize。 var point new THREE.Points(geometry, material);void main(){gl_PointSize20.0;// 控制渲染的点大小gl_Position vec4( position, 1.0 ); }直线绘制模式连点成线 var line new THREE.Line(geometry, material);三角形绘制模式三个顶点确定一个三角形一个个三角形区域构成一个网格模型 var mesh new THREE.Mesh(geometry, material);Three.js着色器——矩阵变换 本节课讲解如何通过ShaderMaterial编写顶点矩阵变换的代码Three.js的渲染器解析场景和相机参数进行渲染的时候会从模型对象获得几何体顶点对应的模型矩阵modelMatrix从相机对象获得视图矩阵viewMatrix和投影矩阵projectionMatrix在着色器中通过获得模型矩阵、视图矩阵、投影矩阵对顶点位置坐标进行矩阵变换。 本节课不会详细去解释模型矩阵、视图矩阵、投影矩阵的具体算法主要是讲解这些矩阵在three.js自定义着色器的时候如何更好的使用。 模型变换 模型矩阵包含了一个几何体的旋转、平移、缩放变换。 如果你有基本的图形学和线性代数知识应该知道几何平移、缩放、旋转变换都是几何变换的一部分几何变换可以使用线性代数的矩阵来表示平移对应一个平移矩阵旋转一个对应旋转矩阵一个几何体经过了多次旋转、平移等几何变换每一个变换都有一个对应的矩阵可以表示所有几何变换对应矩阵的乘积就是一个复合矩阵可以称为模型矩阵modelMatrix。 着色器使用模型矩阵 使用ShaderMaterial编写着色器代码的时候模型矩阵modelMatrix不用程序员手动声明Three.js渲染器 系统渲染的时候会自动往ShaderMaterial顶点着色器字符串中插入一句uniform mat4 modelMatrix; script idvertexShader typex-shader/x-vertex// uniform mat4 modelMatrix;//不需要声明void main(){// 模型矩阵modelMatrix对顶点位置坐标进行模型变换gl_Position modelMatrix*vec4( position, 1.0 );} /scriptmodelMatrix变量数据传递 查看网格模型Mesh的基类Object3D可知道网格模型有一个本地矩阵属性.matrix.matrix的属性值是一个Three.js的矩阵对象 Matrix4。对网格模型进行平移、旋转、缩放等几何变换都会改变网格模型本地矩阵属性.matrix的属性值。 mesh.rotateY(Math.PI/6); mesh.rotateX(Math.PI/6);如果网格模型mesh有一个父对象父对象的几何变换同样会传递到网格模型也就是说顶点着色器中默认的模型矩阵变量modelMatrix对应的不是网格模型自身的几何变换而是网格模型的自身以及它所有父对象的几何变换一个网格模型自身以及父对象所有的几何变换会体现在自己的世界矩阵属性.matrixWorld上。 一个网格模型mesh都包含一个几何体Geometry一个几何体中有一系列的顶点位置数据这些顶点位置数据需要传递给着色器中顶点位置变量position同样着色器中uniform关键字声明的模型矩阵变量modelMatrix也需要传递矩阵数据。 Three.js渲染器渲染的时候会自动从一个Threejs的模型对象提取它世界矩阵属性.matrixWorld的属性值然后传递给着色器的模型矩阵变量modelMatrix这个过程不需要程序员设置Three.js系统会自动完成。如果你编写WebGL原生代码都知道需要调用WebGL的相关API完成数据的传递过程比较麻烦对于开发者来说不太友好为了开发者更好的编写着色器代码Three.js引擎封装了这些WebGL API。 视图矩阵和投影矩阵 相机对象本质上就是存储视图矩阵和投影矩阵的信息的一个对象基类Camera的.matrixWorldInverse属性对应的就是着色器中视图矩阵变量viewMatrix基类Camera的投影矩阵属性.projectionMatrix对应着色器中的投影矩阵变量projectionMatrix。 使用ShaderMaterial构造函数自定义顶点着色器的时候视图矩阵viewMatrix和投影矩阵projectionMatrix一样不需要手动声明WebGL渲染器会通过WebGLProgram.js模块自动声明这两个变量在顶点着色器代码中插入uniform mat4 viewMatrix;和uniform mat4 projectionMatrix;。 Three.js渲染器执行renderer.render(scene, camera)的时候会解析相机对象的信息把相机的矩阵数据自动传递给着色器中的视图矩阵变量viewMatrix和投影矩阵变量projectionMatrix。 script idvertexShader typex-shader/x-vertexvoid main(){gl_Position projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 );} /script模型矩阵和视图矩阵构成的复合矩阵称为模型视图矩阵简称模视矩阵modelViewMatrix,模视矩阵和模型、视图、投影矩阵的使用方式是一样系统会在着色器代码自动声明该变量同时把相关的矩阵数据传递给该变量。 script idvertexShader typex-shader/x-vertexvoid main(){gl_Position projectionMatrix*modelViewMatrix*vec4( position, 1.0 );} /scriptWebGL坐标系 在原生WebGL编程的时候WebGL坐标系的z轴垂直canvas画布x和y轴分别对应于canvas画布的水平和竖直方向你可以发现能够显示在canvas画布上的顶点坐标范围是[-1,1]如果顶点的xyz某个分量上的坐标值不在-1~1区间内会被剪裁掉不显示。 平时编写Three.js应用程序程序默认情况下在Three.js系统中一个模型对应的顶点要经过模型、视图和投影变换后才会在canvas画布上显示出来如果一个顶点的坐标向量经过一系列的矩阵变换后超出了[-1,1]范围就不会显示在canvas画布上平时编程的时候你可以能会遇到相机参数设置不合适看不到场景中模型的情况因为视图、投影矩阵的值是由相机的具体参数决定的相机参数不合适视图、投影矩阵就会对模型进行不合理的缩放和偏移导致canvas画布上看不到场景中的模型。 着色器——uniform 使用着色器语言GLSL编写着色器代码的时候都会使用关键字attribute和uniform来声明一些变量通常关键字attribute用来声明一些几何体顶点数据例如顶点位置数据、顶点法向量数据…uniform关键字通常用来声明模型矩阵、光源颜色、光源位置等变量。 attribute属性uniform统一 Three.js渲染器渲染场景的时候几何体的顶点位置、颜色、法向量等数据系统会自动传递给着色器中attribute关键字声明的对应顶点变量。 着色器中uniform关键字声明的模型矩阵modelMatrix、视图矩阵viewMatrix、投影矩阵projectionMatrix等Three.js系统定义的uniform变量Threejs系统会自动从对应的Threejs对象中解析数据并自动传递。比如视图矩阵的值Three.js系统会从相机对象中获得具体的值然后传递给viewMatrix变量。 自定义uniform变量数据传递 如果程序员在着色器中任意命名自定义了一个uniform变量,如果需要给该uniform变量传递数据在原生WebGL中需要特定的WebGL API来传递数据在Three.js中不需要这样只需要在着色器材质对象的ShaderMaterial的属性.uniforms中定义一个属性属性名字和着色器中uniform变量保持一致对于程序员而言只需要保持名字一致至于数据传递过程Three.js系统会自动帮你完成。 属性.uniforms使用案例 片元着色器中通过uniform关键字声明了一个颜色变量color为了给该变量传递数据在ShaderMaterial对象的uniforms属性中定义了一个名为color的属性按照Three.js系统uniform变量数据自动传递的机制如果你在着色器代码中自定义声明了多个uniform变量只要名字和ShaderMaterial对象中uniform数据的名字保持一直就可以正确完成数据传递。 !-- 片元着色器 -- script idfragmentShader typex-shader/x-fragment// color变量数据来自ShaderMaterial的uniforms属性的color属性uniform vec3 color;void main() {// gl_FragColor vec4(1.0,0.0,0.0,1.0);gl_FragColor vec4(color,1.0);} /scriptShaderMaterial的uniforms属性代码 var material new THREE.ShaderMaterial({//定义uniforms属性uniforms的属性和着色器中的uniform变量相对应uniforms:{// 颜色属性clor对应片元着色器代码中uniform声明的color变量color:{value:new THREE.Color(0xff0000)}},// 顶点着色器vertexShader: document.getElementById(vertexShader).textContent,// 片元着色器fragmentShader: document.getElementById(fragmentShader).textContent, });数据类型 着色器声明的uniform变量数据类型要和着色器材质对象的ShaderMaterial的属性.uniforms的属性的属性值数据类型保持一致。例如value:new THREE.Color(0xff0000)对应的着色器中数据类型是vec3,value:new THREE.Matrix4()对应的着色器中数据类型是mat4。 value的值和和着色器数据类型的对应关系可以参考Threejs文档core分类下的Uniform Three.js着色器——光照计算 为了更好的渲染效果一般都会对网格模型进行光照计算,光照计算的相关算法是对生活中光线漫反射、镜面反射等光学现象的模拟如果你不了解光照计算的一些算法可以去学习一下原生的WebGL教程和图形学方面的知识。 前面说过Three.js的材质对象本质上都是着色器代码有些材质支持光照计算有些材质不支持光照计算比如基础网格材质MeshBasicMaterial,有些材质支持光照计算支持光照计算的材质具体的算法也不尽相同,兰伯特网格材质MeshPhongMaterial、高光网格材质MeshPhongMaterial、标准网格材质MeshStandardMaterial 。 平行光模型 本节课通过一个平行光的案例来进一步让大家认识着色器材质对象ShaderMaterial的使用。 顶点着色器 系统自动声明的变量 使用ShaderMaterial构造函数自定义着色器的时候,顶点法向量变量normal和顶点位置变量’position’一样不用手动声明Three.js渲染器系统会通过WebGLPrograms.js模块自动声明attribute vec3 normal;。 geometry.attributes.position对应着色器中position变量geometry.attributes.normal对应着色器中normal变量 var geometry new THREE.BoxBufferGeometry(0.5, 0.5, 0.5); // 查看几何体的顶点位置、顶点法向量数据 console.log(geometry.attributes);法向量矩阵normalMatrix和模型矩阵modelMatrix一样不需要手动声明系统会自动声明可以直接在main函数中使用。顶点的位置进行了旋转等变换顶点的法向量方向肯定会发生变化所以需要一个法向量矩阵对顶点法向量进行变换Threejs渲染器模块会根据顶点位置的相关变换矩阵计算normalMatrix的值。 script idvertexShader typex-shader/x-vertex//varying声明顶点法向量插值后变量varying vec3 v_normal;void main(){// normalMatrix法向量矩阵模型的顶点进行了模型变换顶点的法向量要跟着变化// 顶点的法向量执行插值计算v_normalnormalMatrix*normal;// 模型矩阵modelMatrixgl_Position modelMatrix*vec4( position, 1.0 );} /script片元着色器 片元着色器代码中包含了平行光漫反射计算的光照模型算法。光线的入射角不同反射的强度不同一个立方的表面法线方向不相同与平行光的夹角不同每个面的明暗就不同。关于光照模型更多的知识可以学习原生WebGL教程和图形学。 script idfragmentShader typex-shader/x-fragment// 声明一个颜色变量表示网格模型颜色uniform vec3 u_color;// 顶点法向量插值后的结果一个片元数据对应一个法向量数据varying vec3 v_normal;// uniform声明平行光颜色变量uniform vec3 u_lightColor;//平行光方向变量uniform vec3 u_lightDirection;void main() {// 法向量归一化vec3 norlmal2 normalize(v_normal);// 计算平行光方向向量和片元法向量的点积// 不同的入射角度反射强度不同float dot dot(u_lightDirection, norlmal2);// 计算反射后的颜色 光线颜色*物体颜色*dotvec3 reflectedLight u_lightColor * u_color * dot;// 反射颜色赋值给内置变量gl_FragColorgl_FragColor vec4(reflectedLight,1.0);} /scriptuniforms定义 // 定义材质对象的uniforms属性传递着色器中uniform变量对应的值 uniforms: {// 网格模型颜色u_color: {value: new THREE.Color(0xff0000)},// 平行光光源颜色u_lightColor: {value: new THREE.Color(0xffffff)},// 平行光的方向u_lightDirection: {value: new THREE.Vector3(-1.0, -1.0, 1.0).normalize()}, },着色器——颜色插值计算 本节课通过一个颜色插值的案例来加强大家对Three.js着色器代码的理解。 几何体数据 创建一个缓冲类型的几何体BufferGeometry然后给该几何体Geometry对象设置顶点位置数据和顶点颜色数据顶点颜色和顶点位置。 把几何体作为网格模型Mesh的参数也就是使用三角形模式渲染几何体6个顶点构成2个三角形每个三角形有三个顶点每个顶点有一个颜色三角形中间的颜色是三个顶点颜色插值计算的结果整个三角形会显示为彩色。 几何体顶点位置数据 如果不是特殊需求实际开发中一般也不用程序员自定义顶点位置数据通常通过3D美术软件创建这里为了大家更容易深度理解Three.js底层知识自定义顶点位置数据。 //类型数组创建顶点位置position数据 var vertices new Float32Array([0, 0, 0, //顶点1坐标50, 0, 0, //顶点2坐标0, 100, 0, //顶点3坐标0, 0, 10, //顶点4坐标0, 0, 100, //顶点5坐标50, 0, 10, //顶点6坐标 ]); // 创建属性缓冲区对象 var attribue new THREE.BufferAttribute(vertices, 3); //3个为一组作为一个顶点的xyz坐标 // 设置几何体attributes属性的位置position属性 geometry.attributes.position attribue;几何体顶点颜色数据 顶点颜色数据和顶点位置数据一一对应 //类型数组创建顶点颜色color数据 var colors new Float32Array([1, 0, 0, //顶点1颜色0, 1, 0, //顶点2颜色0, 0, 1, //顶点3颜色1, 1, 0, //顶点4颜色0, 1, 1, //顶点5颜色1, 0, 1, //顶点6颜色 ]); // 设置几何体attributes属性的颜色color属性 //3个为一组,表示一个顶点的颜色数据RGB geometry.attributes.color new THREE.BufferAttribute(colors, 3);材质对象vertexColors属性 材质对象Material是自定义材质对象ShaderMaterial的基类自然ShaderMaterial会继承基类Material的vertexColors属性和side属性。 前面课程讲到过Three.js的点材质、线材质和网格材质的颜色默认是由color属性值决定的如果希望Three.js渲染器系统使用几何体的顶点颜色数据进行渲染需要设置vertexColors: THREE.VertexColors,,对于自定义材质对象ShaderMaterial同样需要这样设置。 var material new THREE.ShaderMaterial({// 顶点着色器vertexShader: document.getElementById(vertexShader).textContent,// 片元着色器fragmentShader: document.getElementById(fragmentShader).textContent,//以顶点颜色为准进行渲染vertexColors: THREE.VertexColors,// 双面可见side:THREE.DoubleSide, });着色器 顶点着色器 使用ShaderMaterial API的时候顶点颜色变量和顶点位置变量一样不需要手动声明,系统会自动声明attribute vec3 color; 插值计算的实现要通过着色器语言的关键字varying实现。 // attribute vec3 position; // attribute vec3 color;// varying关键字声明一个变量表示顶点颜色插值后的结果 varying vec3 vColor; void main(){// 顶点颜色数据进行插值计算vColor color;// 投影矩阵projectionMatrix、视图矩阵viewMatrix、模型矩阵modelMatrixgl_Position projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 ); }片元着色器 // 顶点片元化后有多少个片元就有多少个颜色数据vColor varying vec3 vColor; void main() {//把插值后的到颜色数据赋值给对应的片元gl_FragColor vec4(vColor,1.0); }着色器——纹理贴图 Three.js网格材质都有一个map属性该属性用来设置网格模型的颜色贴图,渲染器系统会调用网格材质对应的着色器代码解析map属性的值进行渲染。本节课通过自定义着色器的纹理贴图代码来展示网格材质map属性对应的着色器原理。 顶点纹理坐标数据uv 通过Three.js的球体、矩形平面、立方体等特定几何体构造函数创建一个几何体对象构造函数会按照特定的算法生成顶点位置position、顶点法向量normal、顶点纹理坐标uv数据。 //球体 var geometry new THREE.SphereBufferGeometry(60, 25, 25); // 查看几何体的顶点数据 console.log(geometry.attributes); // 顶点纹理坐标attributes.uv的itemSize属性值是2意味着顶点纹理坐标是二维向量 // 查看几何体顶点纹理坐标数据uv console.log(geometry.attributes.uv);纹理贴图 在ShaderMaterial中设置一个属性来表示纹理贴图对应着色器中texture变量。 uniforms: {// texture对应顶点着色器中uniform声明的texture变量texture: {// 加载纹理贴图返回Texture对象作为texture的值// Texture对象对应着色器中sampler2D数据类型变量value: new THREE.TextureLoader().load(./Earth.png)}, },着色器 编写着色器代码从纹理贴图上采集像素值然后赋值给片元。 顶点着色器 使用ShaderMaterial API的时候顶点纹理坐标变量uv和顶点位置变量position一样不需要手动声明,系统会自动声明attribute vec2 uv; // varying关键字声明一个变量表示顶点纹理坐标插值后的结果 varying vec2 vUv; void main(){// 顶点纹理坐标uv数据进行插值计算vUv uv;// 投影矩阵projectionMatrix、视图矩阵viewMatrix、模型矩阵modelMatrixgl_Position projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 ); }片元着色器 uniform关键字声明一个数据类型为sampler2D的变量texture对应uniforms中texture的值。 // 声明一个纹理对象变量 uniform sampler2D texture; // 顶点片元化后有多少个片元就有多少个纹理坐标数据vUv varying vec2 vUv; void main() {//内置函数texture2D通过纹理坐标vUv获得贴图texture的像素值gl_FragColor texture2D( texture, vUv ); }着色器——UV动画 通过自定着色器代码的方式实现UV动画。 Texture偏移属性offset实现UV动画 .wrapS定义了纹理如何水平包裹并对应于UV映射中的U. .wrapT这定义了纹理垂直包裹的方式与UV映射中的V相对应. var texture textureLoader.load(./大气.png); // 设置重复的作用是能够让一个效果循环 texture.wrapS THREE.RepeatWrapping; texture.wrapT THREE.RepeatWrapping;渲染函数周期性执行的过程中Three.js纹理对象Texture的偏移属性offset两个分量x和y递增或递减。 // 渲染函数 function render() {// 每次渲染对纹理对象进行偏移不停的偏移纹理就产生了动画的效果texture.offset.x - 0.001;texture.offset.y 0.001;group.rotateY(-0.005)renderer.render(scene, camera); //执行渲染操作requestAnimationFrame(render); //请求再次执行渲染函数render渲染下一帧 }着色器中uniform变量更新 片元着色器中声明的一个时间变量time // 声明一个时间变量用来控制UV动画 uniform float time; // 声明一个纹理对象变量 uniform sampler2D texture; // 顶点片元化后有多少个片元就有多少个纹理坐标数据vUv varying vec2 vUv; void main() {vec2 newT vUv vec2( -0.02, 0.02 ) * time;//通过偏移后的纹理坐标newT采样像素gl_FragColor texture2D( texture, newT );// 大气层整体透明度增加gl_FragColor.a *0.6; }uniforms: {// 对应片元着色器中的时间变量timetime: {value: 0.0}, },在渲染函数中不停地更新ShaderMaterial对象uniforms属性的时间变量time的值每次执行新的渲染Threejs系统会自动更新片元着色器中的时间变量time的值。 // 创建一个时钟对象Clock var clock new THREE.Clock(); // 渲染函数 function render() {// 获得两次渲染的时间间隔deltaTimevar deltaTime clock.getDelta();// 更新uniforms中时间这样就可以更新着色器中time变量的值material.uniforms.time.value deltaTime;renderer.render(scene, camera);requestAnimationFrame(render); }着色器——着色器模块.glsl调用 路径three.js-master\src\renderers\shaders下ShaderChunk文件中有大量具有特定功能的着色器代码块.glslShaderLib文件夹下面的着色器文件是对ShaderChunk文件中的着色器代码块进行调用组合得到一个新的着色器代码新的着色器文件是一个完整的顶点着色器或片元着色器代码这些完成的顶点或片元着色器代码和Three.js的点材质、线线材质或网格材质是一一对应的。比如顶点着色器文件meshphong_vert.glsl和片元着色器文件meshphong_frag.glsl对应的是高光网格材质MeshPhongMaterial顶点着色器文件points_vert.glsl和片元着色器文件points_frag.glsl对应的是点材质PointsMaterial。 使用ShaderMaterial自定义着色器代码的时候可以手动编写着色器代码也可以调用ShaderChunk文件和ShaderLib文件夹下面的着色器代码模块。 如果想很好的复用three.js的着色器代码块至少应该阅读下着色器源码对每一个文件有一个大致的认识。 顶点位置矩阵变换 手动编写顶点位置position进行投影矩阵、相机矩阵、视图矩阵变换的着色器代码。 void main(){// 投影矩阵projectionMatrix、视图矩阵viewMatrix、模型矩阵modelMatrixgl_Position projectionMatrix*viewMatrix*modelMatrix*vec4( position, 1.0 );// modelViewMatrix等价于viewMatrix*modelMatrix// gl_Position projectionMatrix*modelViewMatrix*vec4( position, 1.0 ); } /script调用project_vertex.glsl文件 调用ShaderChunk文件夹下的project_vertex.glsl文件注意该着色器块文件中的代码依赖着色器文件begin_vertex.glsl。这里也给大家提醒ShaderChunk文件夹下的着色器模块之间既有一定的独立性有些着色器代码块有依赖别的着色器代码块。如果想更好的使用这些着色器代码块或者理解Three.js系统原理阅读每一句着色器代码的工作肯定是要做的。 void main(){//模块功能拷贝顶点位置变量值#include begin_vertex// 模块功能投影视图模型矩阵变换#include project_vertex }begin_vertex.glsl //拷贝顶点位置变量值 vec3 transformed vec3( position );project_vertex.glsl // 模型视图矩阵对顶点位置数据进行变换 // modelViewMatrix模型视图矩阵模型矩阵和视图矩阵的复合矩阵 vec4 mvPosition modelViewMatrix * vec4( transformed, 1.0 ); // projectionMatrix相机的投影矩阵 gl_Position projectionMatrix * mvPosition;map_pars_fragment.glsl 使用仅仅使用该模块注意设置预定义#define USE_MAP;。 #ifdef USE_MAP // 直接声明一个纹理贴图变量uniform sampler2D map;#endifuv_pars_fragment.glsl // 如果使用了任何纹理贴图就需要进行纹理坐标的插值计算也就是说需要使用varying关键字声明变量vUv #if defined( USE_MAP ) || defined( USE_NORMALMAP ) || defined( USE_SPECULARMAP ) ...// 片元着色器中声明一个变量vUv用于插值计算varying vec2 vUv;#endif着色器——系统uniforms模块调用UniformsLib 本节课用到两个APITHREE.UniformsUtils和THREE.UniformsLib,关于这两个API的使用官方文档并没有详细介绍如果想了解它们的使用规则建议听课简单了解或者阅读相应的源码这两个API的所在的文件目录是\three.js-master\src\renderers\shaders。THREE.UniformsUtils提供了一个方法.merge()可以组合THREE.UniformsLib提供的uniforms代码块或者自定义的uniforms属性。 THREE.UniformsLib 访问uniforms代码common块THREE.UniformsLib[common]或者THREE.UniformsLib.common访问方式都可以。执行后获得相应的值可以作为.merge()参数数组的元素。 {// 对应材质对象的颜色color属性diffuse: { value: new Color( 0xeeeeee ) },// 透明度变量opacity: { value: 1.0 },// 颜色贴图变量map: { value: null },uvTransform: { value: new Matrix3() },alphaMap: { value: null },}或者 方法.merge() 方法THREE.UniformsUtils.merge()的作用是拷贝组合THREE.UniformsLib调用的模块代码。 .merge()的参数是一个数组数组的元素满足格式 // 自定义uniform变量属性写在一个对象中{time: {value: 0.3},opacity: {value: 0.6},}THREE.UniformsLib代码块可以和自定义的uniform属性组合使用。 uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib[common],THREE.UniformsLib[fog],{// 自定义uniform变量写在一个对象中time: {value: 0.3},opacity: {value: 0.6},} ]),着色器——模仿系统的材质对象 MeshPhongMaterial、PointsMaterial等three.js的材质材质对象本质上都是着色器代码本节课就通过自定义着色器ShaderMaterial调用Threejs系统的着色器库和uniforms库来模仿这些材质对象。 UniformsLib.js 包含一些常见uniform变量对应的属性和属性值 THREE.UniformsLib.common, THREE.UniformsLib.specularmap, THREE.UniformsLib.envmap, ...UniformsUtils.js 方法.merge()用来赋值组合UniformsLib.js中提供的一些uniforms块。 uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.points,THREE.UniformsLib.fog, ]),ShaderChunk.js 通过THREE.ShaderChunk可以获得ShaderChunk和ShaderLib文件下面的所有着色器文件.glsl代码。 比如THREE.ShaderChunk.points_vert返回ShaderLib文件下points_vert.glsl文件中着色器代码字符串该着色器代码是点材质对象PointsMaterial的顶点着色器比如THREE.ShaderChunk.meshphong_frag返回ShaderLib文件下meshphong_frag.glsl文件中着色器代码字符串该着色器代码是高光网格材质对象MeshPhongMaterial的片元着色器 ShaderLib.js 该代码模块设置了每一种Three.js材质对象的顶点着色器、片元着色器和uniform变量对应的属性和值。 模仿PointsMaterial var material new THREE.ShaderMaterial({// 调用UniformsLib.js文件中的uniform变量代码块uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.points,THREE.UniformsLib.fog,]),// 获得ShaderLib文件下着色器文件points_vert.glsl代码vertexShader: THREE.ShaderChunk.points_vert,// 获得ShaderLib文件下着色器文件points_frag.glsl代码fragmentShader: THREE.ShaderChunk.points_frag });// 重置uniform变量对应属性的值 // 点尺寸设置 material.uniforms.size.value20.0; // 点颜色数据设置 material.uniforms.diffuse.value.setRGB(1,0,0)模仿MeshPhongMaterial var material new THREE.ShaderMaterial({// 调用UniformsLib.js文件中的uniform变量代码块uniforms: THREE.UniformsUtils.merge([THREE.UniformsLib.common,THREE.UniformsLib.specularmap,THREE.UniformsLib.envmap,THREE.UniformsLib.aomap,THREE.UniformsLib.lightmap,THREE.UniformsLib.emissivemap,THREE.UniformsLib.bumpmap,THREE.UniformsLib.normalmap,THREE.UniformsLib.displacementmap,THREE.UniformsLib.gradientmap,THREE.UniformsLib.fog,THREE.UniformsLib.lights,{emissive: { value: new THREE.Color( 0x000000 ) },specular: { value: new THREE.Color( 0x111111 ) },shininess: { value: 30 }}]),// 获得ShaderLib文件下着色器文件meshphong_vert.glsl代码vertexShader: THREE.ShaderChunk.meshphong_vert,// 获得ShaderLib文件下着色器文件meshphong_frag.glsl代码fragmentShader: THREE.ShaderChunk.meshphong_frag });// phong受光照影响,ShaderMaterial的lights属性需要设置为true默认是false material.lights true; // 设置材质颜色 material.uniforms.diffuse.value.setRGB(1.0, 1.0, 0.0)着色器——自动提取光源对象信息 Three.js有点光源、环境光等等各种常见光源对象一个应用中会有多个光源对象在渲染的过程中Threejs渲染器会自动从这些光源对象提取光源的颜色、位置等信息传值给着色器中的uniform变量。 本节课通过自定义着色器材质ShaderMaterialAPI来演示光源对象的自动化传值过程。 着色器 片元着色器中声明点光源、平行光源、方向光源等等光源对应的所有uniform变量。 // 大多数着色器模块依赖该模块该模块定义了大多数通用的常量、变量和函数 #include common // 光照计算的一些相关算法函数 #include bsdfs // 声明点光源、环境光、方向光等等光源的uniform变量 // lights_pars_begin模块依赖bsdfs和common #include lights_pars_begin访问光源信息 lights_pars_begin中声明一些光源相关的变量比如一个光源对象的所有属性对应一个自定义结构体所有方向光光源对象作为着色器中一个数组变量的元素。 访问第一个方向光源的方向directionalLights[0].direction,访问第二个方向光光源的颜色directionalLights[1].color uniforms属性 调用THREE.UniformsLib[lights]设置ShaderMaterial材质对象的uniforms属性。该属性设置后光源对象相关的值value都是空的只要设置material.lights true;这些光源对象相关的uniform变量对应的值valueThreejs渲染器系统会自动帮你从Threejs光源对象中提取相关的信息。具体的提取过程可以阅读three.js-master\src\renderers\webgl目录下WebGLLights.js等源码模块了解。 着色器——phong网格材质二次开发 通过着色器材质ShaderMaterial编写着色器代码自定义一个材质对象保证材质对象实现高光网格材质MeshPhongMaterial的功能同时增加灰度计算的功能。 实现思路 完全重新编写ShaderMaterial的着色器代码比较麻烦可以复制Three.js高光网格材质MeshPhongMaterial对应的顶点着色器代码meshphong_vert.glsl和片元着色器代码meshphong_frag.glsl然后在复制的代码基础上进行改写。灰度计算代码属于片元着色器代码所以只需要修改片元着色器代码meshphong_frag.glsl即可。 var material new THREE.ShaderMaterial({// 通过THREE.ShaderLib获得MeshPhongMaterial材质对象的uniforms值// 用于给着色器中的uniform变量传值uniforms: THREE.ShaderLib[phong].uniforms,// 顶点着色器vertexShader: THREE.ShaderChunk[meshphong_vert],// 片元着色器fragmentShader: document.getElementById(fragmentShader).textContent, });meshphong_frag.glsl修改 复制meshphong_frag.glsl着色器代码然后增加一个灰度计算的功能 script idfragmentShader typex-shader/x-fragment #define PHONG uniform vec3 diffuse; ... uniform float opacity; #include common ... ... #include envmap_fragment // 原来的给片元赋值的代码注释重新编写加入灰度计算功能 // gl_FragColor vec4( outgoingLight, diffuseColor.a );//计算RGB三个分量光能量之和也就是亮度 float luminance 0.299*outgoingLight.r0.587*outgoingLight.g0.114*outgoingLight.b; //逐片元赋值RGB相同均为亮度值用黑白两色表达图片的明暗变化 gl_FragColor vec4(luminance,luminance,luminance,diffuseColor.a);... ... /scriptWebGLRenderTarget(离屏渲染) WebGL渲染目标对象WebGLRenderTarget实现了WebGL的离屏渲染功能如果你有一定的WebGL或OpenGL基础对帧缓冲区、离线渲染、后处理等概念应该是不陌生的。 .render()方法 WebGL渲染器WebGLRenderer渲染方法.render()的参数( Scene, Camera, WebGLRenderTarget, forceClear ). Scene:要渲染的场景对象Camera:场景对象对应的相机对象WebGLRenderTarget:如果参数指定了WebGL渲染目标WebGLRenderTarget渲染的图像结果保存到该对象或者说保存到GPU自定义帧缓冲区中不会显示到canvas画布上 如果没有指定渲染目标也就是没有该参数渲染结果会直接显示到canvas画布上或者说渲染结果保存到canvas画布对应的默认帧缓冲区中. 无渲染目标(Canvas显示) 执行下面代码会把场景scene的渲染结果保存到canvas画布对应的默认帧缓冲区中形象点说就是可以直接显示到Cnavas画布上显示器会自动读取CPU默认帧缓冲区上面的图像数据显示。 renderer.render(scene, camera);var renderer new THREE.WebGLRenderer(); renderer.setSize(width, height); // 渲染结果canvas元素插入到body元素中 document.body.appendChild(renderer.domElement); // .domElement属性返回的一个canvas画布对象保存了render方法的渲染结果 console.log(renderer.domElement);有渲染目标(Canvas不显示) 执行下面代码WebGL渲染器的渲染结果也就是一张图像不会直接显示在Canvas画布上从Three.js的角度阐述渲染结果的RGBA像素数据存储到了WebGL渲染目标对象WebGLRenderTarget中通过目标对象的纹理属性.texture可以获得渲染结果的RGBA像素数据也就是一个Three.js的纹理对象THREE.Texture可以作为材质对象颜色贴图属性map的属性值从原生WebGL的角度阐述就是渲染结果的RGBA像素值存储到了GPU一个自定义的帧缓冲区中屏幕默认不会直接读取该缓冲区中的像素数据通过WebGL的特定API可以获取更多的信息可以百度WebGL或OpenGL离屏渲染。 // 创建一个WebGL渲染目标对象WebGLRenderTarget // 设置渲染结果(一帧图像)的像素为500x500 var target new THREE.WebGLRenderTarget(500, 500); // 设置特定target的时候render渲染结果不会显示在canvas画布上 renderer.render(scene, camera,target); //执行渲染操作.texture 通过WebGL渲染目标WebGLRenderTarget的纹理属性.texture可以获得WebGL渲染器的渲染结果该属性返回的结果是一个纹理对象THREE.Texture可以作为材质Material对象颜色贴图属性map的属性。 var material new THREE.MeshLambertMaterial({// WebGL渲染目标对象属性.texture返回一张纹理贴图也就是scene在camera下的渲染结果map: target.texture, });WebGLRenderTarget实现灰度图后处理功能 这节课主要内容是把WebGL渲染目标对象WebGLRenderTarget和自定义着色器材质对象ShaderMaterial结合实现后处理功能。 灰度计算后处理 场景Scene对象的渲染结果保存到渲染目标对象target中 var target new THREE.WebGLRenderTarget(500, 500);renderer.render(scene, camera,target);target.texture从渲染目标对象target获得渲染结果然后通过ShaderMaterial对象把渲染结果传值给片元着色器中uniform定义的变量texture然后进行灰度计算后处理。 // 自定义顶点着色器对象 var material2 new THREE.ShaderMaterial({uniforms: {// texture对应顶点着色器中uniform声明的texture变量texture: {// WebGL渲染目标对象属性.texture返回一张纹理贴图value: target.texture},},// 顶点着色器vertexShader: document.getElementById(vertexShader).textContent,// 片元着色器fragmentShader: document.getElementById(fragmentShader).textContent, });材质对象material2是场景2中一个网格模型的纹理贴图通过render渲染方法把后处理灰度效果显示出来 renderer.render(scene2, camera2);创建多个WebGL渲染目标对象 可以创建多个WebGL渲染目标对象分别保存一个WebGL渲染器的渲染结果满足一个应用需要在GPU上临时保存多个后处理效果而不显示在Canvas画布上。 // 创建一个WebGL渲染目标对象target0像素500X500 var target0 new THREE.WebGLRenderTarget(500, 500);// 创建一个WebGL渲染目标对象target1像素300X500 var target1 new THREE.WebGLRenderTarget(500, 500);后处理EffectComposer—自定义着色器 3D场景和相机设置好后执行渲染器渲染相机下的场景渲染结果就是一张图片周期性地执行渲染器渲染方法一帧一帧图片就构成了动画效果。 后处理简单的说可以理解为处理图片比如把一张彩色图变成灰度图或者给一张图片或者图片场景中一个物体添加一个一个边框… EffectComposer.js封装了WebGL渲染目标WebGLRenderTargetAPI相比直接使用WebGLRenderTarget进行后处理要方便得多。 后期处理相关.js文件路径 后处理相关的库基本都在路径three.js-master\examples\js\下面postprocessing和shaders 两个文件夹下。 EffectComposer.js库依赖RenderPass.js、ShaderPass.js、CopyShader.js。 !-- 引入EffectComposer.js库 封装了WebGLRenderTarget 可以调用WebGL渲染器的渲染方法 -- script src./three.js-master/examples/js/postprocessing/EffectComposer.js/script!-- renderPass.js库 构造函数传入场景Scene和相机Camera作为构造函数renderPass的参数 -- script src./three.js-master/examples/js/postprocessing/RenderPass.js/script!-- 这两个好像不能删除 EffectComposer依赖它们-- !-- ShaderPass.js库一个ShaderPass调用一个自定义着色器代码就构成一个后处理通道 -- script src./three.js-master/examples/js/postprocessing/ShaderPass.js/script!-- 引入CopyShader.js库 CopyShader.js包含着色器代码着色器代码功能采样一张图片像素赋值给片元 -- script src./three.js-master/examples/js/shaders/CopyShader.js/scriptEffectComposer.js EffectComposer构造函数的参数是渲染器对象renderer. var composer new THREE.EffectComposer(renderer);EffectComposer方法.render() EffectComposer构造函数的参数是WebGL渲染器,执行EffectComposer的渲染方法.render()方法相当于执行了WebGL渲染器对象的.render()方法。 function render() {// EffectComposer的渲染方法.render()执行一次相当于执行一次renderer.render()得到一帧图像composer.render();requestAnimationFrame(render); }EffectComposer方法.addPass() 该方法用于给EffectComposer对象添加后处理通道可以添加多个后处理通道每个通道就是一个处理环节通道本质就是着色器代码。 // 把渲染器作为参数 var composer new THREE.EffectComposer(renderer); // 设置renderPass通道该通道并不对渲染结果的像素数据进行处理 composer.addPass(renderPass); // 设置灰度图通道grayShaderPass对渲染结果进行灰度计算处理 composer.addPass(grayShaderPass);通道对象的属性.renderToScreen 默认值是false经过该通道的处理后的图像结果保存到EffectComposer对象的WebGL渲染目标对象WebGLRenderTarget中如果你有WebGL基础你也可以理解为把结果保存到自定义的帧缓冲区中不会在canvas画布上直接显示。 如果设置Pass.renderToScreen true;表示经过该通道的处理结果存储到系统默认的帧缓冲区中也就是直接显示在canvas画布上面。 RenderPass通道 RenderPass构造函数的参数是场景和相机对象(scene,camera) RenderPass通道的作用是把场景和相机作为参数传入获得场景的渲染结果并不对渲染结果做特定处理。如果EffectComposer对象只使用该通道可以简单认为和直接调用WebGL渲染器的render方法区别不大最终效果是一样的。一般来说RenderPass通道是EffectComposer对象的第一个通道。 var renderPass new THREE.RenderPass(scene, camera); // 渲染结果默认不显示如果renderToScreen设置为true经过该通道处理后会直接显示到Caanvas画布上 renderPass.renderToScreen true;var composer new THREE.EffectComposer(renderer); // 渲染通道插入EffectComposer对象中 composer.addPass(renderPass);THREE.ShaderPass通道 该通道是着色器通道可以自定义后处理的着色器代码作为THREE.ShaderPasss构造函数的参数。 顶点着色器和片元着色器的编写要遵守一定的格式具体格式可以参照CopyShader.js文件在该文件的基础上进行修改CopyShader.js文件中的着色器代码基本功能就是获取颜色贴图的像素值赋值给片元不做特定功能的后期处理。 下面顶点和片元着色器代码的后期处理功能就是灰度计算。 顶点着色器代码和CopyShader.js文件中顶点着色器代码一样没有改变。 script idvertexShader typex-shader/x-vertex// 声明一个变量vUv表示uv坐标插值后的结果varying vec2 vUv;void main(){// 纹理坐标插值计算vUv uv;// projectionMatrix投影矩阵 modelViewMatrix模型视图矩阵gl_Position projectionMatrix * modelViewMatrix * vec4( position, 1.0 );} /script片元着色器在CopyShader.js片元着色器的基础上进行了一定更改插入一段灰度计算的代码。 script idfragmentShader typex-shader/x-fragment// 默认设置颜色贴图的变量是tDiffuseuniform sampler2D tDiffuse;varying vec2 vUv;void main() {//采集纹素vec4 tColor texture2D( tDiffuse, vUv );//计算RGB三个分量光能量之和也就是亮度float luminance 0.299*tColor.r0.587*tColor.g0.114*tColor.b;//逐片元赋值RGB相同均为亮度值用黑白两色表达图片的明暗变化gl_FragColor vec4(luminance,luminance,luminance,1);} /script创建着色器通道ShaderPass着色器通道ShaderPass构造函数参数格式和着色器材质ShaderMaterial构造函数的选项参数一样。 //自定义后处理通道 var GreyShader {uniforms: {// 和着色器tDiffuse变量对应// THREE.ShaderPass会把渲染结果也就是一张图片的像素值对应Texture对象赋值给tDiffusetDiffuse: {value: null},},vertexShader: document.getElementById(vertexShader).textContent,fragmentShader: document.getElementById(fragmentShader).textContent, } // GreyShader作为THREE.ShaderPass的参数 var grayShaderPass new THREE.ShaderPass(GreyShader);自定义R分量提取功能的着色器代码 直接修改片元着色器就可以 void main() {gl_FragColor texture2D( tDiffuse, vUv ); }void main() {//采集纹素vec4 tColor texture2D( tDiffuse, vUv );//计算RGB三个分量光能量之和也就是亮度float luminance 0.299*tColor.r0.587*tColor.g0.114*tColor.b;//逐片元赋值RGB相同均为亮度值用黑白两色表达图片的明暗变化gl_FragColor vec4(luminance,luminance,luminance,1); }void main() {//采集纹素vec4 tColor texture2D( tDiffuse, vUv );//逐片元赋值RGB相同均为亮度值用黑白两色表达图片的明暗变化gl_FragColor vec4(tColor.r,0,0,1); }多个处理通道 多个通道之间是串联关系执行一个通道的渲染结果默认保存得到CPU自定义帧缓冲区中不会显示在Canvas画布上如果某个通道设置Pass.renderToScreen true;渲染结果就会直接显示在Canvas画布上。 var composer new THREE.EffectComposer(renderer); // 设置renderPass通道 composer.addPass(renderPass); // 设置R分量提取通道RShaderPass composer.addPass(RShaderPass); // 设置灰度图通道grayShaderPass对渲染结果进行灰度计算处理 composer.addPass(grayShaderPass); 后处理EffectComposer——直接调用常见通道 上节课讲解的是自定义通道的着色器代码本节课讲解直接调用一个特定功能的通道模块通道使用的着色器代码已经配置好不需要自己编写。 GlitchPass通道 效果随机产生电脉冲 GlitchPass通道依赖THREE.DigitalGlitch提供的uniforms对象、顶点着色器代码和片元着色器代码。 THREE.DigitalGlitch的路径three.js-master\examples\js\shaders\DigitalGlitch.js var renderPass new THREE.RenderPass(scene, camera); var GlitchPass new THREE.GlitchPass(64); GlitchPass.renderToScreen true; var composer new THREE.EffectComposer(renderer); composer.addPass(renderPass); composer.addPass(GlitchPass);FilmPass通道 模拟电视屏效果 var renderPass new THREE.RenderPass(scene, camera); var FilmPass new THREE.FilmPass(0.3, 0.4, 512, false); FilmPass.renderToScreen true; var composer new THREE.EffectComposer(renderer); composer.addPass(renderPass); composer.addPass(FilmPass);OutlinePass通道 一个模型外面添加一个高亮的外边框 var renderPass new THREE.RenderPass(scene, camera); var OutlinePass new THREE.OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera); OutlinePass.renderToScreen true; var composer new THREE.EffectComposer(renderer); composer.addPass(renderPass); composer.addPass(OutlinePass);//设置需要添加外边框的网格模型 //交互的时候可以设置一个鼠标事件点击选中了某个模型就直接把某个网格模型作为值的元素 OutlinePass.selectedObjects [mesh];
http://www.dnsts.com.cn/news/3349.html

相关文章:

  • 网站建设企业谁家好wordpress侧边栏美化
  • 自助搜优惠券网站怎么做的网站开发从整体上
  • 电子商务网站设计分析怎么做广州 网站建设公司
  • 晋中市住房与城乡建设厅网站湖南旅游
  • 济宁网站建设哪家便宜江门网站推广设计
  • 传媒网站源码带手机建设部中国建造师网查询
  • 域名拍卖网站婚礼礼网站如何做的
  • 搭积木建网站软件建网站需要哪些技术
  • 帝国cms网站地图生成器怎么接外贸订单
  • 建站工具搭建前台网站wordpress网页psd下载
  • qq查冻结网站怎么做什么是网络营销?网络营销与电商营销有什么区别?
  • 万远翔网站建设建什么网站能百度收录
  • wordpress网站防采集威海seo公司
  • 河北建设厅网站电话建筑网站绿地新里城
  • 广州网站平台怎么做中铁建设集团有限公司领导名单
  • 做网站在哪里买空间域名商城网站实例
  • html mip 网站千海网站建设 小程序
  • 丹寇服饰官方网站百度收录网站要多久
  • 特殊教育学校网站建设方案WordPress建立文档系统
  • 南京医院网站建设网站建设多少钱专业
  • 网站开发的基本语言搜素引擎排名优化技术
  • ppt怎么做网站网页制作工作描述
  • 网站设计套用模板wordpress 非小工具形式 微博秀
  • 兼职做网站的费用中国制造网外贸网官网登录
  • 在线生成网站地图营销软文200字
  • 张家界建设网站公司app和网站趋势
  • 网站建设教论坛网站建设模板的
  • 多语种网站制作街景地图手机版下载
  • 做网站流量是什么阿里巴巴网站建设的背景
  • 如何免费建设自己稳定的网站内蒙古呼和浩特市邮编