Android OpenGL+Camera2渲染(2) —— OpenGL实现Camera2图像预览

Android OpenGL+Camera2渲染(1) —— OpenGL简单介绍

Android OpenGL+Camera2渲染(2) —— OpenGL实现Camera2图像预览

Android OpenGL+Camera2渲染(3) —— 大眼,贴纸功能实现

Android OpenGL+Camera2渲染(4) —— 美颜功能实现

Android OpenGL+Camera2渲染(5) —— 录制视频,实现快录慢录

 

使用GlRenderView(继承自GLSurfaceView)来进行预览。

构造方法中配置EGL版本和渲染模式,通过setRenderer设置GlSurfaceView中渲染监听 GLSurfaceView.Renderer。

public GlRenderView(Context context, AttributeSet attrs) {
    super(context, attrs);

    //设置EGL 版本
    setEGLContextClientVersion(2);

    glRender = new GlRenderWrapper(this);
    setRenderer(glRender);
    //手动渲染模式
    setRenderMode(RENDERMODE_WHEN_DIRTY);

}

GLSurfaceView.Renderer

public interface Renderer {
           
        //surface 创建监听
        void onSurfaceCreated(GL10 gl, EGLConfig config);

        //surface改变监听
        void onSurfaceChanged(GL10 gl, int width, int height);

         //负责纹理绘制
        void onDrawFrame(GL10 gl);
    }

我们配置的是手动渲染模式,这里如果想执行onDrawFrame方法,需要每来一帧图像,调用 requestRender()。

 

@Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        
        camera2Helper = new Camera2Helper((Activity) glRenderView.getContext());

        mTextures = new int[1];
        //创建一个纹理
        GLES20.glGenTextures(mTextures.length, mTextures, 0);
        //将纹理和离屏buffer绑定
        mSurfaceTexture = new SurfaceTexture(mTextures[0]);
        //监听有新图像到来
        mSurfaceTexture.setOnFrameAvailableListener(this);

        //使用fbo 将samplerExternalOES 输入到sampler2D中
        cameraFilter = new CameraFilter(glRenderView.getContext());
        //负责将图像绘制到屏幕上
        screenFilter = new ScreenFilter(glRenderView.getContext());

    }

 

onSurfaceCreated中,实例Camera2Helper对象,创建一个SurfaceTexture和纹理,并进行绑定。这个SurfaceTexture会传给Camera2中,camera2负责输入图像到SurfaceTexture中,这里的SurfaceTexture是一个离屏buffer。并且实例CameraFilter和ScreenFilter。

当有新图像来了,会执行onFrameAvailable,更新图像。

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        glRenderView.requestRender();
    }

CameraFilter 的作用下面会讲到,问什么要将 samplerExternalOES 输入到sampler2D中?

ScreenFilter:负责将图像绘制到屏幕上(加完滤镜美颜等效果,也是用这个类进行展示的)

 

CameraFilter的顶点着色器。

// 把顶点坐标给这个变量, 确定要画画的形状
attribute vec4 vPosition;
//接收纹理坐标,接收采样器采样图片的坐标
attribute vec4 vCoord;
//变换矩阵, 需要将原本的vCoord(01,11,00,10) 与矩阵相乘 才能够得到 surfacetexure(特殊)的正确的采样坐标
uniform mat4 vMatrix;
//传给片元着色器 像素点
varying vec2 aCoord;
void main(){
    //内置变量 gl_Position ,我们把顶点数据赋值给这个变量 opengl就知道它要画什么形状了
    gl_Position = vPosition;
    // 进过测试 和设备有关(有些设备直接就采集不到图像,有些呢则会镜像)
    aCoord = (vMatrix * vCoord).xy;
    //aCoord =  vec2((vCoord*vMatrix).x,(vCoord*vMatrix).y);
}

片元着色器:

#extension GL_OES_EGL_image_external : require
//SurfaceTexture比较特殊
//float数据是什么精度的
precision mediump float;

//采样点的坐标
varying vec2 aCoord;

//采样器
uniform samplerExternalOES vTexture;

void main(){
    //变量 接收像素值
    // texture2D:采样器 采集 aCoord的像素
    //赋值给 gl_FragColor 就可以了
    gl_FragColor = texture2D(vTexture,aCoord);
}

在实例化CameraFilter,会创建着色器程序。拿到着色器中的变量。

 

public BaseFilter(Context mContext, int mVertexShaderId, int mFragShaderId) {
        this.mContext = mContext;
        this.mVertexShaderId = mVertexShaderId;
        this.mFragShaderId = mFragShaderId;


        mGlVertexBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mGlVertexBuffer.clear();

//        float[] VERTEXT = {
//                -1.0f, -1.0f,
//                1.0f, -1.0f,
//                -1.0f, 1.0f,
//                1.0f, 1.0f
//        };

        float[] VERTEXT = {
                -1.0f, 1.0f,
                1.0f, 1.0f,
                -1.0f, -1.0f,
                1.0f, -1.0f
        };


        mGlVertexBuffer.put(VERTEXT);

        mGlTextureBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        mGlTextureBuffer.clear();

//        float[] TEXTURE = {
//                0.0f, 1.0f,
//                1.0f, 1.0f,
//                0.0f, 0.0f,
//                1.0f, 0.0f,
//        };

        float[] TEXTURE = {
                0.0f, 0.0f,
                1.0f, 0.0f,
                0.0f, 1.0f,
                1.0f, 1.0f,
        };


        mGlTextureBuffer.put(TEXTURE);

        initilize(mContext);

        resetCoordinate();

    }

BaseFilter 是父类,首先初始化了顶点坐标和纹理坐标的值,

 

    private void initilize(Context mContext) {
        //读取着色器信息
        mVertexShader = OpenGlUtils.readRawShaderFile(mContext, mVertexShaderId);
        mFragShader = OpenGlUtils.readRawShaderFile(mContext, mFragShaderId);
        //创建着色器程序
        mProgramId = OpenGlUtils.loadProgram(mVertexShader, mFragShader);
        //获取着色器变量,需要赋值
        vPosition = GLES20.glGetAttribLocation(mProgramId, "vPosition");
        vCoord = GLES20.glGetAttribLocation(mProgramId, "vCoord");
        vMatrix = GLES20.glGetUniformLocation(mProgramId, "vMatrix");
        vTexture = GLES20.glGetUniformLocation(mProgramId, "vTexture");

    }

会把着色器里的信息以String读取出来,OpenGlUtils是个工具类,OpenGlUtils.loadProgram创建着色器程序。

ScreenFilter也是一样的,但是不同的是在片元着色器中,接收的纹理是Sampler2D,而不是 samplerExternalOES。这是因为,在CameraFilter中,传入的直接是SurfaceTexture,它不属于OpenGL中定义的东西,所以使用samplerExternalOES,经过CameraFilter使用FBO处理后,后续的所有操作都使用Sampler2D就可以了。
precision mediump float;

varying vec2 aCoord;

uniform sampler2D vTexture;

void main(){
    gl_FragColor = texture2D(vTexture, aCoord);
}

 

onSurfaceChanged

@Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        //1080 1899

        camera2Helper.setPreviewSizeListener(this);
        camera2Helper.setOnPreviewListener(this);
        //打开相机
        camera2Helper.openCamera(width, height, mSurfaceTexture);
        


        float scaleX = (float) mPreviewHeight / (float) width;
        float scaleY = (float) mPreviewWdith / (float) height;

        float max = Math.max(scaleX, scaleY);

        screenSurfaceWid = (int) (mPreviewHeight / max);
        screenSurfaceHeight = (int) (mPreviewWdith / max);
        screenX = width - (int) (mPreviewHeight / max);
        screenY = height - (int) (mPreviewWdith / max);
        
        //prepare 传如 绘制到屏幕上的宽 高 起始点的X坐标 起使点的Y坐标
        cameraFilter.prepare(screenSurfaceWid, screenSurfaceHeight, screenX, screenY);
        screenFilter.prepare(screenSurfaceWid, screenSurfaceHeight, screenX, screenY);

       
    }

 onSurfaceChanged中开启相机,传入screenFilter要预览的大小参数。但对于cameraFilter..prepare中,还会创建FBO。

创建过成在 loadFBO中

 private void loadFOB() {

        if (mFrameBuffers != null) {
            destroyFrameBuffers();
        }
        //创建FrameBuffer
        mFrameBuffers = new int[1];
        GLES20.glGenFramebuffers(mFrameBuffers.length, mFrameBuffers, 0);
        //穿件FBO中的纹理
        mFBOTextures = new int[1];
        OpenGlUtils.glGenTextures(mFBOTextures);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mFBOTextures[0]);
        //指定FBO纹理的输出图像的格式 RGBA
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, mOutputWidth, mOutputHeight,
                0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0]);
        
        //将fbo绑定到2d的纹理上
        GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
                GLES20.GL_TEXTURE_2D, mFBOTextures[0], 0);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);

    }

 

onDrawFrame方法

@Override
    public void onDrawFrame(GL10 gl) {
        int textureId;
        // 配置屏幕
        //清理屏幕 :告诉opengl 需要把屏幕清理成什么颜色
        GLES20.glClearColor(0, 0, 0, 0);
        //执行上一个:glClearColor配置的屏幕颜色
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        
        //更新获取一张图
        mSurfaceTexture.updateTexImage();
        
        mSurfaceTexture.getTransformMatrix(mtx);
        //cameraFiler需要一个矩阵,是Surface和我们手机屏幕的一个坐标之间的关系
        cameraFilter.setMatrix(mtx);
        
        textureId = cameraFilter.onDrawFrame(mTextures[0]);
        

        int id = screenFilter.onDrawFrame(textureId);
        

    }

cameraFilter.onDrawFrame(mTextures[0])

mTextures[0]是SUrfaceTexture绑定的纹理ID
@Override
    public int onDrawFrame(int textureId) {

        //锁定绘制的区域  绘制是从左下角开始的
        GLES20.glViewport(0, 0, mOutputWidth, mOutputHeight);
        //绑定FBO,在FBO上操作
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mFrameBuffers[0]);

        //使用着色器
        GLES20.glUseProgram(mProgramId);

        //赋值vPosition
        mGlVertexBuffer.position(0);
        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, mGlVertexBuffer);
        GLES20.glEnableVertexAttribArray(vPosition);

        //赋值vCoord
        mGlTextureBuffer.position(0);
        GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, mGlTextureBuffer);
        GLES20.glEnableVertexAttribArray(vCoord);
        //赋值vMatrix
        GLES20.glUniformMatrix4fv(vMatrix, 1, false, matrix, 0);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        //SurfaceTexture 对应 GL_TEXTURE_EXTERNAL_OES 类型
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
        //赋值vTexture
        GLES20.glUniform1i(vTexture, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);

        return mFBOTextures[0];
    }

在FBO上操作,把输入的纹理ID上的图像,输出到FBO的纹理ID上然后返回。

 

screenFilter.onDrawFrame(textureId);

    public int onDrawFrame(int textureId) {

        GLES20.glViewport(x, y, mOutputWidth, mOutputHeight);

        GLES20.glUseProgram(mProgramId);

        mGlVertexBuffer.position(0);

        GLES20.glVertexAttribPointer(vPosition, 2, GLES20.GL_FLOAT, false, 0, mGlVertexBuffer);
        GLES20.glEnableVertexAttribArray(vPosition);

        mGlTextureBuffer.position(0);
        GLES20.glVertexAttribPointer(vCoord, 2, GLES20.GL_FLOAT, false, 0, mGlTextureBuffer);
        GLES20.glEnableVertexAttribArray(vCoord);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        //传如的是GL_TEXTURE_2D类型
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
        GLES20.glUniform1i(vTexture, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

        return textureId;


    }

这里拿到的textureId是CameraFilter中FBO的纹理ID,赋值着色器变量,直接glDrawArrays绘制。这里

GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);的意思是绘制三角形,从第一个点开始,总共有4个点。

1 2 3 画一个三角,2 3 4画一个三角,这四个点就是顶点坐标。这里两个三角正好绘制出来的是一个矩形。

 

github项目地址:https://github.com/wangchao0837/OpenGlCameraRender

 

 

 

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
好的,以下是一个简单的 Android OpenGL 实现代码,用于将预览图片数据保存到 Texture ID 中并在屏幕上显示。 首先,您需要创建一个 GLSurfaceView,并在其 Renderer 中实现预览图片的渲染逻辑。在下面的代码中,我们使用 Camera2 API 获取摄像头预览数据并将其渲染到屏幕上。 ``` public class MyGLSurfaceView extends GLSurfaceView { private MyGLRenderer mRenderer; public MyGLSurfaceView(Context context) { super(context); setEGLContextClientVersion(2); mRenderer = new MyGLRenderer(); setRenderer(mRenderer); } public class MyGLRenderer implements GLSurfaceView.Renderer { private int mTextureId = -1; private SurfaceTexture mSurfaceTexture; private CameraDevice mCameraDevice; private CameraCaptureSession mCaptureSession; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // 创建 OpenGL 纹理对象 int[] textureIds = new int[1]; GLES20.glGenTextures(1, textureIds, 0); mTextureId = textureIds[0]; // 将纹理对象绑定到 GL_TEXTURE_2D 目标上 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId); // 设置纹理参数 GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR); GLES20.glTexParameteri(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); // 创建 SurfaceTexture 对象,并将其与纹理对象关联 mSurfaceTexture = new SurfaceTexture(mTextureId); mSurfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() { @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { requestRender(); } }); // 打开摄像头 CameraManager cameraManager = (CameraManager) getContext().getSystemService(Context.CAMERA_SERVICE); try { String cameraId = cameraManager.getCameraIdList()[0]; cameraManager.openCamera(cameraId, new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { mCameraDevice = camera; try { // 创建预览请求 Surface surface = new Surface(mSurfaceTexture); CaptureRequest.Builder builder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); builder.addTarget(surface); CaptureRequest request = builder.build(); // 创建预览会话 mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { mCaptureSession = session; try { // 启动预览 mCaptureSession.setRepeatingRequest(request, null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { session.close(); mCaptureSession = null; } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onDisconnected(CameraDevice camera) { camera.close(); mCameraDevice = null; } @Override public void onError(CameraDevice camera, int error) { camera.close(); mCameraDevice = null; } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); } @Override public void onDrawFrame(GL10 gl) { // 更新纹理数据 mSurfaceTexture.updateTexImage(); // 渲染预览画面 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glUseProgram(mProgram); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureId); int positionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition"); GLES20.glEnableVertexAttribArray(positionHandle); GLES20.glVertexAttribPointer(positionHandle, 2, GLES20.GL_FLOAT, false, 0, mVertexBuffer); int texCoordHandle = GLES20.glGetAttribLocation(mProgram, "aTexCoord"); GLES20.glEnableVertexAttribArray(texCoordHandle); GLES20.glVertexAttribPointer(texCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mTexCoordBuffer); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glDisableVertexAttribArray(positionHandle); GLES20.glDisableVertexAttribArray(texCoordHandle); } } } ``` 上述代码中,我们创建了一个 GLSurfaceView,并在它的 Renderer 中实现预览图片的渲染逻辑。在 onSurfaceCreated 方法中,我们创建了一个 OpenGL 纹理对象,并将其与一个 SurfaceTexture 对象关联。然后,我们打开摄像头并创建了一个预览会话,将摄像头预览数据传递给 SurfaceTexture 对象,从而更新纹理数据。在 onDrawFrame 方法中,我们使用该纹理对象渲染预览画面,并将其显示在屏幕上。 请注意,上述代码中的部分变量和方法实现并未展示,您需要自行完成它们的实现。同时,如果您需要保存预览图片数据到文件中,可以使用 glReadPixels 函数将屏幕上的像素数据读取到内存中,然后将其保存到文件中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值