Android使用OpenGL實(shí)現(xiàn)相機(jī)濾鏡

目錄

效果展示

濾鏡1

濾鏡2

濾鏡3

濾鏡4

實(shí)現(xiàn)步驟

1.繼承GLSurfaceView

繼承GLSurfaceView用于展示渲染的畫面,并實(shí)現(xiàn)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.獲取相機(jī)數(shù)據(jù)

這里我用的是CameraX
版本是早期的版本,可以只獲取相機(jī)數(shù)據(jù)

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.監(jiān)聽相機(jī)數(shù)據(jù)回調(diào)

在onSurfaceCreated方法中通過setOnFrameAvailableListener方法監(jiān)聽相機(jī)數(shù)據(jù)的回調(diào),相機(jī)數(shù)據(jù)回調(diào)時(shí),調(diào)用requestRender()方法,觸發(fā)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) {
                //攝像頭回調(diào)
                //觸發(fā) onDrawFrame
                requestRender();
            }
        });
        cameraDrawer = new CameraDrawer();
    }
4.渲染畫面

在onDrawFrame函數(shù)中對畫面進(jìn)行渲染,渲染的邏輯在CameraDrawer中,基本都是些固定代碼

class CameraDrawer {

    /**
     * 頂點(diǎn)著色器代碼
     */
    private var vertexShaderCode = ""
    /**
     * 片段著色器代碼
     */
    private var fragmentShaderCode = ""

    /**
     * 著色器程序ID引用
     */
    private var mProgram = 0


    // 四邊形頂點(diǎn)的坐標(biāo)
    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
    )

    // 頂點(diǎn)所對應(yīng)的紋理坐標(biāo)
    private var textureVertices = floatArrayOf(
        0f, 1f,      // top left
        1f, 1f,      // bottom left
        1f, 0f,       // bottom right
        0f, 0f     // top right
    )

    // 四個(gè)頂點(diǎn)的緩沖數(shù)組
    private val vertexBuffer: FloatBuffer =
        ByteBuffer.allocateDirect(squareCoords.size * 4).order(ByteOrder.nativeOrder())
            .asFloatBuffer().apply {
                put(squareCoords)
                position(0)
            }

    // 四個(gè)頂點(diǎn)的繪制順序數(shù)組
    private val drawOrder = shortArrayOf(0, 1, 2, 0, 2, 3)

    // 四個(gè)頂點(diǎn)繪制順序數(shù)組的緩沖數(shù)組
    private val drawListBuffer: ShortBuffer =
        ByteBuffer.allocateDirect(drawOrder.size * 2).order(ByteOrder.nativeOrder())
            .asShortBuffer().apply {
                put(drawOrder)
                position(0)
            }

    // 四個(gè)頂點(diǎn)的紋理坐標(biāo)緩沖數(shù)組
    private val textureVerticesBuffer: FloatBuffer =
        ByteBuffer.allocateDirect(textureVertices.size * 4).order(ByteOrder.nativeOrder())
            .asFloatBuffer().apply {
                put(textureVertices)
                position(0)
            }

    // 每個(gè)頂點(diǎn)的坐標(biāo)數(shù)
    private val COORDS_PER_VERTEX = 3

    // 每個(gè)紋理頂點(diǎn)的坐標(biāo)數(shù)
    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)
        // 編譯頂點(diǎn)著色器和片段著色器
        val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
        // glCreateProgram函數(shù)創(chuàng)建一個(gè)著色器程序,并返回新創(chuàng)建程序?qū)ο蟮腎D引用
        mProgram = GLES20.glCreateProgram().also {
            // 把頂點(diǎn)著色器添加到程序?qū)ο?            GLES20.glAttachShader(it, vertexShader)
            // 把片段著色器添加到程序?qū)ο?            GLES20.glAttachShader(it, fragmentShader)
            // 連接并創(chuàng)建一個(gè)可執(zhí)行的OpenGL ES程序?qū)ο?            GLES20.glLinkProgram(it)
        }
    }

    fun draw(textureID:Int) {
        // 激活著色器程序 Add program to OpenGL ES environment
        GLES20.glUseProgram(mProgram)
        // 獲取頂點(diǎn)著色器中的vPosition變量(因?yàn)橹耙呀?jīng)編譯過著色器代碼,所以可以從著色器程序中獲取);用唯一ID表示
        val position = GLES20.glGetAttribLocation(mProgram, "vPosition")
        // 允許操作頂點(diǎn)對象position
        GLES20.glEnableVertexAttribArray(position)
        // 將頂點(diǎn)數(shù)據(jù)傳遞給position指向的vPosition變量;將頂點(diǎn)屬性與頂點(diǎn)緩沖對象關(guān)聯(lián)
        GLES20.glVertexAttribPointer(
            position, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
            false, vertexStride, vertexBuffer
        )
        // 激活textureID對應(yīng)的紋理單元
        GLES20.glActiveTexture(textureID)
        // 綁定紋理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureID)
        // 獲取頂點(diǎn)著色器中的inputTextureCoordinate變量(紋理坐標(biāo));用唯一ID表示
        val textureCoordinate = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate")
        // 允許操作紋理坐標(biāo)inputTextureCoordinate變量
        GLES20.glEnableVertexAttribArray(textureCoordinate)
        // 將紋理坐標(biāo)數(shù)據(jù)傳遞給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
        )
        // 操作完后,取消允許操作頂點(diǎn)對象position
        GLES20.glDisableVertexAttribArray(position)
        GLES20.glDisableVertexAttribArray(textureCoordinate)
    }

    private fun loadShader(type: Int, shaderCode: String): Int {
        // glCreateShader函數(shù)創(chuàng)建一個(gè)頂點(diǎn)著色器或者片段著色器,并返回新創(chuàng)建著色器的ID引用
        val shader = GLES20.glCreateShader(type)
        // 把著色器和代碼關(guān)聯(lián),然后編譯著色器
        GLES20.glShaderSource(shader, shaderCode)
        GLES20.glCompileShader(shader)
        return shader
    }
}

而渲染的glsl代碼我放在了,raw文件夾下


案例源碼

https://gitee.com/itfitness/open-gl-demo

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容