Android OpenGLES2.0开发(十):FBO离屏渲染

人生是一场单程的旅行,即使有些遗憾我们也没有从头再来的机会,与其纠结无法改变的过去不如微笑着珍惜未来。

引言

之前的章节我们使用OpenGL ES绘制图形然后显示到屏幕上,貌似看着并没有什么问题。

如果我们现在不想显示到屏幕中,但还想利用OpenGL ES对图像的强大处理能力那又该如何是好?抑或我们上一节学习到的图片滤镜,真实的情况需要多个滤镜叠加使用,把所有的滤镜都放到一个着色器中似乎不是一种优秀的编程模式。

上面提到问题,接下来我们会给出答案。

什么是FBO

FBO(Frame Buffer Object)即帧缓冲区对象,实际上是一个可添加缓冲区的容器,可以为其添加纹理或渲染缓冲区对象(RBO)。

FBO可以让我们的渲染不渲染到屏幕上,而是渲染到离屏Buffer中。可以理解为将OpenGL ES绘制的图像内容输出到一块内存中,而我们可以对这块内存进行多次绘制。当然这块内存中的数据也可保存到本地SD卡中,也可重新绘制到屏幕上用于显示。

  • FBO是一个容器,自身不能用于渲染,需要与一些可渲染的缓冲区绑定在一起,像纹理或者渲染缓冲区。
  • 可以给他添加添加纹理(Texture )或渲染缓冲区对象(RBO)
  • 它仅且提供了 3 个附着(Attachment):颜色附着深度附着模板附着
    请添加图片描述

FBO优点

  1. 灵活性和控制性‌:FBO允许开发者将渲染结果输出到自定义的帧缓冲区,而不是传统的窗口系统提供的帧缓冲区。这使得开发者可以更灵活地控制渲染过程和结果,适用于需要特殊渲染效果的场景‌
  2. 性能优化‌:通过使用FBO,开发者可以避免不必要的渲染操作,例如在不需要渲染到屏幕的情况下,可以直接将渲染结果存储在内存中,从而提高渲染效率‌
  3. 独立于窗口系统‌:FBO完全受OpenGL控制,不依赖于窗口系统提供的帧缓存。这意味着它可以在没有窗口系统的情况下使用,适用于无窗口环境的渲染需求‌
  4. 多目标应用‌:FBO可以用于多种应用场景,包括但不限于实时渲染、图像处理、视频游戏开发等。其灵活性和高性能使其在需要高质量图形输出的应用中具有显著优势‌

FBO怎么用

我们对OpenGL ES的绘制流程很熟悉了,如何将渲染结果输出到FBO中其实很简单。

  1. 创建FBO缓冲区,在这期间需要绑定纹理和深度缓冲区(可选)
  2. 渲染到FBO,与之前的渲染没有本质的区别,只是在draw前绑定FBO缓冲区,绘制结束解绑FBO即可
  3. 释放FBO,将FBO缓冲区释放

1. 创建FBO

创建FBO基本步骤是:

  • 创建2D纹理(颜色缓冲区)
  • 创建帧缓冲区
  • 创建深度缓冲区
  • 将纹理和深度缓冲区附着到帧缓冲区上
/**
 * 创建帧缓冲区(FBO)
 *
 * @param width
 * @param height
 */
public void createFrameBuffers(int width, int height) {
   
   
    if (mFrameBuffer > 0) {
   
   
        destroyFrameBuffers();
    }
    // 1.创建一个纹理对象并绑定它,这将是颜色缓冲区。
    int[] values = new int[1];
    GLES20.glGenTextures(1, values, 0);
    GLESUtils.checkGlError("glGenTextures");
    mOffscreenTexture = values[0];
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mOffscreenTexture);
    GLESUtils.checkGlError("glBindTexture " + mOffscreenTexture);

    // 2.创建纹理存储对象
    GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height,
            0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);

    // 3.设置参数。我们可能正在使用二维的非幂函数,所以某些值可能无法使用。
    // 设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
    //设置放大过滤为使用纹理中坐标最接近的若干个颜色,通过加权平均算法得到需要绘制的像素颜色
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
    //设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
    //设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
    GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    GLESUtils.checkGlError("glTexParameter");

    // 4.创建帧缓冲区对象并将其绑定
    GLES20.glGenFramebuffers(1, values, 0);
    GLESUtils.checkGlError("glGenFramebuffers");
    mFrameBuffer = values[0];    // expected > 0
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer);
    GLESUtils.checkGlError("glBindFramebuffer " + mFrameBuffer);

    // 5.创建深度缓冲区并绑定它
    GLES20.glGenRenderbuffers(1, values, 0);
    GLESUtils.checkGlError("glGenRenderbuffers");
    mDepthBuffer = values[0];    // expected > 0
    GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, mDepthBuffer);
    GLESUtils.checkGlError("glBindRenderbuffer " + mDepthBuffer);

    // 为深度缓冲区分配存储空间。
    GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, width, height);
    GLESUtils.checkGlError("glRenderbufferStorage");

    // 6.将深度缓冲区和纹理(颜色缓冲区)附着到帧缓冲区对象
    GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT,
            GLES20.GL_RENDERBUFFER, mDepthBuffer);
    GLESUtils.checkGlError("glFramebufferRenderbuffer");
    GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
            GLES20.GL_TEXTURE_2D, mOffscreenTexture, 0);
    GLESUtils.checkGlError("glFramebufferTexture2D");

    // 检查是否一切正常
    int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
    if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
   
   
        throw new RuntimeException("Framebuffer not complete, status=" + status);
    }

    // 解绑纹理
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
    // 解绑Frame Buffer
    GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值