OpenGl入门

OpenGl入门

什么是OpenGL

Open Graphics Library,图形领域的工业标准,是一套跨平台的、专业的、图形变成软件接口。它用于二维、三维图像,是一个功能强大的调用方便的底层图形库。

OpenGL与硬件无关,可以在不同的平台比如Windows、Linux、Mac、Andorid、IOS之间进行移植,因此也得到了广泛的应用。

Android 中使用OpenGl ES

GLSurfaceView,它继承自SurfaceView,它内嵌的sufface专门负责OpenGl渲染,管理Surface与EGL,允许自定义渲染器,让渲染器在独立的线程中运作,和UI线程分离,并且支持按需渲染(on-demand)和连续渲染(continuous)。

OpenGL是一个跨平台操作GUP的API,但是OpenGL需要本地视窗系统进行交互,这几需要一个中间层,EGL就是连接OpenGL ES和本地窗口的接口,引入EGL就是为了屏蔽不同平台上的区别。

CPU和GPU的区别

  • CPU是机器的大脑,中央处理器,用来发号施令
  • GPU的结构主要包括运算器(ALU),控制单元(CU),寄存器(Register),高速缓存器(Cache)和他们之间通讯的数据,控制及状态的总线

计算单元主要执行算数运算,位移和转换

存储单元主要用来保存运算中产生的数据以及指令等

控制单元则对指令译码,发出要完成每条指令要执行的各个操作的控制信号。

CPU中控制器和缓存器比较多,GPU中计算单元比较多。GPU无法单独工作,必须由CPU进行控制调用才能工作,CPU可以单独作用。

Android中渲染步骤

UI对象–>CPU处理为纹理–>通过OpenGLES接口调用GPU

–>GPU对图像进行栅格化–>硬件时钟–>垂直同步–>投射到屏幕

一维 点 顶点

二维 线 2个顶点

三维 面 3个顶点

在OpenGl的世界中中是又很多的3个顶点组成。

  1. 一个3D图片根据像素可以形成无数个顶点,顶点的连线叫做纹理(顶点着色器)
  2. 根据纹理形成一个一个像素(就像PS中对文字进行像素处理)(光栅化)
  3. 对像素进行着色(片元着色器)
  4. 上色之后交给GPU渲染(OpenGl渲染)

在使用OpenGl的时候,第二个和第四个是由系统完成的,我们需要手动写第一个和第三个,告诉系统有多少顶点和怎么着色

练习:

使用OpenGl绘制一个三角形

新建一个项目,Andorid系统中已经有OpenGl,不用导入第三方库,不过需要在AndroidManifest.xml中声明需要使用OpenGl

1
<uses-feature android:glEsVersion="0x00020000" android:required="true" />

OpenGl 使用套路

  • 创建顶点数组
  • 使用gl语言写顶点着色器和片元着色器
  • 将java声明是顶点数组,颜色数组,传给gl语言中的变量
  • GPU开始渲染

自定义一个View继承自GLSurfaceView

1
2
3
4
5
6
7
8
9
10
11
12
13
public class GLView extends GLSurfaceView {

public GLView(Context context) {
super(context);
}

public GLView(Context context, AttributeSet attrs) {
super(context, attrs);
setEGLContextClientVersion(2);
setRenderer(new GLRander(this));
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
}

定义一个渲染器,定义一个三角形的类Triangle,绘制的方法交给它自己来做,因为还可能绘制别的图形。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class GLRander implements GLSurfaceView.Renderer {
protected View mView;
Triangle mTriangle;
public GLRander(View view) {
mView = view;
}

@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0, 0, 0, 0);
mTriangle = new Triangle();
}

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
mTriangle.onSurfaceChanged(gl, width, height);
}

/**
* 该方法不断被调用
*/
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT|GLES20.GL_DEPTH_BUFFER_BIT);
mTriangle.onDrawFrame(gl);
}
}

开始绘制。

顶点着色器用来确定需要绘制的图形的顶点,片元着色器用来给它上色。

顶点着色器和片元着色器都是使用着色器语言(GLSL)语言编写,编写的程序可以直接推送给GPU执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
public class Triangle {

/**
* 三个顶点
*/
static float triangleCoords[] = {
0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
};

/**
* 管道
*/
private FloatBuffer vertexBuffer;

/**
* 顶点着色器
*/
private String vertextShaderCode = "attribute vec4 vPosition;\n" +
"uniform mat4 vMatrix;\n" +
"void main(){" +
"gl_Position=vMatrix*vPosition;" +
"}";

/**
* 片元着色器
*/
private final String fragmentShaderCode = "precision mediump float;\n" +
"uniform vec4 vColor;\n" +
"void main(){\n" +
"gl_FragColor=vColor;\t\n" +
"}";

int mProgram;

public Triangle(){
ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4);
bb.order(ByteOrder.nativeOrder());
vertexBuffer=bb.asFloatBuffer();
// 把顶点 推送 给GPU
vertexBuffer.put(triangleCoords);
vertexBuffer.position(0);

//创建顶点着色器 并在GPU中编译
int shader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
GLES20.glShaderSource(shader,vertextShaderCode);
//编译顶点着色器
GLES20.glCompileShader(shader);

//创建片元着色器 并在GPU中编译
int fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
GLES20.glShaderSource(fragmentShader,fragmentShaderCode);
//编译片元着色器
GLES20.glCompileShader(fragmentShader);

//将顶点着色器和片元着色器放到统一的管理器中进行管理
mProgram = GLES20.glCreateProgram();
GLES20.glAttachShader(mProgram,shader);
GLES20.glAttachShader(mProgram,fragmentShader);

//连接到着色器程序
GLES20.glLinkProgram(mProgram);
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
//计算宽高比
float ratio=(float)width/height;
//投影矩阵
Matrix.frustumM(mProjectMatrix, 0, -ratio, ratio, -1, 1, 3, 120);
//相机
Matrix.setLookAtM(mViewMatrix,
////摄像机的坐标
0, 0, 0, 7,
//目标物的中心坐标
0f, 0f, 0f,
//相机方向
0f, 1f, 0f);
//计算变换矩阵
Matrix.multiplyMM(mMVPMatrix,0,mProjectMatrix,0,mViewMatrix,0);
}

/**
* rgba
*/
float color[] = { 1.0f, 1.0f, 1.0f, 1.0f };
private float[] mViewMatrix=new float[16];
private float[] mProjectMatrix=new float[16];
private float[] mMVPMatrix=new float[16];
public void onDrawFrame(GL10 gl) {
//开始渲染
//拿到总的程序
GLES20.glUseProgram(mProgram);
//拿到vPosition的地址
int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");
//设置缩放
int mMatrixHandler = GLES20.glGetUniformLocation(mProgram, "vMatrix");
GLES20.glUniformMatrix4fv(mMatrixHandler,1,false,mMVPMatrix,0);
//允许对这个地址进行读写
GLES20.glEnableVertexAttribArray(mPositionHandle);
//给顶点着色器赋值
GLES20.glVertexAttribPointer(mPositionHandle,3,
GLES20.GL_FLOAT,false,3*4,vertexBuffer);

//给片元着色器赋值
int mColorHandle = GLES20.glGetUniformLocation(mProgram,"vColor");
GLES20.glUniform4fv(mColorHandle,1,color,0);
//开始绘制
GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,3);
//关闭读写
GLES20.glDisableVertexAttribArray(mPositionHandle);
}
}

结果:

# c/c++

コメント

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×