前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android OpenGL ES(六)-给相机添加滤镜

Android OpenGL ES(六)-给相机添加滤镜

作者头像
deep_sadness
发布2018-10-10 10:12:20
2.7K0
发布2018-10-10 10:12:20
举报
文章被收录于专栏:Flutter入门Flutter入门

上文中我们已经实现了将OpenGL和相机结合到一起,本文就在上文的基础上,添加滤镜。

整体流程理解


添加滤镜后的整体流程.png

上编文章,我们是直接绘制OES的纹理。这里,因为要添加滤镜的效果。所以我们需要将纹理进行处理。

离屏绘制

离屏绘制.png

先将OES纹理,绑定到FrameBuffer上。同时会在FrameBuffer上绑定一个新的textureId(这里命名为OffscreenTextureId)。然后调用绘制OES纹理的方法,数据就会传递到FBO上。而我们可以通过绑定在其上的OffscreenTextureId得到其数据。通常情况下,我们把绑定FrameBuffer和绘制这个新的OffscreenTextureId代表的纹理的过程,称为离屏绘制。

绑定和生成FrameBuffer的时机

创建FrameBuffer。因为RenderBuffer的存储大小要和当前的显示的宽和高相关。所以会在onSurfaceChanged生命周期方法时候调用。

代码语言:javascript
复制
    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //在这里监听到尺寸的改变。做出对应的变化
        prepareFramebuffer(width, height);
        //...
    }

    //生成frameBuffer的时机
    private void prepareFramebuffer(int width, int height) {
        int[] values = new int[1];
        //申请一个与FrameBuffer绑定的textureId
        GLES20.glGenTextures(1, values, 0);
        mOffscreenTextureId = values[0];
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mOffscreenTextureId);
         // Create texture storage.
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0,
                GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);

        // Set parameters.  We're probably using non-power-of-two dimensions, so
        // some values may not be available for use.
        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);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
                GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
                GLES20.GL_CLAMP_TO_EDGE);

        //创建FrameBuffer Object并且绑定它
        GLES20.glGenFramebuffers(1, values, 0);
        mFrameBuffer = values[0];
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer);

        // 创建RenderBuffer Object并且绑定它
        GLES20.glGenRenderbuffers(1, values, 0);
        mRenderBuffer = values[0];
        GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, mRenderBuffer);

        //为我们的RenderBuffer申请存储空间
        GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16, width, height);

        // 将renderBuffer挂载到frameBuffer的depth attachment 上。就上面申请了OffScreenId和FrameBuffer相关联
        GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, mRenderBuffer);
        // 将text2d挂载到frameBuffer的color attachment上
        GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mOffscreenTextureId, 0);

        // See if GLES is happy with all this.
        int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
        if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
            throw new RuntimeException("Framebuffer not complete, status=" + status);
        }
        // 先不使用FrameBuffer,将其切换掉。到开始绘制的时候,在绑定回来
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }
    
    //在onDrawFrame中添加代码
    @Override
    public void onDrawFrame(GL10 gl) {
        //...省略

        //重新切换到FrameBuffer上。
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffer);
        //这里的绘制,就会将数据挂载到FrameBuffer上了。
        mOesFilter.draw();
        //解除绑定,结束FrameBuffer部分的数据写入
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
        
        //....省略
    }
  • FrameBuffer 帧缓冲对象

openGL绘制流程.png

我们自己创建的FrameBuffer其实只是一个容器。所以我们要将数据挂载上去,它才算是完整。

FrameBuffer.png

所以,我们可以看到申请FrameBuffer需要进行下面的三步

  1. 生成一个FrameBuffer
  2. 申请一个RenderBuffer,并且挂载GL_DEPTH_ATTACHMENT上。

RenderBuffer也是一个渲染缓冲区对象。RenderBuffer对象是新引入的用于离屏渲染。它允许将场景直接渲染到Renderbuffer对象,而不是渲染到纹理对象。 Renderbuffer只是一个包含可渲染内部格式的单个映像的数据存储对象。它用于存储没有相应纹理格式的OpenGL逻辑缓冲区,如模板或深度缓冲区。

  1. 申请一个textureId,挂载到GL_COLOR_ATTACHMENT0上。
  2. 重新切换到FrameBuffer上(绑定),然后绘制。

我们就可以通过这个纹理,得到保存在FBO上的数据了

添加滤镜的绘制

添加滤镜.png

我们可以通过FBO,进行滤镜处理。我们将得到的数据,再次进行绘制,在这次的绘制中,我们就可以添加上我们想要的滤镜处理了。

但是这里不仅仅是要绘制到屏幕上,同时要在开启录制的时候,输入给Encoder进行视频的编码和封装。 所以我们需要将数据再写写入一个新的FrameBuffer中,然后再其输出的outputTexture中,就可以得到应用了纹理的数据了。

  • 滤镜处理 就算将上面的OffscreenTextureId作为这里滤镜的输入Id.
代码语言:javascript
复制
    @Override
    public void onDrawFrame(GL10 gl) {
         //...省略
        //经过路径处理
        mColorFilter.setTextureId(mOffscreenTextureId);
        mColorFilter.onDrawFrame();
        int outputTextureId = mColorFilter.getOutputTextureId();
        //...省略
    }

同时滤镜内,也按照上面的FrameBuffer的处理流程。将数据挂载到FrameBuffer上。得到挂载在FrameBuffer上的outputTextureId

代码语言:javascript
复制
代码同上,省略
将应用了滤镜的纹理分别绘制到GLViewEncoder当中

image.png

代码语言:javascript
复制
@Override
    public void onDrawFrame(GL10 gl) {
        //省略...
        //经过滤镜处理
        mColorFilter.setTextureId(mOffscreenTextureId);
        mColorFilter.onDrawFrame();
        int outputTextureId = mColorFilter.getOutputTextureId();

        //将得到的outputTextureId,输入encoder,进行编码
        mVideoEncoder.setTextureId(outputTextureId);

        mVideoEncoder.frameAvailable(mSurfaceTexture);

        //将得到的outputTextureId,再次Draw,因为没有FrameBuffer,所以这次Draw的数据,就直接到了Surface上了。
        mShowFilter.setTextureId(outputTextureId);
        mShowFilter.onDrawFrame();
    }
  1. 将得到的outputTextureId,输入EncoderInputSurface中,通知内部进行draw 和进行编码。
  2. 将得到的outputTextureId,再次Draw,因为没有FrameBuffer,所以这次draw的数据,就直接到了Surface上了。也就是直接绘制到了我们的GLSurfaceView上了。

小结

  1. 对比上一编文章的绘制流程。上文是直接将纹理绘制到了GLView上显示,而这里是将纹理绘制到绑定的FrameBuffer中,而且 绘制的结果不直接显示出来。所以可以形象的理解离屏绘制,就是将绘制的结果保存在与FrameBuffer绑定的一个新的textureIdOffscreenTextureId)中,不直接绘制到屏幕上。
  2. 把握好整体流程之后,这部分的处理也会变得简单起来。后面就可以如何添加更加炫酷的滤镜和玩法了。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.09.18 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 整体流程理解
    • 离屏绘制
      • 绑定和生成FrameBuffer的时机
      • 添加滤镜的绘制
      • 将应用了滤镜的纹理分别绘制到GLView和Encoder当中
  • 小结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档