来实现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);}
五、
颜色校正:调整亮度、图片水印的功能已经在系列文章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