WebGL渲染管线
渲染管线
- WebGL是个状态机,需要先提前设置所有状态,通过DrawCall命令GPU顺着渲染管线来调用设置好的所有状态,最终获得Framebuffer
- 设置所有状态的过程都是在CPU上跑,DrawCall命令下的渲染管线是GPU上跑
- 渲染管线存在可编程阶段,可配置阶段,不可配置阶段,可选阶段等
- GPU内存里面储存着顶点数据,贴图数据(都是shader里面的变量)
- 不同厂商实现的渲染管线部分不同

- 这篇文章的渲染管线:顶点数据->顶点着色器->图元装配(裁剪->背面剔除->透视除法->视口变换)->光栅化->片元着色器->测试和混合阶段(裁切测试->alpha测试->深度测试->模板测试->alpha混合->抖动处理)
顶点数据(Vertex Data)以及uniform,texture,define信息
- 缓冲对象来存储顶点数据(位置,纹理坐标,法线等),它会在GPU内存(通常被称为显存)中储存大量顶点数据。
- 使用这些缓冲对象的好处是我们可以一次性的发送一大批数据到显卡上,而不是每个顶点发送一次。
- 常见的处理attribut信息有uv,position,normal,indices,color
- 硬盘加载到 RAM 的过程是十分耗时的
- 当把数据加载到显存中后, RAM 中的数据就可以移除了。
- 显卡对于显存的访问速度更快,而且大多数显 卡对于 RAM 没有直接的访 问权利 。

顶点着色器(Vertex Shader)
- 如果存在非均匀缩放变换(Scaling)我们需要使用矩阵的逆的转置来变换法线。

- 着色器里涉及到的关键字有(gl_Position,gl_PointSize,gl_)
- 常见的图元包括:点(GL_POINTS)、线(GL_LINES)、线条(GL_LINE_STRIP)、三角面(GL_TRIANGLES)。
- 顶点的xyz靠近远平面(还没除w);
- 会遍历N次,N=需要绘制的顶点个数
曲面细分
几何着色器(Geometry Shader)
图元装配(Primitive Setup)
-
裁剪 (Culling And Clipping)

- 把坐标的x、y、z分量不全在(-1,1)区间内的图元(或者图元的部分)抛弃掉
- 线段的两个顶点一个位于视椎体内而另一个位于视椎体外,那么位于外部的顶点将被裁剪掉,而且在视椎体与线段的交界处产生新的顶点。
-
背面剔除 (Back-Face Culling)
- 光栅化在对多边形图元进行“方块化”之前,要给出多边形是front-facing还是back-facing
- 根据多边形顶点的环绕方向确定的是顺时针还是逆时针,默认逆时针为front-facing,可由glFrontFace(GL_CCW[CW])控制
- 点和直线只有正面
- 通过glCullFace()函数来配置剔除的是正面还是背面,参数为GL_FRONT、GL_BACK(默认值)和GL_FRONT_AND_BACK
- 通过glEnable(GL_CULL_FACE)函数来开启背面剔除的优化。
- 渲染半透明或不透明物体时,不能使用该技术
-
透视除法(Perspective Division)
- 执行透射除法后(除以w分量),我们可以得到标准设备空间,该空间一般也称作标准视体(Canonical View Volume,CVV)
- 透射除法是由硬件自动执行的
- 正交投影变换并没有改变W分量的值(W分量的值仍是1)

- 透视除法后标准化设备坐标(NDC)了,标准化设备坐标是一个x、y和z值在-1.0到1.0的一小段空间。

-
视口变换 (Viewport Transform)
- 视口变换同时也将原来z坐标缩放到[0,1]变成Depth值;可以指定深度值范围:glDepthRange(GLclampd n,GLclampd f)

光栅化
- 光栅化主要包括两个过程:三角形的设置和三角形的遍历。
- 三角形设置: 得到整个三角网格对像素的覆盖情况我们就必须计算每条边上的像素坐标。
- 三角形遍历: 覆盖了哪些片段的采样点,随后得到该图元所对应的片元

- 光栅化除了直接对多边形进行填充这种方式之外,还可以只构造边或只有点,这由glPolyMode(GL_FRONT[BACK,FRONT_AND_BACK],GL_FILL[LINE,POINT])控制。
- cull是off的情况下,三角形遍历正面与背面,back面的法线和front面是一致的,插值信息都是一样的
片段着色器
- 片段着色器是通过编程控制屏幕上显示颜色和它的深度值
- 这个过程颜色并非最终颜色
- cull是off的情况下,没进行修正的情况下,front与back展示的颜色是一致的
- 深度写入关闭的情况下,根据顶点式顺序绘制,只会显示最上层的颜色
测试混合阶段(Tests & Blending)(逐片元操作)
- 各种测试(下图中共5个),每步测试,不通过的片断将被丢弃从而不能进入后续操作,然后进行一些操作(如混合),最终通过所有处理的片断将被写入FrameBuffer用于最终屏幕显示
- 这些操作可以用glEnable/glDisable(GL_ALPHA/STENCIL/DEPTH_TEST)、glEnable/glDisable(GL_BLEND)等打开或关闭,对于RGBA, Depth, Stencil Buffer,可以用glColor/Depth/StencilMask(GLboolean/GLuint)进行控制是否可写。

-
裁切测试
- Scissor Test对用户指定的scissor rectangle进行测试
- 裁切测试可以避免当视口比屏幕窗口小时造成的渲染浪费问题。
- 裁剪测试,主要是剔除窗口区域之外的像素。
- 通过glEnable(GL_SCISSOR_TEST)来开启裁切测试,通过glScissor()来指定裁切区域。
-
模板测试
- Stencil Test可以根据Stencil或Depth Buffer Test结果分条件更新Stencil Buffer实现很多功能。
- 通过glEnable(GL_STENCIL_TEST);开启模板测试
- 我们可以利用模板测试来实现平面镜效果、平面阴影和物体轮廓等功能。

-
alpha测试
- 指的是将一个像素点的alpha值和一个固定值比较,如果比较的结果失败,像素将不会被写到显示输出中;
- alpha测试本身消耗较大,性能较低,必要的情况下才会使用alpha测试。
-
深度测试
- 抛弃掉位置靠后的像素值,因为这个位置的像素本身就是被更前面的像素覆盖的;
- 通过glEnable(GL_DEPTH_TEST)来开启深度测试
- 通过深度函数glDepthFunc()来设置深度比较运算符
- 渲染半透明物体时,需要开启深度测试而关闭深度写入功能。
- alpha为0的时候,还是会进行深度测试
-
Alpha混合
- Alpha混合可以根据片段的alpha值进行混合,用来产生半透明的效果。
- 通过glEnable(GL_BLEND)来开启混合的功能

- 通过glBlendFuncSeparate()、glBlendFunc()和glBlendEquation()来设置各种混合效果,常见的选项有GL_ZERO、GL_ONE、GL_SRC_ALPHA、GL_FUNC_ADD等。
-
抖动处理 (Dithering)
- 通过glEnable(GL_DITHER)来控制抖动输出功能,默认是开启的
- 抖动是一种针对对于可用颜色较少的系统,可以以牺牲分辨率为代价,通过颜色值的抖动来增加可用颜色数量的技术。
帧缓存/帧缓冲区(FBO FrameBufferObject)
- 帧缓存是颜色缓存、深度缓存、模板缓存、累积缓存的集合。
- 画布可以是纹理(Texture)或者是渲染缓冲区(RenderBuffer),而放置这些画布的位置被称为帧缓冲区的附着(Attachment)。
- 帧缓冲区并不是实际存储数据的地方,实际存储图像数据数据的对象就是纹理和渲染缓冲区。

- 在帧缓冲区中可以附着3种类型的附着,颜色附着(ColorAttachment)|深度附着(DepthAttachment)|模板附着(StencilAttachment)。这三种附着对应的存储区域也被称为颜色缓冲区(ColorBuffer)|深度缓冲区(DepthBuffer)|模板缓冲区(StencilBuffer)。
颜色缓冲区(比深度缓存精度更高)
- 16位:RGB565,红色5位,绿色6位,蓝色5位,不保存透明通道(之所以绿色多1位是因为人眼对绿色更加敏感);
- 24位:RGB888,红色8位,绿色8位,蓝色8位,不保存透明通道;
- 32位:RGBA8888,红色8位,绿色8位,蓝色8位,透明通道8位;
- 如果使用了多渲染目标(Multiple Render Targets)技术,那么颜色附着的数量可能会大于一。
深度缓冲区(深度缓存)
- 存储每个像素点的深度,用来决定是丢弃当前像素颜色还是保留,假设,新渲染的颜色在旧颜色的前面,那么就覆盖旧颜色,如果新颜色在旧颜色的后面(被遮挡了)则保留旧颜色。
模板缓冲区
- 用来控制颜色缓存某个位置的写入操作,常用来处理阴影。
参考资料