目录
效果展示
实现步调
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 |