初步了解OpenGL开启摄像头
什么是OpenGL?
openGL是一个图形绘制专业编程接口,功能比较强大,可以绘制二维,三维,它与硬件没有关系,也可以在不同的平台上使用,
进行良好的移植,使用较为广泛
分析OpenGL坐标系和android坐标系
openGL的世界左边是从屏幕的中心点是0,0 android 手机中心点是从屏幕左上角开始的如图:
android:
android的坐标点要用opengl的方式来显示就要换算成opengl的方式,就像是2长图片的重合坐标上的修改,下面直接撸代码
代码部分:
开始之前,我们需要建立2个文件,一个是顶点着色器,一个是片源着色器
顶点着色器是处理顶点、法线等数据,片元着色器是处理光、阴影、遮挡、环境等等对物体表面的影响,最终生成一副图像
在res下面建立一个文件,camera.vert顶点 camera_planyuan.frag片元
下面介绍一下数据类型:float 浮点型
vec2 含两个浮点型数据的向量
vec4 含四个浮点型数据的向量(xyzw,rgba,stpq)
sampler2D 2D纹理采样器(代表一层纹理)
内置函数 exture2D (采样器,坐标) 采样指定位置的纹理
内置变量 顶点 gl_Position vec4 顶点位置
片元 gl_FragColor vec4 颜色
顶点着色器代码
// 把顶点坐标给这个变量, 确定要画画的形状attribute vec4 vPosition;//接收纹理坐标,接收采样器采样图片的坐标attribute vec4 vCoord;//变换矩阵, 需要将原本的vCoord(01,11,00,10) 与矩阵相乘 才能够得到 surfacetexure(特殊)的正确的采样坐标uniform mat4 vMatrix;//传给片元着色器 像素点varying vec2 aCoord;void main(){ //内置变量 gl_Position ,我们把顶点数据赋值给这个变量 opengl就知道它要画什么形状了 gl_Position = vPosition; // 进过测试 和设备有关 aCoord = (vMatrix * vCoord).xy; //aCoord = vec2((vCoord*vMatrix).x,(vCoord*vMatrix).y);}
片元着色器代码
#extension GL_OES_EGL_image_external : require//SurfaceTexture比较特殊//float数据是什么精度的precision mediump float;//采样点的坐标varying vec2 aCoord;//采样器uniform samplerExternalOES vTexture;void main(){ //变量 接收像素值 // texture2D:采样器 采集 aCoord的像素 //赋值给 gl_FragColor 就可以了 gl_FragColor = texture2D(vTexture,aCoord);}
然后我们建立一个类,集成GLSurfaceView 来配置一下OPenGL版本信息 setRenderer(new DouyinRenderer(this));这是一个内部接口,我们建立一个实现类,来实现这个接口
package com.note.opengl.widget;import android.content.Context;import android.opengl.GLSurfaceView;import android.util.AttributeSet;import com.note.opengl.DouyinRenderer;/** * Created by m.wang on 2018/11/6. */public class DouyinView extends GLSurfaceView{ public DouyinView(Context context) { this(context,null); } public DouyinView(Context context, AttributeSet attrs) { super(context, attrs); /*** * 配置GLSurfaceView */ //1.首先是设置EGL版本 setEGLContextClientVersion(2); //设置一个渲染器 setRenderer(new DouyinRenderer(this)); //设置按需加载 连续渲染 就是自动回调onDrawFrame setRenderMode(RENDERMODE_WHEN_DIRTY); }}
DouyinRenderer类说明
DouyinRenderer实现内部类 GLSurfaceView.Renderer接口 有三个方法1.onSurfaceCreated创建画布2.onSurfaceChanged画布改变时候会调用3.onDrawFrame开始画画的时候调用
package com.note.opengl;import android.graphics.SurfaceTexture;import android.hardware.Camera;import android.opengl.GLES20;import android.opengl.GLSurfaceView;import com.note.opengl.filter.ScreenFiter;import com.note.opengl.util.CameraHelper;import com.note.opengl.widget.DouyinView;import javax.microedition.khronos.egl.EGLConfig;import javax.microedition.khronos.opengles.GL10;/** * Created by m.wang on 2018/11/6. */public class DouyinRenderer implements GLSurfaceView.Renderer, SurfaceTexture.OnFrameAvailableListener { private DouyinView mView; private CameraHelper cameraHelper; private SurfaceTexture surfaceTexture; private float[] mtx = new float[16]; private int[] mTextrues; private ScreenFiter mScreenFiter; public DouyinRenderer(DouyinView douyinView) { mView = douyinView; } /** * 创建画布 * @param gl10 * @param eglConfig */ @Override public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) { //初始化摄像头 cameraHelper = new CameraHelper(Camera.CameraInfo.CAMERA_FACING_BACK); //通过openGL 创建一个纹理id mTextrues = new int[1]; GLES20.glGenTextures(mTextrues.length,mTextrues,0); //准备摄像头绘制的画布 surfaceTexture = new SurfaceTexture(mTextrues[0]); surfaceTexture.setOnFrameAvailableListener(this); mScreenFiter = new ScreenFiter(mView.getContext()); } /** * 画布发生改变 * @param gl10 * @param i * @param i1 */ @Override public void onSurfaceChanged(GL10 gl10, int i, int i1) { //开启预览 cameraHelper.starPreview(surfaceTexture); mScreenFiter.onReady(i,i1); } /** * 开始画画 * @param gl10 */ @Override public void onDrawFrame(GL10 gl10) { //画画前清理屏幕 告诉openGL 需要把屏幕清理该改颜色 GLES20.glClearColor(0,0,0,0); //执行清理功能 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); //把摄像头的数据取出来 更新纹理 然后才能从openGL从surfacetexure surfaceTexture.updateTexImage(); //在确立坐标时候获得变化居正 surfaceTexture.getTransformMatrix(mtx); mScreenFiter.onDrawFrame(mTextrues[0],mtx); } /** * 回调方式是 有一个有效的新数据得时候回调 * @param surfaceTexture */ @Override public void onFrameAvailable(SurfaceTexture surfaceTexture) { mView.requestRender(); }}
以上有两个工具类
1.ScreenFiter是把我们写的两个顶点着色器文件和便宜着色器文件读到String之中来
String vertexSource = OpenUtils.readRawTextFile(context,R.raw.camera);String fragSource = OpenUtils.readRawTextFile(context,R.raw.camera_planyuan);
package com.note.opengl.filter;import android.content.Context;import android.opengl.GLES11Ext;import android.opengl.GLES20;import com.note.opengl.R;import com.note.opengl.util.OpenUtils;import java.nio.ByteBuffer;import java.nio.ByteOrder;import java.nio.FloatBuffer;/** * Created by m.wang on 2018/11/6. * 负责往屏幕上渲染 */public class ScreenFiter { private int mProgram; private int mWidth; private int mHieght; private int vPosition; private int vCoord; private int vMatrix; private int vTexture; private FloatBuffer mVertexBuffer; private FloatBuffer mTextureBuffer; public ScreenFiter(Context context){ //获取顶点着色器 读出来 String vertexSource = OpenUtils.readRawTextFile(context,R.raw.camera); String fragSource = OpenUtils.readRawTextFile(context,R.raw.camera_planyuan); //通过字符串代码 //使用openGL //1.创建顶点着色器 int vShaderId = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER); //2.创建片元做着色器 GLES20.glShaderSource(vShaderId,vertexSource); GLES20.glCompileShader(vShaderId); //主动获取成功、失败 int[] status = new int[1]; GLES20.glGetShaderiv(vShaderId,GLES20.GL_COMPILE_STATUS,status,0); if (status[0] != GLES20.GL_TRUE){//方面找出错误原因 throw new IllegalStateException("着顶点色器配置失败"); } int fShaderId = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER); //2.创建片元做着色器 GLES20.glShaderSource(fShaderId,fragSource); GLES20.glCompileShader(fShaderId); //主动获取成功、失败 GLES20.glGetShaderiv(fShaderId,GLES20.GL_COMPILE_STATUS,status,0); if (status[0] != GLES20.GL_TRUE){//方面找出错误原因 throw new IllegalStateException("片源着色器配置失败"); } //3.创建着色器程序(运行到GPU上) mProgram = GLES20.glCreateProgram(); //把着色器塞到程序当中 GLES20.glAttachShader(mProgram,vShaderId); GLES20.glAttachShader(mProgram,fShaderId); //链接着色器 GLES20.glLinkProgram(mProgram); //获得程序是否配置成功 GLES20.glGetProgramiv(mProgram,GLES20.GL_LINK_STATUS,status,0); if (status[0] != GLES20.GL_TRUE){ throw new IllegalStateException("着色器配置失败"); } //已经塞到着色器中了 释放资源 GLES20.glDeleteShader(vShaderId); GLES20.glDeleteShader(fShaderId); //获得着色器三个变量 vPosition = GLES20.glGetAttribLocation(mProgram,"vPosition"); vCoord = GLES20.glGetAttribLocation(mProgram,"vCoord"); vMatrix = GLES20.glGetUniformLocation(mProgram,"vMatrix"); vTexture = GLES20.glGetUniformLocation(mProgram,"vTexture"); //顶点坐标 mVertexBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()) .asFloatBuffer(); mVertexBuffer.clear(); float[] v = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f}; mVertexBuffer.put(v); //顶点坐标 mTextureBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()) .asFloatBuffer(); mTextureBuffer.clear(); float[] t = {1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f }; mTextureBuffer.put(t); } /** * 使用着色器程序画画 * @param texture * @param mtx */ public void onDrawFrame(int texture,float[] mtx){ //设置窗口 画画的 GLES20.glViewport(0,0,mWidth,mHieght); //使用着色器画 GLES20.glUseProgram(mProgram); //将顶点数据传入,并确定形状 mVertexBuffer.position(0); GLES20.glVertexAttribPointer(vPosition,2,GLES20.GL_FLOAT,false,0,mVertexBuffer); //传了数据之后 激活 GLES20.glEnableVertexAttribArray(vPosition); mVertexBuffer.position(0); GLES20.glVertexAttribPointer(vCoord,2,GLES20.GL_FLOAT,false,0,mVertexBuffer); GLES20.glEnableVertexAttribArray(vCoord); //3、变换矩阵 GLES20.glUniformMatrix4fv(vMatrix,1,false,mtx,0); //片元的 vTexture 绑定图像数据到采样器 GLES20.glActiveTexture(GLES20.GL_TEXTURE0); //激活图层 GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,texture); //需要和纹理对应 GLES20.glUniform1f(vTexture,0); //参数传完之后通知openGL 画画 GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4); } public void onReady(int i, int i1) { mWidth = i; mHieght = i1; }}
float[] v = {-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f};
这个代码openGL的世界左边点从左边底部-到右边底部到左边上部到右边上部ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()) 4*2代表有四个顶点 每个顶点的2个值x,y float类型 4个字节的意思
工具类如下:
package com.note.opengl.util;import android.graphics.ImageFormat;import android.graphics.SurfaceTexture;import android.hardware.Camera;import java.io.IOException;/** * Created by m.wang on 2018/11/6. */public class CameraHelper implements Camera.PreviewCallback{ private static final String TAG = "CameraHelper"; public static final int WIDTH = 640; public static final int HEIGHT = 480; private int mCameraId; private Camera mCamera; private byte[] buffer; private Camera.PreviewCallback mPreviewCallback; private SurfaceTexture msufaceTexttrue; public int getmCameraId() { return mCameraId; } public void setmCameraId(int mCameraId) { this.mCameraId = mCameraId; } public CameraHelper(int mCameraId){ this.mCameraId = mCameraId; } public void switchCamera(){ if (mCameraId == Camera.CameraInfo.CAMERA_FACING_BACK){ mCameraId = Camera.CameraInfo.CAMERA_FACING_FRONT; }else { mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK; } stopPreview(); starPreview(msufaceTexttrue); } public void starPreview(SurfaceTexture surfaceTexture) { msufaceTexttrue = surfaceTexture; try { //获得camera对象 mCamera = Camera.open(mCameraId); //配置camera的属性 Camera.Parameters parameters = mCamera.getParameters(); //设置预览格式为nv21 parameters.setPictureFormat(ImageFormat.NV21); //这是摄像头宽高 parameters.setPreviewSize(WIDTH , HEIGHT); //设置摄像头方向角度 mCamera.setParameters(parameters); buffer = new byte[WIDTH * HEIGHT * 3 / 2]; //数据缓存区 mCamera.addCallbackBuffer(buffer); mCamera.setPreviewCallbackWithBuffer(this); //设置预览画面 mCamera.setPreviewTexture(msufaceTexttrue); mCamera.startPreview(); } catch (Exception e) { e.printStackTrace(); } } public void stopPreview(){ if (mCamera != null){ //预览数据回调接口 mCamera.setPreviewCallback(null); //停止预览 mCamera.stopPreview(); mCamera.release(); mCamera = null; } } @Override public void onPreviewFrame(byte[] bytes, Camera camera) { if (null != mPreviewCallback) { mPreviewCallback.onPreviewFrame(bytes, camera); } camera.addCallbackBuffer(buffer); }}
mCameraId = Camera.CameraInfo.CAMERA_FACING_BACK;后置摄像头
文件读出来string接收工具类
package com.note.opengl.util;import android.content.Context;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;/** * Created by m.wang on 2018/11/6. */public class OpenUtils { public static String readRawTextFile(Context context, int rawId) { InputStream is = context.getResources().openRawResource(rawId); BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line; StringBuilder sb = new StringBuilder(); try { while ((line = br.readLine()) != null) { sb.append(line); sb.append("\n"); } } catch (Exception e) { e.printStackTrace(); } try { br.close(); } catch (IOException e) { e.printStackTrace(); } return sb.toString(); }}
demo地址:https://download.csdn.net/download/qq_23213991/10771044
更多相关文章
- Android中的Sqlite数据库的简单使用
- Android之Intent传递数据的方式
- 解决 Android(安卓)中使用ListView和CheckBox批量操作时若干问题
- Android中SharedPreferences的使用(登陆,记住密码)
- android -- 小问题 Android(安卓)WebView缓存分析
- 【Android】Handler的应用(一):从服务器端加载JSON数据
- Android的进阶学习(一)--Activity异常退出
- Android:Intent 显示和隐式 学习
- Android开发中如何使用绘制图表