WebGL 2.0-FrameBuffer Objects And RenderBuffer Objects 实现渲染到纹理,离屏渲染

                            WebGL 2.0 FrameBuffer And RenderBuffer

        在学习WebGL过程中,有一种技术叫渲染到纹理,就是把每一帧画面再次处理渲染,并最为纹理,它能做好多高级效果的实现,如边缘检测,高斯模糊,SSAO等。渲染到纹理技术中最重要的就是FBO和RBO,接下来,我们来看看什么是FBO和RBO。

   FBO就是由颜色附件(COLOR_ATTACHMENT0),深度附件(DEPTH_ATTACHMENT),模板附件(STENCIL_ATTACHMENT)组成的一个逻辑存储对象。那RBO是什么呢,RBO是一个2D图像缓冲区,可以用于分配和存储颜色值,深度或者模板值,可以作为FBO的颜色,深度模板附件。

        在这里可能会困惑,已经有了帧缓冲FBO作为存储对象,那还需要渲染缓冲RBO做什么呀,我当时也困惑。其实,实际的图像数据是保存在渲染缓冲中的,帧缓冲只是管理相关对象的而已,它本身并不是一个物理上的缓冲区。帧缓冲在创建时也有自带的一些必要相关对象,但是我们无法对其修改参数,所以需要创建自己帧缓冲相关对象。

        好,接下来我们就要讨论下帧缓冲的相关对象

        颜色附件:我们一般用纹理图实现,就是我们把每一帧的画面的色彩信息保存下来,作为帧缓冲的颜色附件。这里可能理解起来有些别扭,其实它的意思是纹理图作为帧缓冲的颜色附件,那么这个纹理图中就会存有帧画面的色彩信息。

        深度附件:这个可以纹理图实现,也可以用渲染缓冲对象实现。

        深度附件:使用渲染缓冲对象进行模板测试。

        那如果不理解的话,我们可以看一下opengl es 3.0 programing guide中英文原版的陈述:

        A renderbuffer object is a 2D image buffer allocated by the application.The renderbuffer can be used to allocate and store color, depth, or stencil values and can be used as a color, depth, or stencil attachment in a framebuffer object. A renderbuffer is similar to an off-screen window system–provided drawable surface, such as a pbuffer. A renderbuffer,however, cannot be directly used as a GL texture.
        A framebuffer object (FBO) is a collection of color, depth, and stencil textures or render targets. Various 2D images can be attached to the color attachment point in the framebuffer object. These include a renderbuffer object that stores color values, a mip level of a 2D texture or a cubemap face, a layer of a 2D array textures, or even a mip level of a 2D slice in a 3D texture. Similarly,various 2D images containing depth values can be attached to the depth attachment point of an FBO. These can include a renderbuffer, a mip level of a 2D texture, or a cubemap face that stores depth values. The only 2D image that can be attached to the stencil attachment point of an FBO is a renderbuffer object that stores stencil values

        那我们如果需要WebGL渲染输出的图像作为纹理使用,那么就需要将纹理对象最为帧缓冲的颜色附件,此时们仍需要隐藏面消除,所以我们将渲染缓冲对象作为帧缓冲的深度附件。 结构大体如下图的样子。

     接下来看下基于上述知识实现的Demo,整体是这样的,程序里面画了两幅场景,一幅画面是一个旋转的地球,另外一幅是一个静态的三角形,我们将第一幅的画面保存到帧缓存中,并作为纹理贴到三角行上,下面是案例截图:

       Demo中我们可以看到中间就是绘制了一个三角形,然后中间的纹理贴图是一个动态球体旋转的画面,这就是把每一帧的画面作为纹理贴上去,也就是渲染到纹理。接下来,我们来看下主要的代码

        这部分是初始化FBO和RBO:

function intFBRuffers(gl) {
    //创建帧缓冲并绑定
    frameBuffer=gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER,frameBuffer);

    //创建渲染缓冲并绑定以及初始化存储
    renderBuffer=gl.createRenderbuffer();
    gl.bindRenderbuffer(gl.RENDERBUFFER,renderBuffer);
    gl.renderbufferStorage(gl.RENDERBUFFER,gl.DEPTH_COMPONENT16,gl_width,gl_height);

    //创建纹理
    frTex=gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D,frTex);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
    //设置纹理格式,作为帧缓冲的颜色附件
    gl.texImage2D(gl.TEXTURE_2D,0,gl.RGBA,gl_width,gl_height,0,gl.RGBA,gl.UNSIGNED_BYTE,null);

    //设置上面创建纹理作为颜色附件
    gl.framebufferTexture2D(gl.FRAMEBUFFER,gl.COLOR_ATTACHMENT0,gl.TEXTURE_2D,frTex,0);
    //设置渲染缓冲对象作为深度附件
    gl.framebufferRenderbuffer(gl.FRAMEBUFFER,gl.DEPTH_ATTACHMENT,gl.RENDERBUFFER,renderBuffer)
}

    然后是进行纹理球体旋转画面的绘制,并绑定到帧缓冲:

function generrateTextImage(gl){

//检测球体是否创建完成
if(!ball){ return; }
//设置渲染视口
gl.viewport(0, 0, gl_width, gl_height);
//设置背景色
gl.clearColor(0.8,0.8,0.8,1.0);
//绑定自定义帧缓冲
gl.bindFramebuffer(gl.FRAMEBUFFER,frameBuffer);
//设置摄像机
ms.setCamera(0,0,35,0,0,-1,0,1,0);
//设置投影参数
ms.setProjectFrustum(-1.5,1.5,-1,1,3,200);
//清楚颜色缓冲以及深度缓冲
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//保护现场
ms.pushMatrix();
//绕Y轴旋转
ms.rotate(currentAngle,0,1,0);
//绕Y轴旋转90度
ms.rotate(90,1,0,0);
//绘制球体
ball.drawSelf(ms,earthTex);
//恢复现场
ms.popMatrix();
//修改旋转角度
currentAngle=currentAngle+0.01;
}

        然后绘制三角形的方法。

function drawShadowTexture(gl) {

//检测三角形对象是否创建完成
if(!tr){return;}
//设置视口
gl.viewport(0, 0, gl_width, gl_height);
//设置屏幕背景色RGBA
gl.clearColor(0.0,0.0,0.0,1.0);
//初始化变换矩阵
ms.setInitStack();
//设置摄像机
ms.setCamera(0,0,2,0,0,0,0,1,0);
//设置投影参数
ms.setProjectFrustum(-1.5,1.5,-1,1,1,100);
//清楚帧缓冲区
gl.bindFramebuffer(gl.FRAMEBUFFER,null);
//清除着色缓冲与深度缓冲
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
//保护现场
ms.pushMatrix();
//执行缩放
ms.scale(0.3,0.3,0.3);
//绘制物体
tr.drawSelf(ms,frTex);
//恢复现场
ms.popMatrix();
}

  这是二次绘制的一个简单Demo,在绘制三角形的方法中,最重要的是

gl.bindFramebuffer(gl.FRAMEBUFFER,null);

  这句话其实清楚帧缓冲区的颜色附件和深度附件,就像我们每次都要清楚颜色缓冲和深度缓冲一样。当然如果想了解实现离屏渲染的流程,我们可以参考<WebGL 编程指南>

   最后拉,当然要放个Demo:    点击打开链接

猜你喜欢

转载自blog.csdn.net/weixin_37683659/article/details/80411420
今日推荐