来实现Camera滤镜视频切换显示

发布时间:2025-06-24 18:23:56  作者:北方职教升学中心  阅读量:463


图片水印纹理的创建前几篇已经介绍过,这里着重介绍下帧缓冲FBO的纹理创建,帧缓冲FBO的创建跟一般的纹理创建区别不大:

  • 创建帧缓冲着色器程序,定位着色器的参数:
void GLFBOPostProcessing::createFBOProgram() {    screenProgram = screenShader->createProgram();    if (!screenProgram) {        LOGE("Could not create screenProgram shaderId.");        return;    }    //获取顶点着色器中的in的参数,及片段着色器的uniform    m_screen_vertexPos = (GLuint) glGetAttribLocation(screenProgram,                                                      "aPos");    m_screen_textureCoordLoc = (GLuint) glGetAttribLocation(screenProgram,                                                            "aTexCoords");    m_textureScreenLoc = (GLuint) glGetAttribLocation(screenProgram,                                                      "screenTexture");    screenShader->use();    screenShader->setInt("screenTexture", 0);}
  •  创建帧缓冲FBO纹理:
void GLFBOPostProcessing::creatFBOTexture() {    //1.首先要创建一个帧缓冲对象,并绑定它,这些都很直观    glGenFramebuffers(1, &framebuffer);    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);    //2.接下来我们需要创建一个纹理图像,我们将它作为一个颜色附件附加到帧缓冲上。

        抗锯齿(Anti-aliasing):

         帧缓冲可以用于实现抗锯齿效果,尤其是多采样抗锯齿(MSAA):

  • 多采样纹理:将场景渲染到多采样纹理,然后解析到普通纹理。流场可视化等:

    • 体渲染:将体数据渲染到纹理,然后进行混合和光照计算。纹理数据的绑定跟YUV、

            在个人的github的AndroidLearnOpenGL练习集中,GLFBOPostProcessing.cpp实现了运用帧缓冲的后期处理(Post-processing)滤镜效果。

三、

        粒子系统和特效:

                帧缓冲可以用于实现复杂的粒子系统和特效:

  • 粒子系统:将粒子渲染到纹理,然后对纹理进行混合或模糊。
  • 本篇6:实现使用OpenGL的帧缓存FBO技术,将多个渲染结果合成到最终的图像中,在高级OpenGL的本博主练习集中LearnOpenGL之高级OpenGL(1)有关OpenGL帧缓冲FBO的详细介绍实现。GL C++显示相机YUV视频数据使用帧缓冲FBO后期处理,实现滤镜功能的代码实现

            接下的代码来源于系列文章中5中的GLDrawTextVideoRender.cpp的代码改造,并结合练习集AndroidLearnOpenGL中的GLFBOPostProcessing.cpp,实现的帧缓冲FBO后期处理代码合成的。这在以下场景中非常有用:

    • 反射和折射:将场景渲染到纹理,然后将纹理应用到反射或折射表面(例如镜子、
    • 本系列文章中4.  OpenGL Texture C++ Camera Filter滤镜视频录制; 基于文章1/2/3的代码,结合Google开源项目grafika中的WindowSurface.java/Coregl.java/TextureMovieEncoder2.java/VideoEncoderCore.java创建出录制视频的surface并根据此切换GLContext上下文交换(swapBuffer)渲染与显示的视频数据,最终在VideoEncoderCore中通过MediaCodec获取出encodeData写入MediaMuxer成为MP4文件格式的视频。

              创建着色器程序、帧缓冲对象的简介:

              帧缓冲对象(Framebuffer Object, FBO)是 OpenGL 中用于管理渲染目标的核心工具。更新、

            

     四、 glDisable(GL_DEPTH_TEST); // 清除所有相关缓冲区 // 将透明颜色设置为白色(实际上并没有必要,因为我们无论如何都看不到四边形后面) glClearColor(1.0f, 1.0f, 1.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); screenShader->use(); useFBOProgram(); //使用颜色附着纹理作为四边形平面的纹理 glBindTexture(GL_TEXTURE_2D, fboTexture); glDrawArrays(GL_TRIANGLES, 0, 6); checkGlError("glDrawArrays"); //切换到m_WindowSurface m_WindowSurface->makeCurrent(); m_WindowSurface->swapBuffers();}

        此处的帧缓冲FBO的顶点数据、

        阴影映射(Shadow Mapping):

                帧缓冲用于生成深度贴图,从而实现动态阴影效果:

  • 阴影映射:从光源视角渲染场景,将深度值存储到帧缓冲的深度附件。水印图片的绑定一致:

    void GLFBOPostProcessing::useFBOProgram() {    glUseProgram(screenProgram);    //绑定顶点数据及纹理数据    glVertexAttribPointer(m_screen_vertexPos, 3,                          GL_FLOAT, GL_FALSE, 0,                          PostProcessingQuadVertices1);    glEnableVertexAttribArray(m_screen_vertexPos);    glUniform1i(m_textureScreenLoc, 3);    glVertexAttribPointer(m_screen_textureCoordLoc, 3,                          GL_FLOAT, GL_FALSE, 0,                          PostProcessingQuadTextCoord);    glEnableVertexAttribArray(m_screen_textureCoordLoc);}

            C++类全代码:

            以上的全部代码都提交在:   WyFFmpeg/glplay at main · wangyongyao1989/WyFFmpeg · GitHub

            GLFBOPostProcessing.cpp的代码:

    //  Author : wangyongyao https://github.com/wangyongyao1989// Created by MMM on 2025/1/23.//#include <android/native_window.h>#include <android/asset_manager.h>#include <GLES3/gl3.h>#include "GLFBOPostProcessing.h"bool GLFBOPostProcessing::setupGraphics(int w, int h) {    screenW = w;    screenH = h;    LOGI("setupGraphics(%d, %d)", w, h);    glViewport(0, 0, w, h);    checkGlError("glViewport");    LOGI("glViewport successed!");    //清屏    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);    checkGlError("glClear");    //开启深度测试    glEnable(GL_DEPTH_TEST);    //创建YUV程序及YUV纹理    useYUVProgram();    createYUVTextures();    //创建图片程序及图片纹理    createPicProgram();    creatPicTexture();    //创建FBO程序及FBO纹理    createFBOProgram();    creatFBOTexture();    return true;}void GLFBOPostProcessing::renderFrame() {    if (m_filter != m_prevFilter) {        m_prevFilter = m_filter;        //切换后期处理的着色器,实现滤镜切换。

        VR渲染:

                在虚拟现实(VR)中,帧缓冲用于分别渲染左右眼的视图:

  • 立体渲染:分别为左右眼生成不同的视图。
  • 环境贴图:生成动态的环境贴图(例如立方体贴图),用于实现动态反射或天空盒。 // 我们将纹理的维度设置为窗口的宽度和高度,并且不初始化它的数据 glGenTextures(1, &fboTexture); glBindTexture(GL_TEXTURE_2D, fboTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screenW, screenH, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture, 0); //3.创建渲染缓冲对象 glGenRenderbuffers(1, &rbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, screenW, screenH); //4.将渲染缓冲对象附加到帧缓冲的深度和模板附件上 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); //5.最后,我们希望检查帧缓冲是否是完整的,如果不是,我们将打印错误信息 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { LOGE("ERROR::FRAMEBUFFER:: Framebuffer is not complete!"); } glBindFramebuffer(GL_FRAMEBUFFER, 0);}

            顶点数据及纹理数据的绑定并绘制:

    void GLFBOPostProcessing::renderFrame() {    if (m_filter != m_prevFilter) {        m_prevFilter = m_filter;        //切换后期处理的着色器,实现滤镜切换。动态天空盒)。

        科学可视化:

                帧缓冲可以用于科学数据的可视化,例如体渲染、屏幕空间环境光遮蔽(SSAO)。

        动态纹理生成:

        帧缓冲可以用于动态生成纹理,例如程序化纹理或动态更新的纹理:

  • 程序化纹理:通过渲染生成程序化纹理(例如噪声纹理、

        后期处理(Post-processing):

            帧缓冲是实现后期处理的核心工具。通过将场景渲染到纹理,然后对纹理进行处理,可以实现各种视觉效果:

  •  模糊效果(Blur):使用高斯模糊对渲染结果进行处理,实现景深或运动模糊效果。

        多目标渲染(Mulitpe Render Targets,MRT):

                帧缓冲可以附加多个颜色附件,用于多目标渲染(MRT)。现在重点的内容在帧缓冲FBO后期处理上,代码实现如下:

        Java代码层:

package com.wangyongyao.glplay.view;import android.content.Context;import android.graphics.Point;import android.opengl.GLSurfaceView;import android.util.AttributeSet;import android.util.Log;import android.util.Size;import android.view.GestureDetector;import android.view.MotionEvent;import android.view.ScaleGestureDetector;import android.view.Surface;import android.view.SurfaceHolder;import android.view.SurfaceView;import androidx.annotation.NonNull;import com.wangyongyao.glplay.OpenGLPlayCallJni;import com.wangyongyao.glplay.camera.Camera2Helper2;import com.wangyongyao.glplay.camera.GLCamera2Listener;import com.wangyongyao.glplay.utils.OpenGLPlayFileUtils;/** * author : wangyongyao https://github.com/wangyongyao1989 * Create Time : 2025/1/16 * Descibe : AndroidLearnOpenGL com.wangyongyao.views */public class GLFBOPostProcessingView extends SurfaceView implements SurfaceHolder.Callback, GLCamera2Listener {    private static String TAG = GLFBOPostProcessingView.class.getSimpleName();    private OpenGLPlayCallJni mJniCall;    private Context mContext;    private SurfaceHolder mHolder;    private Surface mSurface;    private Camera2Helper2 camera2Helper;    public GLFBOPostProcessingView(Context context, OpenGLPlayCallJni jniCall) {        super(context);        mContext = context;        mJniCall = jniCall;        init();    }    public GLFBOPostProcessingView(Context context, AttributeSet attrs) {        super(context, attrs);        mContext = context;        init();    }    private void init() {        //获取SurfaceHolder对象        mHolder = getHolder();        //注册SurfaceHolder的回调方法        mHolder.addCallback(this);        String vertexScreenPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "fbo_screen_vertex.glsl");        String fragScreenPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "fbo_screen_fragment.glsl");        String fragOppositionPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "fbo_post_opposition_fragment.glsl");        String fragGrayScalePath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "fbo_post_gray_scale_fragment.glsl");        String fragWeightedGrayPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "fbo_post_weighted_gray_fragment.glsl");        String fragNuclearEffectPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "fbo_post_nuclear_effect_fragment.glsl");        String fYUVPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "fbo_video_play_frament.glsl");        String vYUVPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "fbo_video_play_vert.glsl");        String picPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "yao.jpg");        String picVertexPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "draw_text_video_play_vert.glsl");        String picFragPath = OpenGLPlayFileUtils.getModelFilePath(mContext                , "draw_pic_frament.glsl");        if (mJniCall != null) {            mJniCall.setFBOPostProcessingGLSLPath(                     fragScreenPath, vertexScreenPath                    , fragOppositionPath                    , fragGrayScalePath                    , fragWeightedGrayPath                    , fragNuclearEffectPath                    , vYUVPath                    , fYUVPath                    , picPath                    , picVertexPath                    , picFragPath            );        }    }    public void setFBOPostProcessingType(int type) {        int typeVaule = type % 5;        if (mJniCall != null) {            mJniCall.glFBOPostProcessingSetParameters(typeVaule);        }    }    public int getFBOPostProcessingType() {        int type = 0;        if (mJniCall != null) {            type = mJniCall.glFBOPostProcessingGetParameters();        }        return type;    }    @Override    public void surfaceCreated(@NonNull SurfaceHolder holder) {        Log.e(TAG, "surfaceCreated");        mSurface = holder.getSurface();        if (mJniCall != null) {            mJniCall.glPSSurfaceCreated(mSurface, null);        }    }    @Override    public void surfaceChanged(@NonNull SurfaceHolder holder,         int format, int width, int height) {        Log.e(TAG, "onSurfaceChanged width:" + width + ",height" + height                + "===surface:" + mSurface.toString());        if (mJniCall != null) {            mJniCall.initFBOPostProcessing(width, height);        }        startCameraPreview(width, height);    }    @Override    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {        if (mJniCall != null) {//            mJniCall.glBOPostProcessingDestroy();}    }    @Override    public void onCameraOpened(Size previewSize, int displayOrientation) {    }    @Override    public void onPreviewFrame(byte[] yuvData, int width, int height) {        if (mJniCall != null) {            mJniCall.glFboPostProcessingSurfaceDraw(yuvData, width, height, 90);            mJniCall.glFBOPostProcessingRenderFrame();        }    }    @Override    public void onCameraClosed() {    }    @Override    public void onCameraError(Exception e) {    }    private void startCameraPreview(int width, int height) {        if (camera2Helper == null) {            camera2Helper = new Camera2Helper2.Builder()                    .cameraListener(this)                    .specificCameraId(Camera2Helper2.CAMERA_ID_BACK)                    .context(mContext)                    .previewViewSize(new Point(width, height))                    .rotation(90)                    .build();        }        camera2Helper.start();    }}

         native代码层:

        把GLSL着色器程序、

        以下是帧缓冲的主要作用和应用场景:

      离屏渲染(Off-screen Rendering):

        帧缓冲允许你将场景渲染到一个离屏的缓冲区(例如纹理),而不是直接渲染到屏幕。 // 我们将纹理的维度设置为窗口的宽度和高度,并且不初始化它的数据 glGenTextures(1, &fboTexture); glBindTexture(GL_TEXTURE_2D, fboTexture); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, screenW, screenH, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTexture, 0); //3.创建渲染缓冲对象 glGenRenderbuffers(1, &rbo); glBindRenderbuffer(GL_RENDERBUFFER, rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, screenW, screenH); //4.将渲染缓冲对象附加到帧缓冲的深度和模板附件上 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); //5.最后,我们希望检查帧缓冲是否是完整的,如果不是,我们将打印错误信息 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { LOGE("ERROR::FRAMEBUFFER:: Framebuffer is not complete!"); } glBindFramebuffer(GL_FRAMEBUFFER, 0);}

  

五、
  • 流场可视化:将流场数据渲染到纹理,然后生成箭头或流线。
  • G-Buffer:生成几何缓冲区(G-Buffer),用于存储场景的几何和材质信息。角色、

  •  颜色校正:调整亮度、图片水印的功能已经在系列文章5中实现了。绑定纹理;顶点数据及纹理数据的绑定;最后是绘制。功能实现的前置知识储备:

    • 本系列文章中1.  OpenGL Texture C++ 预览Camera视频;基于GLSurfaceView创建OpenGL运行环境为基础,把Camera的YUV数据传入OpenGL的片段着色器所创建的三个纹理(Texture)中对视频图像的显示;
    • 本系列文章中2.  OpenGL Texture C++ Camera Filter滤镜;在文章1的基础上对render过程进行优化,传入不同滤镜类型的片段着色器程序。

      Github地址:

                      https://github.com/wangyongyao1989/AndroidLearnOpenGL 

                      WyFFmpeg/glplay at main · wangyongyao1989/WyFFmpeg · GitHub

       参考资料:

              中文版LearnOpenGL

              

  •  色调映射(Tone Mapping):将 HDR 渲染结果映射到 LDR 显示范围。饱和度等。

  • 动态贴图:实时更新纹理内容(例如动态水面、
  • 本系列文章中3.  OpenGL 自定义SurfaceView Texture C++预览Camera视频;在文章1的基础上,用自定义的类GLSurfaceView方式创建一个GLThread的OpenGL运行环境,把Camera的YUV数据传入OpenGL的片段着色器所创建的三个纹理(Texture)中对视频图像的显示。
  • 软阴影:通过模糊深度贴图实现软阴影效果。创建;创建、纹理:
    bool GLFBOPostProcessing::setupGraphics(int w, int h) {    screenW = w;    screenH = h;    LOGI("setupGraphics(%d, %d)", w, h);    glViewport(0, 0, w, h);    checkGlError("glViewport");    LOGI("glViewport successed!");    //清屏    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);    checkGlError("glClear");    //开启深度测试    glEnable(GL_DEPTH_TEST);    //创建YUV程序及YUV纹理    useYUVProgram();    createYUVTextures();    //创建图片程序及图片纹理    createPicProgram();    creatPicTexture();    //创建FBO程序及FBO纹理    createFBOProgram();    creatFBOTexture();    return true;}

            YUV视频显示纹理、

  •         帧缓冲的核心组件:

                    帧缓冲由以下组件组成:

    • 颜色附件:用于存储颜色信息(通常是纹理)。它允许你将渲染结果输出到纹理、
    • 本系列文章中5:OpenGL C++视频中添加图片及文字水印播放并录制;基于文章1/2/3/4的代码,视频图片水印以另一个着色器程序的形式加入图片纹理,纹理相关的知识可参考本博主的另一篇文章:LearnOpenGL之入门基础-CSDN博客的知识储备;而视频水印文字在前面的基础上再加上另外一个着色器程序的形式加入文字渲染,OpenGL文字渲染可参考本博主的一篇文章:LearnOpenGL之文字渲染_opengl文字渲染接口-CSDN博客。