WebGL离屏渲染
窗口系统帧缓冲区
通过前面的课程学习都知道,渲染管线生成的片元数据,像素值RGB会存入颜色缓冲区中,深度值Z会存储深度缓冲区中,深度缓冲区和颜色缓冲区都属于帧缓冲区。 帧缓存颜色缓冲的数据会被显示系统读取显示到canvas画布上,每一个canvas画布都有一个自己的帧缓存,或者说帧缓存的颜色缓冲区。系统默认的这个帧缓冲区称为窗口系统的帧缓冲区, WebGL的内容呈现在一个canvas画布上,而画布是浏览器窗口上的一个元素,这就相当于说每一个浏览器窗口上的WebGL应用对应一个帧缓存,自然就把这个默认的帧缓冲区称为窗口系统的帧缓冲区。 窗口系统帧缓冲区中的数据显示在窗口中,窗口也是显示在屏幕上的,所以也可以称为屏幕缓冲区或屏幕缓存,对于默认的帧缓冲区、窗口系统缓冲区、屏幕缓冲区、屏幕缓存,不同的教程使用的词汇略有差异,知道说的是什么就行。
自定义帧缓冲区
WebGL除了有一个与窗口或者说canvas画布关联的帧缓冲区,也支持通过WWebGL方法createFramebuffer()自定义创建帧缓冲区,自定义创建的帧缓冲区和系统默认的窗口系统帧缓冲区一样包含颜色缓冲区、深度缓冲区等子缓冲区。 一般情况下经过渲染管线处理后得到的数据默认存储到窗口系统缓冲区中,而不是工程师自定义创建的帧缓冲区,显示系统读取颜色缓冲区中RGB像素值数据也是默认读取窗口系统帧缓冲区中的颜色缓冲区。 那么自定义的帧缓冲区有什么用,比如实现光照阴影、动态模糊、景深效果。具体点说,工程师可以通过程序设置,把渲染管线生成的数据传入自定义的帧缓冲区中,而不是系统默认的窗口系统缓冲区中, 默认情况下自定义缓冲区中的像素数据就不会显示在浏览器窗口canvas画布上,但是可以通过WebGL API实现把帧缓冲区中的数据导入到纹理缓冲区中,可以作为其它几何体的纹理贴图,当然这只是其中一种情况。 渲染管线生成数据默认存入窗口系统帧缓存中,显示系统默认读取窗口缓冲区中的数据显示在屏幕上,这种渲染方式称为当前屏渲染;像上面自定义帧缓冲区而里面的像素数据又不会被显示系统自动读取直接显示在屏幕窗口上, 而是回流作为纹理贴图或者说二次处理,像这种方式完成的渲染方式就是离屏渲染,这也很好理解,自定义帧缓冲去中的颜色数据不直接显示在屏幕窗口上,换句话描述就是远离窗口的canvas画布,自然称为离屏渲染。 自定义的帧缓冲区中的像素数据不会直接被读取显示在屏幕上,所以可以把自定义的帧缓冲区称为离屏缓存,WebGL图形系统支持创建多个离屏缓存,每个离屏缓存都支持读取和写入数据。
渲染缓冲区
通过方法createRenderbuffer()方法可以创建一个渲染缓冲区,通过渲染缓冲区、帧缓冲区、纹理缓冲区配合可以实现离屏渲染。 自定义帧缓冲区的时候,它的颜色、深度、模板缓冲区也要自定义,通过方法renderbufferStorage()可以设置一个渲染缓冲区的用途, 或者说可以把一个渲染缓冲区定义为颜色缓冲区、深度缓冲区、模板缓冲区,也可以把纹理缓冲区作为帧缓冲区的的颜色缓冲区, 渲染管线生成的片元的像素数据就会存入纹理缓冲区。
纹理缓冲区
纹理缓冲区可以作为帧缓冲区的子缓冲区颜色缓冲区、深度缓冲区。
相关WebGL API简介
createFranebuffer()
学习createFranebuffer()方法可以参考过去学习过的createBuffer()方法、createTexture()方法,createBuffer()方法的作用是创建存储顶点数据缓冲区,createTexture()方法创建的是存储纹理图片的纹理缓冲区, 类比思考,顾名思义createFranebuffer()的作用是创建一个帧缓冲区。帧缓冲区可以创建也可以删除,删除帧缓冲区的方法是deleteFranebuffer(), 删除的时候把帧缓冲区的对象名作为deleteFranebuffer()方法的参数即可。
bindFramebuffer()
学习bindFrambuffer()方法可以参考过去学习过的bindBuffer()方法、bindTexture()方法,bindBuffer()方法的作用是绑定存储顶点数据缓冲区,bindTexture()方法绑定的是存储纹理图片的纹理缓冲区, 类比思考,顾名思义bindFrambuffer()的作用是是绑定帧缓冲区Frambuffer。缓冲区只有绑定后才能够传入相关的数据,对于同类缓冲区在同一个时间只能绑定激活一个,在绑定第二个同类缓冲区的时候,前面的处于绑定状态的同类缓冲区会自动解除绑定, 对于帧缓冲区也是如此,绑定哪一个帧缓冲区,经过渲染管线处理后生成的片元相关数据会存入被绑定的帧缓冲区,默认的情况下,所有的片元数据会默认存入WebGL系统当前窗口系统帧缓冲区中。 在进行离屏绘制渲染的时候需要通过bindFrambuffer()方法切换你要绑定的镇缓冲区对象,通过这样操作可以把每次执行drawArrays绘制命令时候渲染管线产生的片元数据导流到不同的帧缓冲区中。
格式:gl.bindFramebuffer(target, framebuffer),参数target是gl.FRAMEBUFFER,FRAMEBUFFER表示帧缓冲区,第二个参数framebuffer表示帧缓冲区对象变量名。
bindFramebuffer()可以实现帧缓冲区之间切换,同一时间只能绑定激活一个帧缓冲区,如果绑定了一个自定义帧缓冲区,执行绘制drawwArrays,会把片元相关数据存入该自定义帧缓冲区中, 然后执行bindFramebuffer(gl.FRAMEBUFFER,null)就可以释放当前自定义的帧缓冲区,系统会默认自动绑定激活当前窗口系统的帧缓冲区,如果执行绘制命令drawwArrays,片元像素数据就会存入屏幕缓存的颜色缓冲区中, 生成的像素值会被WebGL显示系统读取显示在浏览器窗口上的canvas画布里面。
framebufferTexture2D()
framebufferTexture2D作用是把纹理缓冲区与帧缓冲区关联起来,相当于把纹理缓冲区作为帧缓冲区的颜色缓冲区或深度缓冲区,作为颜色缓冲区可以存储渲染管线生成的片元像素数据, 渲染管线生成的像素集合就相当于一张普通图片,纹理缓冲区可以接受包含像素数据的普通PNG、jpg等格式图片,自然可以作为帧缓冲区的颜色缓冲区接收像素数据。说到这里至少应该可以提醒你,利用离线缓存, 也就是自定义的帧缓冲区可以实现把渲染结果作为其它顶点的纹理贴图使用,比如你渲染一个立方体作为另一个立方体的纹理贴图。
格式:gl.framebufferTexture2D(target,attachment,textarget,texture,level)
参数 | 含义 |
target | 参数target是gl.FRAMEBUFFER,FRAMEBUFFER表示帧缓冲区 |
attachment | 参数attachment是gl.COLOR_ATTACHMENT0表示纹理缓冲区作为帧缓冲区的颜色缓冲区,接收片元像素数据,如果是gl.DEPTH_ATTACHMENT表示纹理缓冲区作为帧缓冲区的深度缓冲区,接收片元深度值Z |
textarget | 和texImage2D()方法的第一个参数相同,是gl.TEXTURE_2D或gl.CUBE_MAP_TEXTURE,都表示纹理缓冲区,表示的类型不同,gl.TEXTURE_2D表示2D纹理,gl.CUBE_MAP_TEXTURE表示立方体纹理 |
texture | 要关联的纹理缓冲区对象的变量名 |
level | 0 |
createRenderbuffer()
createRenderbuffer()的作用是创建一个渲染缓冲区,可以作为帧缓冲区的子缓冲区,接收来自渲染管线的片元像素值、深度值等数据。删除渲染缓冲区的方法是deleteRenderbuffer(),使用方法和帧缓冲区的删除方法一样。
blindRenderbuffer()
blindFranebuffer()方法使用类似bindFrambuffer()方法,只不过一个针对帧缓冲区对象,一个针对渲染缓冲区对象。
renderbufferStorage()
renderbufferStorage()方法的作用是指定createRenderbuffer()方法创建的渲染缓冲区用途,具体说就是把渲染缓冲区作为帧缓冲区的颜色缓冲区、深度缓冲区或模板缓冲区, 渲染缓冲区的用途可以通过enderbufferStorage()方法的第二个参数指定,指定为颜色缓冲区就可以接收帧缓冲区的片元的像素数据,指定为深度缓冲区就可以接收片元的深度值Z数据。
格式:gl.renderbufferStorage(target, internalformat, width, height),target默认写法gl.RENDERBUFFER,RENDERBUFFER表示渲染缓冲区;width和height表示帧缓冲区渲染结果的宽高尺寸, 参数具体值使用整数,表示单位是像素;internalformat字面意思内部格式,也就是指定渲染缓冲区接收帧缓冲区的那种数据,是片元的像素值还是片元的深度值或其它的值。
internalformat | 含义 |
gl.DEPTH_COMPONENT16 | 深度缓冲区 |
gl.STENCIL_INDEX8 | 模板缓冲区 |
gl.RGBA4 | 颜色缓冲区,4个分量都是4比特 |
gl.RGB5_A1 | 颜色缓冲区,RGB分量5比特,A分量1比特 |
gl.RGB565 | 颜色缓冲区,RGB分量分别5、6、5比特 |
framebufferRenderbuffer()
framebufferRenderbuffer()方法的作用基本类似framebufferTexture2D()方法,区别是framebufferRenderbuffer()方法是把渲染缓冲区关联到帧缓冲区。
格式:gl.framebufferRenderbuffer(target,attachment,renderbuffertarget,renderbuffer)
参数 | 含义 |
target | 参数target是gl.FRAMEBUFFER,FRAMEBUFFER表示帧缓冲区 |
attachment | 1.gl.COLOR_ATTACHMENT0表示渲染缓冲区作为帧缓冲区的颜色缓冲区,接收片元像素数据 2.gl.DEPTH_ATTACHMENT表示渲染缓冲区作为帧缓冲区的深度缓冲区,接收片元深度值Z 3.gl.STENCIL_ATTACHMENT表示渲染缓冲区作为帧缓冲区的模板缓冲区 (注意:与renderbufferStorage()设定的渲染缓冲区用途一致) |
renderbuffertarget | gl.RENDERBUFFER |
renderbuffer | 渲染缓冲区对象的变量名 |
level | 0 |
概念区分
不同的教程在讲解WebGL API可以会使用不同而又相似的术语描述,可能会引起混乱,大家也不用深究,要结合具体的代码去理解,多数的工程师都是从事一个领域,不会去了解其他领域的词汇,软件中的术语往往来自计算机硬件, 但是随着时间的发展,可能形成了各自的概念术语系统,可能就会出现一些概念上的交叉、模糊、重复等问题,比如帧缓存对象和帧缓存可以说指的是同一个概念,也可以说不是,说到帧缓存你就会想到缓存,缓存你会联系到内存, 内存中的缓冲寄存器,JavaScript语言支持面向对象,执行createFramebuffer()方法返回了一个对象,只是这个方法具有创建帧缓存的功能,方法返回的对象自然可以称为帧缓存对象,上面的缓存也可以称为缓冲区,帧缓冲区, 帧缓冲区对象。除了行业隔离产生的概念问题,同样把一些英文图形学书籍教程翻译为中文后,不同的书对不同的概念也有不同的描述,但往往还是比较接近的, 所以说理解任何的概念都要以代码为准,不管以英文原版还是汉语翻译版为准都不如以代码为准,所有的术语都是对图形硬件系统和图形学算法的抽象描述,这些问题都会反映到代码中。
帧缓冲区也可以称为帧缓存,对于有独立显卡的PC,帧缓冲区是GPU的专属内存,位于独立显卡上,是显存的一部分;像手机等嵌入设备的帧缓冲区往往位于CPU和GPU的共享内存上。 一般开发的时候只要不涉及到底层性能优化的问题就不用考虑这些和硬件相关的概念。所有的帧缓冲区都是都是离散的内存块,工程师可以通过WebGL API创建,渲染管线生成的数据可以写入帧缓冲区中, 帧缓冲区包含一些用于不同功能的子缓冲区,存储对应的数据,也可以通过API读取帧缓冲中的数据。渲染管线上的深度测试单元可以读取深度缓冲区中的数据,模板测试单元可以读取模板缓冲区中的数据, 显示系统可以读取颜色缓冲区中的片元像素数据,只有颜色缓冲区中的像素数据会显示在浏览器窗口的canvas画布上。