目錄

效果展示

濾鏡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文件夾下
