> 文章列表 > opengl使用流程文述

opengl使用流程文述

opengl使用流程文述

opengl使用流程文述

opengl中是通过三个点确定一个面进行着色,总体思路为确定顶点着色器形成矢量图形进行栅格化==》最后再通过片元着色器进行上色的过程

1,先用一个view继承自GLSurfaceView 也就是mCameraTexure

2, 在构造方法中用setEGLContextClientVersion(2); 选用2版本,2版本稳定一些

3, opengl的渲染方式,手动和自动两种,自动的话直接在构造方法中调用requestRender();

4, 接着初始化CameraX(此处省略)

5,回调接口onUpdate,渲染在哪里

6,我们要通过实现PreviewOnPreviewOutputUpdateListener接口,从onUpdatePreviewOutput参数中拿到CameraX的预览图像,我们从output中通过output.getSurfaceTexture()方法拿到surfaceTexure

7, 我们还要通过实现GLSurfaceView.Render接口,并在构造方法中设置setRender监听,一旦设置监听后,他就会回调onSurfaceCreated方法

8,此时需要设置一个int值的texture,可以理解为在GPU所在的地方

9,接着在onSurfaceCreated方法中进行绑定,需用这个mCameraTextureattachToGLContext(texture)将这个int值传进去,这时就绑定了数据

10,这时候我们给这个mCameraTexture设置setFrameAvailableListener(this)进行渲染,我们需要先实现SurfaceTextureonFrameAvailableListener这个接口

11,摄像头如果有数据,就会回调onFrameAvailable这个方法

12,这个数据就会在GPU没有在CPU

13, 然后我们在onFrameAvailable中调用requestRender方法进行手动渲染,等于摄像头一有数据,它一回调这个方法我们就会手动渲染一次

14,然后就会回调我们写的这个自定义控件中的onDraw方法,也就是onDrawFrame方法

15,到现在我们的Camera获取的数据还在Texture,还没有到我们的CameraView上,接下来我们需要绑定GPU,确定形状,颜色

16,我们需要通过mCameraTextureupdateTextureImage()方法获取最新的数据,到GPU的内存缓冲区。

17,我们需要进行一层转化,将世界坐标系转换为安卓坐标系,因为在opengl中是三个点确定一个面,确定一个矩形就需要四个点的一个矩阵,前面三个是一个面,后面三个是一个面,两个三角形拼成一个矩形,这个就是VERTEX形状

18,这时候需要进行opengl程序了,我们需要先新建一个文件夹raw,写opengl程序,新建一个Camera_vert.glsl文件,这是一个程序,无论片元程序还是顶点程序都有main函数,这个是片元程序

19,再新建一个Camera_vert.glsl这个是顶点程序,在opengl运行着两个程序,一个是片元程序,一个是顶点程序,顶点的作用是确定形状,这个形状已经在CPU中定义好了,接下来我们需要将CPU中定义好的形状传值过来,所以需要写一个接收变量

20,首先我们要声明一个GPU变量,

attribute vec4 vPosition; 

4代表有4个坐标

21,在main方法中对

gl_position = vPosition;

进行赋值,一旦赋值后形状便确认了

22,在这里接收坐标系的是世界坐标系,但是我们需要把它绘制到我们的控件,也就是纹理坐标系中

23,这时我们需要定义纹理坐标系,也是

attribute vec4 vCoord;

24, 接着我们需要定义采样器,将世界坐标系的值传递给纹理坐标系

varying vec2 aCoord;
aCoord = vCoord.xy;

25, 一旦定义varing,它会把我们的顶点坐标系丢到片元坐标系,所以我们在Camera_frag.glsl片元程序中需要声明一个一样的名字,

varying vec2 aCoord; 

名字一定要一模一样,它就会传递,不一样的话就不传递了

26,这时我们需要加载这个opengl程序,在Java方法中

String vertexShader = readRawTextFile(context, R.raw.camera_vert);
//返回的String vertexShader是它的路径

27,接下来我们要创建一个程序

//顶点程序
GLES20.glCreatShader(GLES20.GL_VERTEX_SHADER);//片元程序
GLES20.glCreatShader(GLES20.GL_FRAGMENT_SHADER);

顶点程序和片元程序创建大体上是一样的,只有这里参数上有些差别

28, 创建完顶点程序会返回一个int值,这个intvShader 是程序的地址

29,我们需要关联对应的代码

GLES20.glShaderSource(vShader, vertexShader);

30,接下来编译

//调用这个方法这里GPU会帮你去编译
GLES20.glShaderSource(vShader);

31, 如果编译成功我们需要用一个int[]数组去获取状态

//入参出参对象
int[] status = new int[1];
//获取参数
GLES20.glGetShader(vShader, GLES20.GLCOMPILE_STATUS, status, 0);
//查看配置,是否成功

接下来我们通过status[0]是否等于GLES20.GL_TRUE来判断是否成功

32,然后我们加载片元程序,片元程序和顶点程序流程一样,唯一不同就是两个地址和参数有些不一样

33,两个都执行完成我们需要创建一个总程序

int program = GLES20.glCreatProgram();

它会返回一个int program,这个int值对于CPU来说没有意义,但是对GPU却有意义

34,总程序添加两个程序

//加载顶点程序
GLES20.glAttachShader(program, vShader);
//加载片元程序
GLES20.glAttachShader(program, fShader);

这个总程序就相当于一个exe功能

35,最后我们把整个程序激活

GLES20.glLinkProgram(program);

36, 这个时候我们的自定义控件CameraView和我们的CameraTexture还是没有关系,我们需要将CPU的世界坐标系矩阵,纹理坐标系矩阵传递给GPU

37,需要在Java层用ByteBuffer.allocateDirect,这个方法是通过allocateDirect创建CPUGPU之间的一个通道

Buffer vertexBuffer = ByteBuffer.allocateDirect(4*2*4).order(Byteorder.nativeOrder()).asFloatBuffer();

这时会返回一个Buffer对象,vertexBuffer,先把他清空

vertexBuffer.clear();

再把坐标点放进去

vertexBuffer.put(VERTEX);

38, 这时我们写一个onDraw方法,传入宽高,每次有数据我们就渲染一次,因此在onDrawFrame中去调用onDraw

39, 在onDraw方法中,需要告诉GPU范围,调用GLES20.glViewport()并传入宽高

40,调用program程序,

GLES20.glUSEProgram(program);

41,定位到GPU的地址

int vPostion = GLES20.glGetAttribLocation(program, "vPostion");

它会返回一个intvPosition,地址值

42,将CPU的数据传递给GPU

`GLES20.glVertxAttribPointer()`

要传入6个参数,主要要传入的参数是这个vPositon的地址值,和刚才通道的哪个返回值Buffer也就是vetexBuffer,这样就是把vetexBuffer传递到GPU里去了,这个GPU里的vPositon就有值了

43.接下来调用

GLES20.glEnableVertexAttibArray(vPosition);

代表传完了,告诉GPU现在启动这个通道

44,接着传递纹理坐标系,与顶点坐标系一致,开启vCoord变量

GLES20.glEnableVertexAttibArray(vCoord);

45, 这样在GPU中我们两个值都传递了,一个是vPostion,一个是vCoord

46,我们还要在onDraw方法中每次渲染都需要将vertexBuffertextureBuffer放在0位

47,在onSurfaceCreat中,将CameraView绑定到Texture

mCameraTexture.attachToGLContext(texture);

这样我们的自定义CameraView就与texture绑定了

48, 这时我们需要激活一个图层

GLES20.glActiveTexture(GLES20.GL_TEXTURE0);

一共有32个图层,我们随便使用某一个都可以

49,

//绑定一个采样器和摄像头的内容
GLES20.glBindTexture(GLES20.TEXTURE0, texture);

50, 我们需要在片元程序中定义一个采样器

uniform samplerExternalOES vTexture;

51, 在java中先定义一个intvTexture,接着定位到片元变量

vTexture = GLES20.glGetUniformLocation(program, "vTexture");

52, 接下来我们将vTexture传进去

//告诉摄像机在第几个图层,摄像头和第几个图层进行绑定
GLES20.glUniform1f(vTexure, GL_TEXTURE0);

这个vTexture就拿到片元中的vTexture

53, 在片元程序的main方法中texture2D()

//自带的采样器,图层采样对应的像素值
vec4 rgba = texture2D(vTexture, aCoord);
//我们得到的采样像素就是rgba

54, 我们给gl_FragColor = rgba进行赋值,就可以得到我们摄像头捕获的图像

55,最后在java层通知GPU进行渲染

GLES20.glDrawArrays(GLES20.GL_TRIANGLE, 0, 4);
//第一个参数代表三个顶点
//第二个0 
//第三个代表4个坐标值

56,如果想加滤镜,就在gl_FragColor = rgba赋值的时候分别对rgba的r g b a值乘以一定的系数进行转换,形成一定的滤镜效果
比方说相加灰色滤镜

float color = (rgba.r + rgba.g + rgba.b) / 3
gl_FragColor = vec4(color, color, color, rgba.a);

57,想要增加分屏效果,就是通过控制采样点坐标达到分屏的效果

texture2D(vTexture, vec2(aCoord.x, aCoord.y));

59, 如果我们得到CameraX的预览图像时横着的,我们可以在onDrawFrame方法中

float[] mtx = new float[16];
mCameraTexture.getTransformMatrix(mtx);//得到一个纠正的矩阵//通过摄像头捕获这个偏移值传进来
GLES20.glUniformMatix4fv(vMatrix, 1, false, mtx);

在顶点程序中来一个矩阵

uniform mat4 vMatrix;
aCoord = (vMaxtrix * vCoord.xy);

这样就可以得到一个纠正的图像了

PS: 美颜的主要思路

1,其实就是高斯模糊,就是取每个点的平均值放在周围

2,一般取20个点,上,下 ,左,右各4个,然后里三环外三环,三环的图案20个点累加除以20得到平均值,最后将取到的值赋值到这个坐标点,就得到一个高斯模糊的图像

3,再用高反差,就是清晰图减去一张模糊的图片,保留边界的细节

4,调优。分别对r,g,b三种颜色的图层的高反差图乘以一定的调优系数

5,线性叠加,将蓝图的值取出来,保留边界的细节

6,设置一个磨皮的系数,一般是0~1f,再大会模糊

7,然后两个图原图保留的细节,与一个高斯模糊的图相加

6,再对gl_Fragcolor进行赋值

gl_Fragcolor = vec4(r, 1.0);