openGL是一个图形绘制专业编程接口,功能比较强大,可以绘制二维,三维,它与硬件没有关系,也可以在不同的平台上使用,
进行良好的移植,使用较为广泛
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