第一篇文章讲述了OpenGL ES 涉及到的一些原理,相信初学者应该对OpenGL ES有了一定的初步认识与了解。下面我们讲解如何进行OpenGL ES的编程实现。
先来一个简单的例子,在手机的屏幕上绘制一个三角形,三角形的三个顶点采用不同的颜色。
1.在AndroidManifest.xml中添加对OpenGL ES 2.0的支持。
复制代码
1
2
3<uses-feature android:glEsVersion="0x00020000" android:required="true" />
同时在自定义的Activity中判断手机硬件是否支持OpenGL ES 2.0,创建GLSurfaceView实现对OpenGL ES API的绘制。
复制代码
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
35public class TriangleActivity extends Activity { private GLSurfaceView mGLSurfaceView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGLSurfaceView = new GLSurfaceView(this); final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo(); final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000; if (supportsEs2) { mGLSurfaceView.setEGLContextClientVersion(2); mGLSurfaceView.setRenderer(new MyRenderer()); } else { return; } setContentView(mGLSurfaceView); } @Override protected void onResume() { super.onResume(); mGLSurfaceView.onResume(); } @Override protected void onPause() { super.onPause(); mGLSurfaceView.onPause(); } }
2.实现自定义的渲染器,继承GLSurfaceView.Renderer。
复制代码
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193public class MyRenderer implements GLSurfaceView.Renderer { private float[] mModelMatrix = new float[16];//模型矩阵 private float[] mViewMatrix = new float[16];//视图矩阵 private float[] mProjectionMatrix = new float[16];//投影矩阵 private float[] mMVPMatrix = new float[16];//总矩阵 private final FloatBuffer mVertices;//存储顶点坐标数据 private int mMVPMatrixHandle;//对应顶点着色器中的总矩阵句柄 private int mPositionHandle;//坐标句柄 private int mColorHandle;//颜色句柄 private final int mBytesPerFloat = 4;//每一个float占4个字节 private final int mStrideBytes = 7 * mBytesPerFloat;//每一个顶点的步长,下文中一个顶点包括X,Y,Z,R,G,B,A所以这里是7 private final int mPositionOffset = 0;//顶点偏移量 private final int mPositionDataSize = 3;//一个顶点数据大小 private final int mColorOffset = 3;//颜色偏移量,即一个顶点数据,位置为3开始表示的是颜色值 private final int mColorDataSize = 4;//颜色占据的数据大小 public MyRenderer() { final float[] verticesData = { // X, Y, Z, // R, G, B, A -0.5f, -0.25f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,//第一个顶点 0.5f, -0.25f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f,//第二个顶点 0.0f, 0.5f, 0.0f,//第三个顶点 0.0f, 1.0f, 0.0f, 1.0f }; //GPU不能直接访问java虚拟机内存,所以需要将java内存转化为本地系统内存,还要考虑存储时的大小端模式,这里直接指定采用本地存储顺序即可 mVertices = ByteBuffer.allocateDirect(verticesData.length * mBytesPerFloat) .order(ByteOrder.nativeOrder()).asFloatBuffer(); mVertices.put(verticesData).position(0);//位置置0,从开始的位置取数据 } @Override public void onSurfaceCreated(GL10 glUnused, EGLConfig config) { //这里可以调用OpenGL ES API进行绘制操作了,下面一行仅为清屏操作,设置屏幕为某一颜色 GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f); //设置观察点的位置,即摄像机的位置 final float eyeX = 0.0f; final float eyeY = 0.0f; final float eyeZ = 1.5f; //设置观察物体的位置 final float lookX = 0.0f; final float lookY = 0.0f; final float lookZ = -5.0f; //设置摄像机的上方向,类比人看某一物体时,头顶方向即为上方向 final float upX = 0.0f; final float upY = 1.0f; final float upZ = 0.0f; //通过下面这个函数设置视图矩阵 Matrix.setLookAtM(mViewMatrix, 0, eyeX, eyeY, eyeZ, lookX, lookY, lookZ, upX, upY, upZ); //编写顶点着色器,一个顶点执行一次 final String vertexShader = "uniform mat4 u_MVPMatrix; n" //总矩阵,与mMVPMatrixHandle对应 + "attribute vec4 a_Position; n" //顶点坐标,与mPositionHandle对应 + "attribute vec4 a_Color; n" //颜色值,与mColorHandle对应 + "varying vec4 v_Color; n" //由顶点着色器向片元着色器传递颜色值的中间变量 + "void main() n" + "{ n" + " v_Color = a_Color; n" + " gl_Position = u_MVPMatrix* a_Position;n" //计算最终的顶点坐标 + "} n"; //编写片元着色器,一个片元执行一次 final String fragmentShader = "precision mediump float; n" //指定为中等精度 + "varying vec4 v_Color; n" //接收来自顶点着色器传递过来的颜色变量 + "void main() n" + "{ n" + " gl_FragColor = v_Color; n" //设置片元最后的颜色 + "} n"; //创建顶点着色器 int vertexShaderHandle = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER); if (vertexShaderHandle != 0) { //加载顶点着色器的脚本到创建的顶点着色器中,并进行编译 GLES20.glShaderSource(vertexShaderHandle, vertexShader); GLES20.glCompileShader(vertexShaderHandle); final int[] compileStatus = new int[1]; //检查编译的结果是否正确,因为GPU的执行无法像CPU执行一样通过打印日志来获取执行结果,所以提供了获取执行结果的函数 GLES20.glGetShaderiv(vertexShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0); if (compileStatus[0] == 0) { GLES20.glDeleteShader(vertexShaderHandle); vertexShaderHandle = 0; } } if (vertexShaderHandle == 0) { throw new RuntimeException("创建顶点着色器错误"); } //创建片元着色器 int fragmentShaderHandle = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER); if (fragmentShaderHandle != 0) { //加载片元着色器的脚本到创建的片元着色器中,并进行编译 GLES20.glShaderSource(fragmentShaderHandle, fragmentShader); GLES20.glCompileShader(fragmentShaderHandle); final int[] compileStatus = new int[1]; GLES20.glGetShaderiv(fragmentShaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, 0); if (compileStatus[0] == 0) { GLES20.glDeleteShader(fragmentShaderHandle); fragmentShaderHandle = 0; } } if (fragmentShaderHandle == 0) { throw new RuntimeException("创建片元着色器错误"); } //创建一个着色器程序 int programHandle = GLES20.glCreateProgram(); if (programHandle != 0) { //将顶点着色器和片元着色器链接到执行程序中 GLES20.glAttachShader(programHandle, vertexShaderHandle); GLES20.glAttachShader(programHandle, fragmentShaderHandle); GLES20.glLinkProgram(programHandle); final int[] linkStatus = new int[1]; //检查链接的结果是否出错 GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(programHandle); programHandle = 0; } } if (programHandle == 0) { throw new RuntimeException("创建程序失败"); } //获取顶点着色器中相应属性的句柄值 mMVPMatrixHandle = GLES20.glGetUniformLocation(programHandle, "u_MVPMatrix"); mPositionHandle = GLES20.glGetAttribLocation(programHandle, "a_Position"); mColorHandle = GLES20.glGetAttribLocation(programHandle, "a_Color"); //需要指定使用哪一个着色器程序来执行绘制操作,一个应用程序中可以有多个着色器程序 GLES20.glUseProgram(programHandle); } @Override public void onSurfaceChanged(GL10 glUnused, int width, int height) { //指定视图窗口的大小,即在手机上需要显示的大小 GLES20.glViewport(0, 0, width, height); //计算宽度比例,避免屏幕方向变化,引起比例失调,导致失真 final float ratio = (float) width / height; //指定近平面的值 final float left = -ratio; final float right =ratio; final float bottom = -1.0f; final float top = 1.0f; final float near = 1.0f; final float far = 10.0f; //设置透视投影矩阵,设置正交投影矩阵函数为Matrix.orthoM() Matrix.frustumM(mProjectionMatrix, 0, left, right, bottom, top, near, far); } @Override public void onDrawFrame(GL10 glUnused) { //调用OpenGL ES API 清除深度缓存和颜色缓存,避免之前的数据对结果造成影响 GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); long time = SystemClock.uptimeMillis() % 10000L; float angle = (360.0f / 10000.0f) * ((int) time); Matrix.setIdentityM(mModelMatrix, 0); //随着时间的流逝,变换三角形绕着X轴的旋转角度,三角形会绕着X轴不断的旋转 Matrix.rotateM(mModelMatrix, 0, angle, 1, 0, 0); drawTriangle(mVertices); } private void drawTriangle(final FloatBuffer triangleData) { triangleData.position(mPositionOffset); //将三角形的顶点数据传给着色器中相应的顶点句柄 GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false, mStrideBytes, triangleData); GLES20.glEnableVertexAttribArray(mPositionHandle);//使能顶点数组 triangleData.position(mColorOffset); //将颜色的值传给顶点着色器中颜色对应的句柄 GLES20.glVertexAttribPointer(mColorHandle, mColorDataSize, GLES20.GL_FLOAT, false, mStrideBytes, triangleData); GLES20.glEnableVertexAttribArray(mColorHandle); //通过矩阵相乘,计算最终的总矩阵 Matrix.multiplyMM(mMVPMatrix, 0, mViewMatrix, 0, mModelMatrix, 0); Matrix.multiplyMM(mMVPMatrix, 0, mProjectionMatrix, 0, mMVPMatrix, 0); //将计算后的总矩阵传给顶点着色器中总矩阵对应的句柄 GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0); //开始绘制三角形,绘制方式为GLES20.GL_TRIANGLES模式,第一篇文章已经指出 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); } }
以上每一行的作用我都在后面做了注释,希望可以说的清楚,不明白的朋友可以留言。
最后
以上就是风趣枕头最近收集整理的关于OpenGL ES 在Android平台的应用实践(2)的全部内容,更多相关OpenGL内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复