WebGL—实现使用FBO离屏渲染(亦同拷贝纹理)off-screen rendering的两种方式

1.离屏渲染使用场景:

1.游戏中的小地图;
2.画中画场景;
3.游戏中观战模式的多场景场合;
4.镜像场景——比如汽车游戏当中的倒车镜,采用的就是离屏渲染技术,在倒车镜上安装一个摄像机,把摄像机渲染的数据保存到FBO(Frame Buffer Object帧缓冲区),再从FBO提取生成纹理进行贴图到倒车镜;

2.与实时渲染的优缺点比较:

离屏渲染:
1.在变化的场景下,因为离屏渲染需要创建一个新的缓冲区,且需要多次切换上下文环境,所以代价很高;
2.在稳定的场景下,离屏渲染可以采用一张纹理进行渲染,所以性能较当前屏渲染有较大提升。
从上述对比可以看出,在稳定场景下使用离屏渲染的优势较大。
(此处优缺点对比来源于高德地图技术博客:离屏渲染在车载导航中的应用

3.明确概念:

3.1 帧缓冲FBO(Frame Buffer Object);

在OpenGL渲染管线中,几何数据和纹理最终都是以2d像素绘制到屏幕上。最后一步的渲染目标在OpenGL渲染管线中被称为帧缓存(Frame Buffer Object即FBO)帧缓存是颜色缓存、深度缓存、模板缓存、累积缓存的集合。 默认情况下, OpenGL使用的帧缓存是由窗体系统创建和管理的。
下图展示了FBO和renderbuffer object与texture object之间的关系。从图中我们可以看出:多个renderbuffer object和texture object可以通过挂接点挂接到FBO上。需要主要的是FBO并没有实际存储数据的地方,它只是一个数据的壳,它只有挂接点,说白了就是两个缓冲区指针的集合
在这里插入图片描述
3.2 渲染缓冲RBO(Renderbuffer Object)
就像纹理图像一样,渲染缓冲区对象是实际的缓冲区,例如字节,整数,像素或其他的数组。但是,不能直接读取renderbuffer对象。这给它带来了一个额外的优势,那就是OpenGL可以进行一些内存优化,从而使其性能优于纹理,以便在屏幕外渲染到帧缓冲区。

3.3 FrameBuffer,RenderBuffer,Texture区别
在这里插入图片描述
在这里插入图片描述
**Color Attachment:**存储的是纹理图片颜色值,实质上纹理图片颜色值属于颜色附着点的一种
**Depth Attachment:**指向的是深度缓冲区和颜色缓冲区
**Stencil Attachment:**指向的是模版缓冲区
**RenderBuffer Objects :**渲染缓冲区对象,无论是纹理、图片、颜色、深度缓冲区、模版缓冲区都存在这个对象
FrameBuffer 上的附着点其实相当于内存地址,它并没有存储实质的内容,只是三个附着点或三个内存地址在FrameBuffer Objects例如color Attachment ,它仅仅是附着在FrameBuffer身上;
差异对比源自这里

3.4 FrameBuffer,RenderBuffer使用场景
Renderbuffer对象可以在屏幕外的渲染项目中使用,效率更高,但是重要的是要意识到何时使用renderbuffer对象以及何时使用纹理。一般规则是,如果永远不需要从特定缓冲区中采样数据,明智的做法是为该特定缓冲区使用renderbuffer对象。如果需要从特定的缓冲区(例如颜色或深度值)中采样数据,则应使用纹理附件。
源自这里

4.实现离屏渲染(亦同拷贝纹理)的两种思路

4.1 获取纹理再拷贝——将FBO的纹理拷贝到一张纹理当中;
实现效果:
左上角即为将FBO中颜色缓冲区的内容复制到纹理,进行再渲染的效果;
在这里插入图片描述
关键思路: 利用webgl的gl.copyTexSubImage2D()的函数,将FBO中颜色缓冲区的内容复制到纹理;

4.2 FBO拷贝——在webgl里面直接建立一个FBO,把绘制的结果直接存到FBO当中,然后在主的FBO中使用之前创建的FBO,实现离线渲染;
实现效果:
在这里插入图片描述
白色立方体为主FBO,白色立方体的贴图为自己创建FBO的动态纹理贴图,把自定义FBO的纹理贴到立方体上,再在主FBO上进行绘制。
思路如下:
在这里插入图片描述
参考上图:对FBO的几个缓冲区逐个进行RBO的填充依附;
1.自定义FBO的创建—对立方体进行木制纹理贴图;
新建帧缓冲区,对其的颜色缓冲区进行木制纹理贴图的填充,用模板缓冲区的深度缓冲区进行帧缓冲区深度缓冲区的链接,将绘制完成的帧缓冲对象以纹理的形式存放,以供下次主FBO中使用。
注意最后要取消几个缓冲区的绑定,使其恢复状态机,使用原生FBO;

function createFOB(width,height) {
            //新建帧缓冲对象
            var obj         =   webgl.createFramebuffer();
            //对帧缓冲对象进行绑定
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, obj);
            //新建渲染缓冲区对象
            var depthObj    =   webgl.createRenderbuffer();
            //对渲染缓冲对象进行绑定
            webgl.bindRenderbuffer(webgl.RENDERBUFFER, depthObj);
            //创建并初始化渲染缓冲区对象的数据存储。
            webgl.renderbufferStorage(webgl.RENDERBUFFER, webgl.DEPTH_COMPONENT16, width, height);
            //渲染缓冲区对象作为帧缓冲区的深度缓冲区对象
            webgl.framebufferRenderbuffer(webgl.FRAMEBUFFER, webgl.DEPTH_ATTACHMENT, webgl.RENDERBUFFER, depthObj);
            //新建纹理对象
            textureDynamic = createDynamicTexture(width, height);
            //纹理对象作为帧缓冲区的颜色缓冲区对象
            webgl.framebufferTexture2D(webgl.FRAMEBUFFER, webgl.COLOR_ATTACHMENT0, webgl.TEXTURE_2D, textureDynamic, 0);
            //将绑定好的帧缓冲区,纹理,渲染缓冲区都取消绑定
            //恢复状态机,使用原生的FBO
            webgl.bindRenderbuffer(webgl.RENDERBUFFER, null);
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, null);
            webgl.bindTexture(webgl.TEXTURE_2D, null);
            return obj;
        }

2.使用自己创建的FBO,webgl.bindFramebuffer(webgl.FRAMEBUFFER, fbo);,接下来所有OpenGL绘制的数据将会保存至自己创建的FBO中;在此FBO中我主要进行“创建一个旋转立方体,对立方体进行木制纹理贴图”;

function renderToFBO() {
            //这里使用创建的FBO,接下来所有OpenGL绘制的数据将会保存至自己创建的FBO
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, fbo);
            webgl.viewport(0, 0, texWidth, texHeigh);

            //! 设置重绘背景的颜色
            webgl.clearColor(1.0, 1.0, 1.0, 1.0);
            //! 执行绘制,即将背景清空成制定的颜色(clearColor)
            webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
            webgl.enable(webgl.DEPTH_TEST);
            //! 指定绘制所使用的顶点数据 从 该缓冲区中获取
            webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);

            var mvp = mat4.create();
            var matTrans = mat4.create();
            var matRot = mat4.create();
            var matScale = mat4.create();
            var matModel = mat4.create();
            var matAll = mat4.create();

            mat4.identity(matTrans);
            mat4.identity(matRot);
            mat4.identity(matModel);
            mat4.identity(matScale);
            mat4.identity(matAll);

            webgl.activeTexture(webgl.TEXTURE0);
            //将给定的textureHandle绑定到目标,即0号纹理;此为木制纹理
            webgl.bindTexture(webgl.TEXTURE_2D, textureHandle);
            //给片元着色器传递纹理
            webgl.uniform1i(uniformTexture, 0);

            mat4.translate(matTrans, [varTransX, 0.0, varTransZ]);
            mat4.rotate(matRot, degToRad(varRot), [1.0, 1.0, 1.0]);

            mat4.multiply(matTrans, matRot, matModel);

            mat4.scale(matScale, [varScale, 1, 1], matScale);

            mat4.multiply(matModel, matScale, matAll);

            mat4.multiply(projectMat, matAll, mvp);

            webgl.uniformMatrix4fv(uniformProj, false, mvp);

            webgl.enableVertexAttribArray(v3PositionIndex);
            webgl.enableVertexAttribArray(attrColor);
            webgl.enableVertexAttribArray(attrUV);

            webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 4 * 9, 0);
            webgl.vertexAttribPointer(attrUV, 2, webgl.FLOAT, false, 4 * 9, 4 * 3);
            webgl.vertexAttribPointer(attrColor, 4, webgl.FLOAT, false, 4 * 9, 4 * 5);

            webgl.drawArrays(webgl.TRIANGLES, 0, 36);
        }

3.切换到OpenGL的主FBO(即原生FBO)中webgl.bindFramebuffer(webgl.FRAMEBUFFER, null);,用之前自己创建的FBO生成的纹理贴图textureDynamic进行白色立方体的贴图(总体概括为使用之前创建的FBO);

            //切换成OpenGL自己创建的FBO,即主FBO
            webgl.bindFramebuffer(webgl.FRAMEBUFFER, null);
            webgl.viewport(0, 0, texWidth, texHeigh);

            //! 设置重绘背景的颜色
            webgl.clearColor(0.0, 0.0, 0.0, 1.0);
            //! 执行绘制,即将背景清空成制定的颜色(clearColor)
            webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);
            webgl.enable(webgl.DEPTH_TEST);
            //! 指定绘制所使用的顶点数据 从 该缓冲区中获取
            webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);

            var mvp = mat4.create();
            var matTrans = mat4.create();
            var matRot = mat4.create();
            var matScale = mat4.create();
            var matModel = mat4.create();
            var matAll = mat4.create();

            varRot += 1;

            mat4.identity(matTrans);
            mat4.identity(matRot);
            mat4.identity(matModel);
            mat4.identity(matScale);
            mat4.identity(matAll);

            webgl.activeTexture(webgl.TEXTURE0);
            //textureDynamic 已经把FBO的内容绘制到这个纹理上了
            //把自定义FBO的纹理贴到立方体上,再在主FBO上进行绘制
            webgl.bindTexture(webgl.TEXTURE_2D, textureDynamic);
            webgl.uniform1i(uniformTexture, 0);


            mat4.translate(matTrans, [varTransX, 0.0, varTransZ]);
            mat4.rotate(matRot, degToRad(varRot), [1.0, 1.0, 1.0]);

            mat4.multiply(matTrans, matRot, matModel);

            mat4.scale(matScale, [varScale, 1, 1], matScale);

            mat4.multiply(matModel, matScale, matAll);

            mat4.multiply(projectMat, matAll, mvp);

            webgl.uniformMatrix4fv(uniformProj, false, mvp);

            webgl.enableVertexAttribArray(v3PositionIndex);
            webgl.enableVertexAttribArray(attrColor);
            webgl.enableVertexAttribArray(attrUV);

            webgl.vertexAttribPointer(v3PositionIndex, 3, webgl.FLOAT, false, 4 * 9, 0);
            webgl.vertexAttribPointer(attrUV, 2, webgl.FLOAT, false, 4 * 9, 4 * 3);
            webgl.vertexAttribPointer(attrColor, 4, webgl.FLOAT, false, 4 * 9, 4 * 5);

            webgl.drawArrays(webgl.TRIANGLES, 0, 36);

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值