主要基于WebGLRendere类的render方法开展,需要读者有基本的计算机图形知识,比如计算机图形管线(实时渲染管线)之类的。

在Three.js的渲染中,大概可以分为以下几步:

  1. 清空当前帧缓冲区,更新MVP矩阵;
  2. 将物体分为透明和不透明两类,按照离摄像机从近到远排序(也可在Object3D单独设置renderOrder);
  3. 根据灯光信息,阴影计算,如果有开启平面裁剪就对进行剪裁;
  4. 开始逐个渲染物体,按以下顺序,背景、不透明物体、透明物体;
  5. 渲染前后还有两个类似于生命周期的回调函数,scene.onBeforeRender和scene.onAfterRender;
  6. 最后将深度、模版测试、多边形偏移恢复默认。

其中1、2和5、6都是准备和善后工作,2中为物体分为不透明、透明进行排序,原因有二:

一、为了最大限度地避免overdraw,一个重要的优化策略就是控制绘制顺序。由于深度测试的存在,如果我们可以保证物体都是从前往后绘制的,那么就可以很大程度上减少overdraw。这是因为,在后面绘制的物体由于无法通过深度测试,因此,就不会再进行后面的渲染处理。

二、把透明物体抽出来最后渲染,是为了和不透明物体进行颜色混合。

阴影计算

简单说明下,在threejs的阴影渲染的原理:

  1. 将相机移动光源位置称为shadowCamera,记录下当前像素点的深度值,记为Z1;

  2. 将相机移动原来位置, 绘制是将当前像素点的深度值Z与Z1比较,如果小于Z1则说明是光源照不到的地方,画上阴影

(为什么是小于,因为有一个默认规定,相机的上方向是以相机为原点的坐标轴的Y轴,视线为-Z轴方向)。

这一部分也可以叫做实时阴影计算,与光照贴图添加阴影(·lightMap)区别。具体逻辑在WebGLShadowMap类里,它也是做了第一步,将深度信息存在texture里,具体第二步还是要到物体渲染里进行。

WebGLShadowMap的render方法,设置了WebGL的状态,然后创建阴影贴图,最后还是使用shadowCamera,上交给WebGLRendere类的renderBufferDirect方法处理去了(这是个累死累活的打工方法,物体渲染最后也交给它了)。

物体渲染

重点到了,但还是只能简单描述下,里面细节太多了。threejs说到底是简化我们对webgl的使用,了解物体渲染,我需要简单描述下 webgl怎么绘制(以前的笔记主要为代码,就不贴了),大致分为五步:

  1. 获取webgl上下文
  2. 初始化着色器代码
  • 创建指定类型的着色器代码,上传source源码,并编译
  • 创建着色器程序,添加着色器,链接webgl
  1. 初始化着色器缓冲区
  • 创建缓冲区,绑定到程序,并推入数据(position、normal、uv、color以及MVP矩阵、灯光信息等数据)
  • 将缓冲区写入着色器指定属性,并激活
  1. 纹理绑定
  2. 绘制(选择模式、设置顶点个数)

这里需要控制的就是 指定不同的着色器代码、推入不同的数据,绑定纹理、选择模式进行绘制,也是物体渲染这块所做的。

renderObjects方法:
遍历渲染列表,如果是 摄像机阵列,再遍历摄像机,将object, scene, camera, geometry, material, group丢给renderObject处理单个物体。

renderObject方法:
renderObject就一个作用,判断物体是否为立即渲染物体(object.isImmediateRenderObject):

立即渲染物体和普通物体表现在代码上的区别是,没有用到顶点数组对象(Vertex Array Object,简称 VAO),立即渲染物体直接获取object上的position、normal、uv和color数据丢进buffer,进行绘制。

普通物体会在第一次绘制保存下position、normal、uv和color数据,下一帧绘制,如果没有改变,直接bindVertexArrayOES,就可以了,不用一个个bindBuffer。采用VAO的方式可以降低数据传输,在大场景绘制时,可以提高性能。

详细代码讲解可以看看这篇文章。

renderBufferDirect方法:渲染普通物体。

1、先调用setProgram拿到添加着色器后的着色器程序,setProgram不仅仅添加shader,也会绑定一些除顶点以外的数据。

setProgram内部,在material变化时调用initMaterial初始化着色器,每种Material有自己对应的shader代码(通过WebGLPrograms获得,这是threejs内置的),在根据environment、fog、envMap、needsLights等不同设置,会再拼接其他的shader代码,最后组成一个完整的shader代码,传入一些需要的uniform数据(包括绑定纹理),把这个program挂在materia,materia存入WebGLProperties对象里。

setProgram内部,在material不变化时,直接取出保存的数据,推入数据即可。

所以说setProgram解决了指定不同的着色器代码、推入不同的数据(除了顶点相关),绑定纹理的问题。

2、WebGLBindingStates对象的setup是用来推入顶点相关的数据,使用了 VAO,上面提及了。

3、最后renderBufferDirect会根据object的类型,设置绘制模式和顶点个数,选择绘制方法,进行绘制。

指定不同的着色器代码、推入不同的数据,绑定纹理、选择模式进行绘制四个工作就都完成了,这也就是renderBufferDirect的大概的功能。渲染器会保存物体的状态和绘制所需数据,只在物体发生改变时,才重新计算获取,不变时就直接推入着色器程序进行绘制。

其实WebGL绘制时还需要设置各种状态,比如深度测试、模版测试等,具体控制在WebGLState类里,对应的的state.setMaterial。

 this.renderBufferDirect = function ( camera, scene, geometry, material, object, group ) {if ( scene === null ) scene = _emptyScene; // renderBufferDirect second parameter used to be fog (could be null)const frontFaceCW = ( object.isMesh && object.matrixWorld.determinant() < 0 );// 获取着色器程序const program = setProgram( camera, scene, material, object );// 设置绘制所需状态state.setMaterial( material, frontFaceCW );// 获取索引和顶点数据let index = geometry.index;const position = geometry.attributes.position;// 判断有无索引和顶点数据if ( index === null ) {if ( position === undefined || position.count === 0 ) return;} else if ( index.count === 0 ) {return;}// 线框模式let rangeFactor = 1;if ( material.wireframe === true ) {index = geometries.getWireframeAttribute( geometry );rangeFactor = 2;}//设置变形动画if ( material.morphTargets || material.morphNormals ) {morphtargets.update( object, geometry, material, program );}//绑定顶点数据bindingStates.setup( object, material, program, geometry, index );let attribute;let renderer = bufferRenderer;if ( index !== null ) {attribute = attributes.get( index );renderer = indexedBufferRenderer;renderer.setIndex( attribute );}//计算需要绘制顶点个数const dataCount = ( index !== null ) ? index.count : position.count;const rangeStart = geometry.drawRange.start * rangeFactor;const rangeCount = geometry.drawRange.count * rangeFactor;const groupStart = group !== null ? group.start * rangeFactor : 0;const groupCount = group !== null ? group.count * rangeFactor : Infinity;const drawStart = Math.max( rangeStart, groupStart );const drawEnd = Math.min( dataCount, rangeStart + rangeCount, groupStart + groupCount ) - 1;const drawCount = Math.max( 0, drawEnd - drawStart + 1 );if ( drawCount === 0 ) return;//设置绘制模式和绘制方法if ( object.isMesh ) {if ( material.wireframe === true ) {state.setLineWidth( material.wireframeLinewidth * getTargetPixelRatio() );renderer.setMode( _gl.LINES );} else {renderer.setMode( _gl.TRIANGLES );}} else if ( object.isLine ) {let lineWidth = material.linewidth;if ( lineWidth === undefined ) lineWidth = 1; // Not using Line*Materialstate.setLineWidth( lineWidth * getTargetPixelRatio() );if ( object.isLineSegments ) {renderer.setMode( _gl.LINES );} else if ( object.isLineLoop ) {renderer.setMode( _gl.LINE_LOOP );} else {renderer.setMode( _gl.LINE_STRIP );}} else if ( object.isPoints ) {renderer.setMode( _gl.POINTS );} else if ( object.isSprite ) {renderer.setMode( _gl.TRIANGLES );}if ( object.isInstancedMesh ) {renderer.renderInstances( drawStart, drawCount, object.count );} else if ( geometry.isInstancedBufferGeometry ) {const instanceCount = Math.min( geometry.instanceCount, geometry._maxInstanceCount );renderer.renderInstances( drawStart, drawCount, instanceCount );} else {renderer.render( drawStart, drawCount );}};

Three.js的渲染过程相关推荐

  1. 通俗的说下浏览器的渲染过程

    最初的模型: 浏览器下载 html 开始解析 html 遇见外链资源, 保存起来, 并且继续解析 html 解析结束 开始下载外链 下载结束 开始处理 css 处理 js 处理 处理完毕, 开始渲染 ...

  2. 彻底搞清楚浏览器渲染过程

    一.概述 在分析浏览器的渲染过程之前,我们先了解一下什么是进程和线程: (1)什么是进程? 进程是CPU进行资源分配的基本单位. (2)什么是线程? 线程是CPU调度的最小单位,是建立在进程的基础上运 ...

  3. 10分钟看懂浏览器的渲染过程及优化

    一.浏览器概述   目前的主流浏览器有5个:Internet Explorer.Firefox.Safari.Chrome和Opera浏览器.根据 StatCounter 浏览器统计数据,目前(截止2 ...

  4. 分析不同类型页面渲染过程

    现在让我们看看浏览器从网络上加载资源所耗费的时间(我们忽略从缓存以及从CDN等中间商网络上加载资源),我们首先要知道的是: 一个到无服务的网路往返 (传播延迟) 大约100ms 服务器对于HTML文档 ...

  5. [html] 如何优化页面的渲染过程?

    [html] 如何优化页面的渲染过程? 将样式表放到head中 将js脚本置底 减少脚本的数量,将多个脚本合并,可以使用webpack等前端工具打包 压缩样式.脚本.图片等的体积 个人简介 我是歌谣, ...

  6. 浏览器从输入URL到页面渲染过程 ——页面渲染流程

    之前我有总结过一篇经典面试题:浏览器从输入URL到页面渲染过程 ,接下里我将对某些知识点进行更细致的解析. 浏览器从输入URL到页面渲染过程 系列文章: (一):浏览器从输入URL到页面渲染过程 -- ...

  7. 浏览器从输入URL到页面渲染过程 —— 浏览器的进程与线程

    之前我有总结过一篇经典面试题:浏览器从输入URL到页面渲染过程,接下里我将对某些知识点进行更细致的解析. 浏览器从输入URL到页面渲染过程 系列文章: (二):浏览器从输入URL到页面渲染过程 --页 ...

  8. vuex中的值变化 页面重新渲染_浅谈浏览器的渲染过程,重绘与回流

    浏览器的渲染过程 首先,我们先来了解一下浏览器的渲染过程是什么样的,也就是说浏览器把一堆代码呈现到页面上的过程是什么样子的,浏览器采用流式布局模型(Flow Bsaed Layout),根据下图,我们 ...

  9. Vue.js 服务器端渲染指南

    默认情况下,可以在浏览器中输出 Vue 组件,进行生成 DOM 和操作 DOM.然而,也可以将同一个组件渲染为服务器端的 HTML 字符串,将它们直接发送到浏览器. 简单理解是将组件或页面通过服务器生 ...

最新文章

  1. Virtura box 构建一个简单局域网并联入外网
  2. python的继承模式_Python之继承--增加新功能,不更改以前的代码
  3. Office协同办公:SkyDrive
  4. redis 内存溢出_Redis为何这么快数据存储角度
  5. 每周一起读 | ACL 2019 NAACL 2019:文本关系抽取专题沙龙
  6. springboot整合shiro和session的详细过程和自定义登录拦截器
  7. (学习c++primer5th的重要)c++ primer5th类指针版本hasptr (网上源代码错误) 定义错误
  8. DC概论四之setup time 与 hold time 之三
  9. oracle dbms_crypto,DBMS_CRYPTO包对Oracle加密
  10. 读《大道至简》第三章 有感
  11. matlab外部调用lingo,讲讲官方的例子,MATLAB调用Lingo
  12. java图表标题_Java 创建Excel图表
  13. 步进电机工作原理 驱动 _28BYJ-48 以及程序实例下载
  14. win10计算机的数字小键盘,让Win10登录时默认开启数字小键盘
  15. debian系统离线安装iperf2
  16. FCM聚类算法(模糊C均值算法)
  17. 根据word标题结构转换为excel的方法
  18. IE主页被https://hao.360.cn/?a1004劫持,如何解决
  19. microchip-01 MPLAB IDE安装
  20. Hadoop_day01_大数据的概念及磁盘存储

热门文章

  1. Notepad++ 设置文字自动换行
  2. linux 系统上安装或自动更新安全补丁
  3. C语言复杂链表的复制
  4. Cost Component Group
  5. 结束键盘输入的快捷键linux,linux 命令使用
  6. 周末用了下Google Allo:有些弱,百度度秘比它强
  7. CAN通讯协议的数据帧、远程帧
  8. liferay5.2.1基础数据库和 MySql安装的心得
  9. Ubuntu16.04-64位操作系统上安装Cadence Conformal10.1教程
  10. CAD转PDF软件怎么选择