Android使用OpenGL实现相机滤镜

源码 2024-9-4 11:50:22 82 0 来自 中国
目录

效果展示



4.gif
实现步调

1.继承GLSurfaceView

继承GLSurfaceView用于展示渲染的画面,并实现GLSurfaceView.Renderer接口
public class CameraView extends GLSurfaceView implements GLSurfaceView.Renderer {    public CameraView(Context context) {        super(context);    }    public CameraView(Context context, AttributeSet attrs) {        super(context, attrs);    }     @Override    public void onSurfaceCreated(GL10 gl, EGLConfig config) {            }    @Override    public void onSurfaceChanged(GL10 gl, int width, int height) {            }    @Override    public void onDrawFrame(final GL10 gl) {            }}2.获取相机数据

这里我用的是CameraX
版本是早期的版本,可以只获取相机数据
implementation "androidx.camera:camera-core:1.0.0-alpha05"implementation "androidx.camera:camera-camera2:1.0.0-alpha05" private void initCameraX() {        PreviewConfig config = new PreviewConfig.Builder()//                .setTargetResolution(new Size(640,480))                .setLensFacing(CameraX.LensFacing.BACK)                .build();        Preview preview = new Preview(config);        CameraX.bindToLifecycle((LifecycleOwner) getContext(),preview);        preview.setOnPreviewOutputUpdateListener(new Preview.OnPreviewOutputUpdateListener() {            @Override            public void onUpdated(Preview.PreviewOutput output) {                //拿到输出画布                surfaceTexture = output.getSurfaceTexture();            }        });    }3.监听相机数据回调

在onSurfaceCreated方法中通过setOnFrameAvailableListener方法监听相机数据的回调,相机数据回调时,调用requestRender()方法,触发onDrawFrame方法
@Override    public void onSurfaceCreated(GL10 gl, EGLConfig config) {        //拿到摄像头在GPU所在        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);        surfaceTexture.attachToGLContext(textures);        surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {            @Override            public void onFrameAvailable(SurfaceTexture surfaceTexture) {                //摄像头回调                //触发 onDrawFrame                requestRender();            }        });        cameraDrawer = new CameraDrawer();    }4.渲染画面

在onDrawFrame函数中对画面举行渲染,渲染的逻辑在CameraDrawer中,基本都是些固定代码
class CameraDrawer {    /**     * 极点着色器代码     */    private var vertexShaderCode = ""    /**     * 片断着色器代码     */    private var fragmentShaderCode = ""    /**     * 着色器步伐ID引用     */    private var mProgram = 0    // 四边形极点的坐标    private var squareCoords = floatArrayOf(        -1f, 1f, 0.0f,      // top left        -1f, -1f, 0.0f,      // bottom left        1f, -1f, 0.0f,      // bottom right        1f, 1f, 0.0f       // top right    )    // 极点所对应的纹理坐标    private var textureVertices = floatArrayOf(        0f, 1f,      // top left        1f, 1f,      // bottom left        1f, 0f,       // bottom right        0f, 0f     // top right    )    // 四个极点的缓冲数组    private val vertexBuffer: FloatBuffer =        ByteBuffer.allocateDirect(squareCoords.size * 4).order(ByteOrder.nativeOrder())            .asFloatBuffer().apply {                put(squareCoords)                position(0)            }    // 四个极点的绘制序次数组    private val drawOrder = shortArrayOf(0, 1, 2, 0, 2, 3)    // 四个极点绘制序次数组的缓冲数组    private val drawListBuffer: ShortBuffer =        ByteBuffer.allocateDirect(drawOrder.size * 2).order(ByteOrder.nativeOrder())            .asShortBuffer().apply {                put(drawOrder)                position(0)            }    // 四个极点的纹理坐标缓冲数组    private val textureVerticesBuffer: FloatBuffer =        ByteBuffer.allocateDirect(textureVertices.size * 4).order(ByteOrder.nativeOrder())            .asFloatBuffer().apply {                put(textureVertices)                position(0)            }    // 每个极点的坐标数    private val COORDS_PER_VERTEX = 3    // 每个纹理极点的坐标数    private val COORDS_PER_TEXTURE_VERTEX = 2    private val vertexStride: Int = COORDS_PER_VERTEX * 4    private val textVertexStride: Int = COORDS_PER_TEXTURE_VERTEX * 4    init {        fragmentShaderCode = ResourceUtils.readRaw2String(R.raw.camera_frag4)        vertexShaderCode = ResourceUtils.readRaw2String(R.raw.camera_vert)        // 编译极点着色器和片断着色器        val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)        val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)        // glCreateProgram函数创建一个着色器步伐,并返回新创建步伐对象的ID引用        mProgram = GLES20.glCreateProgram().also {            // 把极点着色器添加到步伐对象            GLES20.glAttachShader(it, vertexShader)            // 把片断着色器添加到步伐对象            GLES20.glAttachShader(it, fragmentShader)            // 毗连并创建一个可实行的OpenGL ES步伐对象            GLES20.glLinkProgram(it)        }    }    fun draw(textureID:Int) {        // 激在世色器步伐 Add program to OpenGL ES environment        GLES20.glUseProgram(mProgram)        // 获取极点着色器中的vPosition变量(由于之前已经编译过着色器代码,以是可以从着色器步伐中获取);用唯一ID体现        val position = GLES20.glGetAttribLocation(mProgram, "vPosition")        // 允许利用极点对象position        GLES20.glEnableVertexAttribArray(position)        // 将极点数据传递给position指向的vPosition变量;将极点属性与极点缓冲对象关联        GLES20.glVertexAttribPointer(            position, COORDS_PER_VERTEX, GLES20.GL_FLOAT,            false, vertexStride, vertexBuffer        )        // 激活textureID对应的纹理单元        GLES20.glActiveTexture(textureID)        // 绑定纹理        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID)        // 获取极点着色器中的inputTextureCoordinate变量(纹理坐标);用唯一ID体现        val textureCoordinate = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate")        // 允许利用纹理坐标inputTextureCoordinate变量        GLES20.glEnableVertexAttribArray(textureCoordinate)        // 将纹理坐标数据传递给inputTextureCoordinate变量        GLES20.glVertexAttribPointer(            textureCoordinate, COORDS_PER_TEXTURE_VERTEX, GLES20.GL_FLOAT,            false, textVertexStride, textureVerticesBuffer        )        // 按drawListBuffer中指定的序次绘制四边形        GLES20.glDrawElements(            GLES20.GL_TRIANGLE_STRIP, drawOrder.size,            GLES20.GL_UNSIGNED_SHORT, drawListBuffer        )        // 利用完后,取消允许利用极点对象position        GLES20.glDisableVertexAttribArray(position)        GLES20.glDisableVertexAttribArray(textureCoordinate)    }    private fun loadShader(type: Int, shaderCode: String): Int {        // glCreateShader函数创建一个极点着色器大概片断着色器,并返回新创建着色器的ID引用        val shader = GLES20.glCreateShader(type)        // 把着色器和代码关联,然后编译着色器        GLES20.glShaderSource(shader, shaderCode)        GLES20.glCompileShader(shader)        return shader    }}而渲染的glsl代码我放在了,raw文件夹下

案例源码

https://gitee.com/itfitness/open-gl-demo
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-18 18:27, Processed in 0.153016 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表