初步了解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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值