WebGL性能优化初尝
Posted 听云
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WebGL性能优化初尝相关的知识,希望对你有一定的参考价值。
上次文章介绍了如何用WebGL快速创建一个自己的小世界,在我们入门WebGL之后,并且可以用原生WebGL写demo越来越复杂之后,大家可能会纠结一点:就是我使用webgl的姿势对不对。因为WebGL可以操控Shader加上超底层API,带来了一个现象就是同样一个东西,可以有多种的实现方式,而此时我们该如何选择呢?这篇文章将稍微深入一点webgl,给大家介绍一点WebGL的优化知识。
讲WebGL优化之前我们先简单回忆一下canvas2D的优化,常用的display list、动态区域重绘等等。用canvas2D多的同学应该对以上的优化或多或少都有了解,但是你对WebGL的优化了解么,如果不了解的话往下看就对了,这里会先从底层图像是如何渲染到屏幕上开始,逐步开始我们的WebGL优化。
GPU如何渲染出一个物体
先看一个简单的球的例子,下面是用WebGL画出来的一个球,加上了一点光的效果,代码很简单,这里就不展开说了。
一个球(https://vorshen.github.io/simple-3d-text-universe/doc/0.html)
这个球是一个简单的3D模型,也没有复杂的一些变化,所以例子中的球性能很好,看FPS值稳定在60。后面我们会尝试让它变得复杂起来,然后进行一些优化,不过这一节我们得先了解渲染的原理,知其根本才能知道优化的原理。
我们都知道WebGL与着色器是密不可分的关系,WebGL当中有顶点着色器和片段着色器,下面用一张图来简单说明下一个物体由0到1生成的过程。
0就是起点,对应图上面的3D mesh,在程序中这个就是3D顶点信息
1就是终点,对应图上面的Image Output,此时已经渲染到屏幕上了
我们重点是关注中间那三个阶段,第一个是一个标准的三角形,甚至三角形上面用三个圈指明了三个点,再加上vertex关键字,可以很明白的知道是顶点着色器处理的阶段,图翻译为大白话就是:
我们将顶点信息传给顶点着色器(drawElements/drawArray),然后着色器将顶点信息处理并开始画出三角形(gl_Position)
如何理解光栅化-知乎(https://www.zhihu.com/question/29163054)
后面阶段是上色,可以用textture或者color都可以,反正统一以rgba的形式赋给gl_FragColor
图中vertexShader会执行3次,而fragmentShader会执行35次(有35个方块)
发现fragmentShader执行次数远远超过vertexShader,此时机智的朋友们肯定就想到尽可能的将fragmentShader中的计算放在vertexShader中,但是能这样玩么?
强行去找还是能找到这样的场景的,比如说反射光。反射光的计算其实不是很复杂,但也稍微有一定的计算量,看核心代码
上面反射光代码就不细说了,核心就是内置的reflect方法。这段代码既可以放在fragmentShader中也可以放在vertexShader中,但是二者的结果有些不同,结果分别如下
放在vertexShader中(https://vorshen.github.io/simple-3d-text-universe/doc/-1.html)
放在fragmentShader中(https://vorshen.github.io/simple-3d-text-universe/doc/0.html)
所以说这里的优化是有缺陷的,可以看到vertexShader中执行光计算和fragmentShader中执行生成的结果区别还是蛮大的。换言之如果想要实现真实反射光的效果,必须在fragmentShader中去计算。开头就说了这篇文章的主题在同样的一个效果,用什么方式是最优的,所以continue~
GPU计算能力很猛
上一节说了GPU渲染的原理,种种现象都表明了GPU在单纯的计算能力上是超过普通的CPU,而我们关注一下前一节Shader里面的代码
vertexShader
fragmentShader
可以发现逻辑语句很少,更多的都是计算,特别是矩阵的运算,两个mat4相乘通过js需要写成这样(代码来自glMatrix)
可以说相比普通的加减乘除来说矩阵相关的计算量还是有点大的,而gpu对矩阵的计算有过专门的优化,是非常快的
所以我们第一反应肯定就是能在Shader中干的活就不要让js折腾啦,比如说前面代码中将proMatrix/viewMatrix/modelMatrix都放在shader中去计算。甚至将modelMatrix里面再区分成moveMatrix和rotateMatrix可以更好的去维护不是么。
但是了解threejs或者看其他学习资料的的同学肯定知道threejs会把这些计算放在js中去执行,这是为啥呢??比如下方代码(节选自webgl编程指南)
vertexShader中
这里居然把proMatrix/viewMatrix/modelMatrix全部在js中计算好,然后传入到shader中去,为什么要这样呢?
结合第一节我们看下vertexShader执行的次数是和顶点有关系的,而每个顶点都需要做对象坐标->世界坐标->眼睛坐标的变换,如果传入三个顶点,就代表GPU需要将proMatrix * viewMatrix * modelMatrix计算三次,而如果我们在js中就计算好,当作一个矩阵传给GPU,则是极好的。js中虽然计算起来相较GPU慢,但是胜在次数少啊。
看下面两个结果
在shader中计算(https://vorshen.github.io/simple-3d-text-universe/doc/2.html)
在js中计算(https://vorshen.github.io/simple-3d-text-universe/doc/1.html)
第一个是将矩阵都传入给GPU去计算的,我这边看到FPS维持在50左右
第二个是将部分矩阵计算在js中完成的,我这边看到FPS维持在60样的
这里用的180个球,如果球的数量更大,区别还可以更加明显。所以说GPU计算虽好,但不要滥用。
js与shader交互的成本
动画就是画一个静态场景然后擦掉接着画一个新的,重复不断。第一节中我们用的是setInterval去执行的,每一个tick中我们必须的操作就是更新shader中的attribute或者uniform,这些操作是很耗时的,因为是js和glsl程序去沟通,此时我们想一想,有没有什么可以优化的地方呢?
比如有一个场景,同样是一个球,这个球的材质颜色比较特殊
x,y方向上都有着渐变,不再是第一节上面一个色的了,此时我们该怎么办?
首先分析一下这个这个球
总而言之就是水平和垂直方向都有渐变,如果按之前的逻辑扩展,就意味着我们得有多个uniform去标识。
我们先尝试一下,用如下的代码,切换uniform的方式
使用切换uniform的方式(https://vorshen.github.io/simple-3d-text-universe/doc/4.html)
发现FPS在40左右,还是蛮卡的。然后我们考虑一下,卡顿在哪?
vertexShader和fragmentShader执行的次数可以说都是一样的,但是uniform4fv和drawElements每一次tick中执行了多次,就代表着js与shader耗费了较大的时间。那我们应该如何优化呢?
核心在避免多次改变uniform,比方说我们可以尝试用attribute去代替uniform
看下结果怎样
使用attribute的方式(https://vorshen.github.io/simple-3d-text-universe/doc/3.html)
瞬间FPS就上去了对不~所以说灵活变通很重要,不能一味的死板,尽可能的减少js与Shader的交互对性能的提高是大大有帮助的。
切换program的成本
上一节我们发现频繁切换切换uniform的开销比较大,有没有更大的呢?
当然有,那就是切换program,我们把之前的例子用切换program的方式试下,直接看下面的例子
点击前慎重,可能会引起浏览器崩溃
切换program(https://vorshen.github.io/simple-3d-text-universe/doc/5.html)
已经不需要关心FPS的了,可以直观的感觉到奇卡无比。切换program的成本应该是在WebGL中开销是非常大的了,所以一定要少切换program
这里说的是少切换program,而不是说不要切换program,从理论上来说可以单个program写完整个程序的呀,那什么时候又需要切换program呢?
program的作用是代替if else语句,相当于把if else抽出来单独一个program,所以就是如果一个shader里面的if else多到开销超过program的开销,此时我们就能选择用program啦。
当然这里的度有点难把握,需要开发者自己多尝试,结合实际情况进行选择。这里有一个关于选择program还是if else的讨论,感兴趣的同学可以看看
结语
我们这里从原理触发,尝试了WebGL的一些优化,如果你有什么建议和疑惑,欢迎留言讨论。
本文转载自AlloyTeam:http://www.alloyteam.com/2017/05/webgl-performance-optimizations-first-taste/
了解更多性能优化内容及听云产品请点击“阅读原文”申请试用
以上是关于WebGL性能优化初尝的主要内容,如果未能解决你的问题,请参考以下文章