我是靠谱客的博主 无语往事,这篇文章主要介绍Android OpenGL ES 学习(一),现在分享给大家,希望可以做个参考。

opengl es 相关概念

opengl es 全称 Open Graphics Library for Embedded Systems ,是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。该API由Khronos集团定义推广,Khronos是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。(摘自百度百科)。
概念很抽象,先记住 opengl 可以做出很多牛逼的特效就可以了。 只有熟练使用opengl后才会对opengl的概念有更加深刻的理解。

opengl es 初步使用

使用opengl 绘图需要用到两个基础的类,GLSurfaceView与GLSurfaceView的内部接口类Renderer,其中GLSurfaceView继承SurfaceView,用于显示渲染的图像,Renderer主要用于渲染图像。 在Renderer中调用opengl进行相应的绘制。

复制代码
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
private MyGLSurfaceView mGlSurfaceView; private MyRenderer mMyRenderer; private boolean mRendererHasSet; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGlSurfaceView = new MyGLSurfaceView(this); setContentView(mGlSurfaceView); if (isSupportES20()) { mGlSurfaceView.setEGLContextClientVersion(2); mMyRenderer = new MyRenderer(this); mGlSurfaceView.setRenderer(mMyRenderer); mGlSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY); mRendererHasSet = true; } else { Toast.makeText(this, "不支持opengl es 2.0", Toast.LENGTH_SHORT).show(); } } // 判断设备是否支持opengl es 2.0 private boolean isSupportES20() { ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); ConfigurationInfo deviceConfigurationInfo = activityManager.getDeviceConfigurationInfo(); return deviceConfigurationInfo.reqGlEsVersion >= 0x20000; }

着色器

着色器分为顶点着色器和片元着色器两类,顶点着色器顾名思义是给顶点上色的,而片元着色器是给顶点之间的像素上色的,片元着色器的粒度更小。 着色器使用gpu对元素进行渲染,故在绘制及一些特效处理方面效率比较高。
着色器的语法接近C规范,且在编译时比较严格,故在写着色器代码时一定要小心各种规范问题。比如浮点数一定要写成带小数点的形式的。
如果应用在最后显示的结果不是预期的,那么可以看下着色器代码有没有报错。

顶点着色器基本写法如下:

复制代码
1
2
3
4
5
6
attribute vec4 a_Position; void main() { // gl_Position是着色器内建变量 gl_Position = a_Position; }

片元着色器基本写法如下:

复制代码
1
2
3
4
5
6
uniform vec4 u_Color; void main() { // gl_FragColor是着色器内建变量 gl_FragColor = u_Color; }

着色器一般以文本文件的形式放在raw文件夹下,有的也直接以字符串的形式写在代码中。 之所以可以这么做,是因为着色器最后都是以字符串的形式加载到opengl的。 可以用如下的代码将着色器代码读到字符串中

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static String readTextFromResource(Context context, int resId) { StringBuilder sb = new StringBuilder(); try { InputStream inputStream = context.getResources().openRawResource(resId); InputStreamReader inputStreamReader = new InputStreamReader(inputStream); BufferedReader reader = new BufferedReader(inputStreamReader); String nextLine; while ((nextLine = reader.readLine()) != null) { sb.append(nextLine); sb.append("n"); } } catch (Exception e) { } return sb.toString(); }

加载着色器

加载着色器一般分为三步,即创建着色器、链接着色器源码、编译着色器。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private static int compileShader(int type, String shaderCode) { // 1、创建着色器, type是指顶点还是片元。 final int shaderObjId = GLES20.glCreateShader(type); if (shaderObjId == 0) { Log.d(TAG, "compileShader: could not create shader with type: " + type); return 0; } // 2、链接着色器源码,其中源码以字符串的形式给出。 GLES20.glShaderSource(shaderObjId, shaderCode); // 3、编译着色器 GLES20.glCompileShader(shaderObjId); final int[] compileStatus = new int[1]; GLES20.glGetShaderiv(shaderObjId, GLES20.GL_COMPILE_STATUS, compileStatus, 0); if (compileStatus[0] == 0) { GLES20.glDeleteShader(shaderObjId); Log.i(TAG, "compileShader: failed compile shader: " + GLES20.glGetShaderInfoLog(shaderObjId)); return 0; } // 返回的是着色器的引用id。 return shaderObjId; }

一般第一步和第三步要进行检查代码是否出错,并打印出相应的日志,方便在开发过程中定位问题。

加载opengl程序

加载opengl程序也分为三步,即创建opengl程序、链接着色器(包括顶点和片元)、链接opengl程序。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static int linkProgram(int vertextShaderId, int fragmentShaderId) { // 1、创建opengl程序 int programId = GLES20.glCreateProgram(); if (programId == 0) { Log.d(TAG, "linkProgram: create program failed " + GLES20.glGetProgramInfoLog(programId)); return 0; } // 2、链接顶点着色器与片元着色器 GLES20.glAttachShader(programId, vertextShaderId); GLES20.glAttachShader(programId, fragmentShaderId); // 3、链接opengl程序 GLES20.glLinkProgram(programId); int[] linkStatus = new int[1]; GLES20.glGetProgramiv(programId, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] == 0) { GLES20.glDeleteProgram(programId); return 0; } // 返回opengl程序的引用id return programId; }

同加载着色器一样,加载opengl程序也一般在第一步和第三步进行检查并打印相关日志。

向着色器传递值

如下代码中,获取着色程序后,便可以使用opengl程序引用id来获取顶点着色器和片元着色器中的变量对应的引用,这样便可以传递顶点数据和颜色数据到opengl,从而绘制相应的图形。

复制代码
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
@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { glClearColor(1f, 0f, 0f, 0f); String vertexShaderSource = TextResourceReader.readTextFromResource(mContext, R.raw.simple_vertex_shader); String fragmentShaderSource = TextResourceReader.readTextFromResource(mContext, R.raw.simple_fragment_shader); int vertexShader = ShaderHelper.compileVertexShader(vertexShaderSource); int fragmentShader = ShaderHelper.compileFragmentShader(fragmentShaderSource); mProgram = ShaderHelper.linkProgram(vertexShader, fragmentShader); glUseProgram(mProgram); // 通过aPositionHandle可以将数据传递到着色器中的变量a_Position aPositionHandle = glGetAttribLocation(mProgram, "a_Position"); // 由于opengl是直接使用gpu渲染故在使用顶点数据时不能使用java层的,必需使用native层,故要通过ByteBuffer来转换一下。 // 注意这里是allocateDirect,不是allocate。 使用不当可能会报native order的错误。 mTableFloatBuffer = ByteBuffer.allocateDirect(mTablevertex.length * BYTE_PER_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer(); mTableFloatBuffer.put(mTablevertex); mTableFloatBuffer.position(0); // 指定aPositionHandle使用的数据指针,也及数据的位置。 glVertexAttribPointer(aPositionHandle, POSITION_COMPONENT_SIZE, GL_FLOAT, false, STRIDE, mTableFloatBuffer); // 使能aPositionHandle,也即可以绘制了。 不调用这个就不会绘制。 glEnableVertexAttribArray(aPositionHandle); uColor = glGetUniformLocation(mProgram, "u_Color"); // aColor = glGetAttribLocation(mProgram, "a_Color"); mTableFloatBuffer.position(POSITION_COMPONENT_SIZE); glVertexAttribPointer(aColor, COLOR_COMPONENT_SIZE, GL_FLOAT, false, STRIDE, mTableFloatBuffer); glEnableVertexAttribArray(aColor); }

绘制

数据传到着色器中后,绘制便比较容易了。 主要代码如下:

复制代码
1
2
3
4
5
6
7
8
9
10
@Override public void onDrawFrame(GL10 gl) { // 清空屏幕 glClear(GLES20.GL_COLOR_BUFFER_BIT); // 将颜色传到着色器中的uniform变量。 四个参数对应red,green, blue,alpha分量,取值范围均为[0,1] glUniform4f(uColor, 1.0f, 1.0f, 1.0f, 1.0f); // 绘制多个三角形,绘制模式还有三角形带,点,线等。 glDrawArrays(GL_TRIANGLE_FAN, 0, 10); }

其中绘制模式有多种方式可选,其中三角形带和三角形扇是为了省略构建图形的三角形顶点设计的(比如挨着的顶点可以共用),也可以将每个三角开的顶点均指定,最后以绘制三角形的方式来绘制(对应的模式为GL_TRIANGLES)。

总结

opengl的使用最主要是正确的处理着色器,一般的步骤如下:
1、加载着色器
2、加载opengl程序(依赖上一步的着色器)
3、传递数据到opengl
4、调用绘制命令绘制。

如果在屏幕上没有显示预期的图像,那么可以通过日志查看是不是着色器代码有错误(如浮点数必需要以小数点的形式表达)或着色器及opengl程序是否正确加载,最后检查对着色器的变量引用获取是不是正确的。
opengl的使用细节很多,后面再慢慢补充吧。 万事开头难,坚持一定会有收获。

最后

以上就是无语往事最近收集整理的关于Android OpenGL ES 学习(一)的全部内容,更多相关Android内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(61)

评论列表共有 0 条评论

立即
投稿
返回
顶部