音视频系列--OpenGL纹理+CameraX滤镜渲染

上一篇写了OpenGL基础理论

最近趁着空余时间把OpenGL纹理学习了,然后结合CameraX做了下滤镜效果,还是打算记录下学习效果。

关于纹理的介绍可以学习这篇文章

一、CameraRender


数据源使用CameraX,没有Camera2代码的复杂,CameraX的使用比较简单,之前写的这篇文章有过CameraX的介绍使用,这里不做过多赘述了。

public class CameraRender implements GLSurfaceView.Renderer, Preview.OnPreviewOutputUpdateListener, SurfaceTexture.OnFrameAvailableListener {

    private static final String TAG = "CameraRender";
    private CameraHelper cameraHelper;
    private CameraView cameraView;
    private SurfaceTexture mCameraTexture;
    private  int[] textures;
    float[] mtx = new float[16];
    private CameraFilter screenFilter;

    public CameraRender(CameraView cameraView) {
        this.cameraView = cameraView;
        LifecycleOwner lifecycleOwner = (LifecycleOwner) cameraView.getContext();
        cameraHelper = new CameraHelper(lifecycleOwner, this);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        textures = new int[1];

        //让SurfaceTexture与Gpu共享一个数据源  0-31
        mCameraTexture.attachToGLContext(textures[0]);
        //监听摄像头数据回调
        mCameraTexture.setOnFrameAvailableListener(this);

        screenFilter = new CameraFilter(cameraView.getContext());
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
    //保存宽高
        screenFilter.setSize(width,height);  //初始化完成
    }

    @Override
    public void onDrawFrame(GL10 gl) {
//        Log.e(TAG, "线程: " + Thread.currentThread().getName());
//      更新摄像头的数据,给了gpu
        mCameraTexture.updateTexImage();
//      每次updateTexImage()被调用时,纹理矩阵都可能发生变化。所以,每次texture image被更新时,getTransformMatrix ()也应该被调用。
        mCameraTexture.getTransformMatrix(mtx);
        screenFilter.setTransformMatrix(mtx);
//      开始绘制
        screenFilter.onDraw(textures[0]);
    }

    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        //一帧一帧回调时,手动渲染
        cameraView.requestRender();
    }

    @Override
    public void onUpdated(Preview.PreviewOutput output) {
//      摄像头预览到的数据 在这里
        mCameraTexture = output.getSurfaceTexture();
    }
}

二、顶点坐标和纹理坐标


    //顶点坐标
    float[] VERTEX = {
            -1.0f, -1.0f,
            1.0f, -1.0f,
            -1.0f, 1.0f,
            1.0f, 1.0f
    };
    //纹理坐标
    float[] TEXTURE = {
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f
    };

两个坐标需要对应上,不然显示出来的图像可能显示不正常,因为用OpenGL渲染就是把Camera的图像贴到纹理坐标上,所以要对应上,上面是固定的

三、顶点着色器


//把顶点坐标给这个变量, 确定要画画的形状
//字节定义的  4个   数组  矩阵
attribute vec4 vPosition;//
//运行在GPU中
//接收纹理坐标,接收采样器采样图片的坐标  camera
attribute vec4 vCoord;

//oepngl  camera opengl坐标和camera坐标不一样,这里需要转换下,vMatrix是通过getTransformMatrix获取的
uniform mat4 vMatrix;

//传给片元着色器 像素点
varying vec2 aCoord;
void main(){
    //    gpu需要渲染的,什么图像、形状
    gl_Position=vPosition;
    aCoord= (vMatrix * vCoord).xy;
}

四、片元着色器


#extension GL_OES_EGL_image_external : require
//必须 写的 固定的  意思   用采样器
//所有float类型数据的精度是lowp
precision mediump float;
varying vec2 aCoord;
//采样器  uniform static
uniform samplerExternalOES vTexture;
void main(){
//Opengl 自带函数
    vec4 rgba = texture2D(vTexture,aCoord);
//    灰色  滤镜
    float color=(rgba.r + rgba.g + rgba.b) / 3.0;
    vec4 tempColor=vec4(color,color,color,1);
    gl_FragColor=tempColor;

}

最后的滤镜效果,还有其他的,这里不一一列举了

五、着色器编译和创建缓存区


public AbstractFilter(Context context, int vertexSharderId, int fragSharderId) {
    //GPU中创建一个缓冲区,提供给CPU,然后数据通过这个放到缓冲区
    vertexBuffer = ByteBuffer.allocateDirect(4 * 4 * 2)
            .order(ByteOrder.nativeOrder())  //重新整理下内存
            .asFloatBuffer();
    vertexBuffer.clear();
    vertexBuffer.put(VERTEX);

    textureBuffer = ByteBuffer.allocateDirect(4 * 4 * 2)
            .order(ByteOrder.nativeOrder())  //重新整理下内存
            .asFloatBuffer();
    textureBuffer.clear();
    textureBuffer.put(TEXTURE);

    String vertexSharder = OpenGLUtils.readRawTextFile(context, vertexSharderId);
    String fragSharder = OpenGLUtils.readRawTextFile(context, fragSharderId);

    //给gpu使用的
    program = OpenGLUtils.loadProgram(vertexSharder, fragSharder);

    vPosition = GLES20.glGetAttribLocation(program, "vPosition");//0
    //接收纹理坐标,接收采样器采样图片的坐标
    vCoord = GLES20.glGetAttribLocation(program, "vCoord");//1
    //采样点的坐标
    vTexture = GLES20.glGetUniformLocation(program, "vTexture");

}

GPU中数据不能直接操作,这里拿到索引间接操作。并且会创建一个缓冲区,提供给CPU,然后数据通过ByteBuffer放到缓冲区,提供给GPU使用。

六、绘制

//摄像头数据,开始渲染
public int onDraw(int texture) {
//      View 的大小
    GLES20.glViewport(0, 0, mWidth, mHeight);
//        使用程序
    GLES20.glUseProgram(program);

//      从索引为0的地方读
    vertexBuffer.position(0);
//     index 指定要修改的通用顶点属性的索引。
//     size  指定每个通用顶点属性的组件数。
//     type  指定数组中每个组件的数据类型。
//        接受符号常量GL_FLOAT  GL_BYTE,GL_UNSIGNED_BYTE,GL_SHORT,GL_UNSIGNED_SHORT或GL_FIXED。 初始值为GL_FLOAT。
//     normalized    指定在访问定点数据值时是应将其标准化(GL_TRUE)还是直接转换为定点值(GL_FALSE)。
//     stride 步长
//      使句柄和数据绑定
    GLES20.glVertexAttribPointer(vPosition, 2, GL_FLOAT, false, 0, vertexBuffer);
//      vPosition生效
    GLES20.glEnableVertexAttribArray(vPosition);

    textureBuffer.position(0);
    GLES20.glVertexAttribPointer(vCoord, 2, GL_FLOAT, false, 0, textureBuffer);
    //CPU传数据到GPU,默认情况下着色器无法读取到这个数据。 需要我们启用一下才可以读取
    GLES20.glEnableVertexAttribArray(vCoord);

//        激活图层
    GLES20.glActiveTexture(GL_TEXTURE0);

//      生成一个采样
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture);
    GLES20.glUniform1i(vTexture, GL_TEXTURE0);

    beforeDraw();

//      通知渲染, 没有调用fbo,就渲染到屏幕中了
    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);

    return texture;
}

详细代码位置

OpenGL代码写好了,大部分就不用改动了,需要改动的也只是特效部分,也就是着色器。

七、参考


1 .OpenGL 之 EGL 使用实践

2 .OpenGL 学习系列 — 纹理

3 .Android SurfaceTexture解读

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值