OpenGL ES for Android 环境搭建

在Android上运行OpenGL ES程序需要用到GLSurfaceView控件,GLSurfaceView继承自SurfaceView并实现了GLThread,通过OpenGL ES进行绘制。

- OpenGL ES1.0是基于OpenGL 1.3的,OpenGL ES1.1是基于OpenGL 1.5的。Android1.0和更高的版本支持这个API规范。OpenGL ES 1.x是针对固定硬件管线的。
- OpenGL ES2.0是基于OpenGL 2.0的,不兼容OpenGL ES 1.x。Android 2.2(API 8)和更高的版本支持这个API规范。OpenGL ES 2.x是针对可编程硬件管线的。
- OpenGL ES3.0的技术特性几乎完全来自OpenGL 3.x的,向下兼容OpenGL ES 2.x。Android 4.3(API 18)及更高的版本支持这个API规范。
- OpenGL ES3.1基本上可以属于OpenGL 4.x的子集,向下兼容OpenGL ES3.0/2.0。Android 5.0(API 21)和更高的版本支持这个API规范。


Android工程中OpenGL ES的版本在AndroidManifest.xml中指定:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
0x00020000表示支持OpenGL ES 2.0。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"> <android.opengl.GLSurfaceView
android:id="@+id/glSurfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent"/> </androidx.constraintlayout.widget.ConstraintLayout>
在Activity中初始化GLSurfaceView
class TriangleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//设置opengl es版
glSurfaceView.setEGLContextClientVersion()
//设置renderer
glSurfaceView.setRenderer(MyRenderer(context = baseContext))
//设置渲染模式
glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
}
override fun onResume() {
super.onResume()
glSurfaceView.onResume()
}
override fun onPause() {
super.onPause()
glSurfaceView.onPause()
}
}
OpenGL ES版本号和AndroidManifest.xml中版本号保持一致,当然我们也可以在设置版本之前判断当前设备是否支持设置的版本,下面的代码判断是支持ES 2.0版本。
fun supportsEs2(context: Context): Boolean {
val configurationInfo =
(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).deviceConfigurationInfo
return configurationInfo.reqGlEsVersion >= 0x20000
}
setRenderMode方法是设置GLSurfaceView渲染模式,渲染模式有RENDERMODE_WHEN_DIRTY和RENDERMODE_CONTINUOUSLY两种,RENDERMODE_WHEN_DIRTY表示当需要的时候才渲染,只有在调用requestRender或者onResume等方法时才渲染,RENDERMODE_CONTINUOUSLY表示一直渲染。
setRenderMode一定要在setRenderer方法之后调用,另外一般需要在Activity或者Fragment的onPause和onResume生命周期中调用GLSurfaceView的onPause和onResume方法,节省系统资源。
Renderer必须实现GLSurfaceView.Renderer接口,并实现onSurfaceCreated ,onDrawFrame,onSurfaceChanged方法,OpenGL ES的渲染工作由此Renderer实现。
MyRenderer的实现:
class MyRenderer(val context: Context) : GLSurfaceView.Renderer {
override fun onDrawFrame(p0: GL10?) {
}
override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int){
}
override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
}
}
onSurfaceCreated ,onDrawFrame,onSurfaceChanged方法说明如下:
- onSurfaceCreated:GLSurfaceView创建完成,也代表OpenGL ES环境创建完成,通常情况下在此方法中创建Program及初始化参数。
- onSurfaceChanged:当Surface发生变化的时候回调,比如竖屏转横屏导致GLSurfaceView大小发生变化,通常情况下在此方法中设置绘制窗口及和GLSurfaceView大小有关系的参数。
- onDrawFrame:执行OpenGL ES渲染工作,由系统以一定的频率来调用重绘View,当设置GLSurfaceView的渲染模式为GLSurfaceView.RENDERMODE_CONTINUOUSLY或不设置时,系统就会主动回调onDrawFrame()方法, 如果设置为 RENDERMODE_WHEN_DIRTY ,手动调用requestRender(),才会渲染。


在OpenGL ES中Shader和Program是两个非常重要的概念,Program需要Vertex Shader(顶点Shader和Fragment Shader(片段Shader),Renderer的渲染就是在执行Program。
- Vertex Shader(顶点Shader)处理顶点数据,对于发送给GPU的每一个顶点都要执行一次Vertex Shader,它的作用就是把顶点在虚拟空间中的三维坐标变换为屏幕上的二维坐标,并带有深度信息。
- Fragment Shader计算每个像素的颜色和其他属性。它通过应用光照值、凹凸贴图,阴影,镜面高光,半透明等处理来计算像素的颜色并输出。

Shader可以以字符串形式存在也可以单独存放在文件中,建议写在assets目录下并以.glsl结尾,因为Android Studio安装GLSL插件可以高亮其代码,便于查找错误。
在assets下创建glsl文件夹,用于存放glsl文件,创建triangle_vertex.glsl文件,保存Vertex Shader代码:
attribute vec4 vPosition;
void main() {
gl_Position = vPosition;
}
创建triangle_fragment.glsl文件,保存Fragment Shader代码:
precision mediump float;
void main()
{
gl_FragColor = vec4(,,,);
}
上面代码表示顶点区域内绘制为红色,vec4内的值表示r,g,b,a。
将上面2个shader文件编译为Shader,
private fun compileShader(shaderType: Int, shaderSource: String): Int {
//创建一个空shader
var shaderHandle: Int = GLES20.glCreateShader(shaderType)
if (shaderHandle != ) {
//加载shader源码
GLES20.glShaderSource(shaderHandle, shaderSource)
//编译shader
GLES20.glCompileShader(shaderHandle)
val compileStatus = IntArray()
//检查shader状态
GLES20.glGetShaderiv(shaderHandle, GLES20.GL_COMPILE_STATUS, compileStatus, )
if (compileStatus[] == ) {
//输入shader异常日志
Log.e(TAG, "Error compile shader:${GLES20.glGetShaderInfoLog(shaderHandle)}")
//删除shader
GLES20.glDeleteShader(shaderHandle)
shaderHandle =
}
}
if (shaderHandle == ) {
Log.e(TAG, "Error create shader")
}
return shaderHandle
}
ShaderType分为GLES20.GL_VERTEX_SHADER和GLES20.GL_FRAGMENT_SHADER,GLES20.GL_VERTEX_SHADER编译Vertex Shader的,GLES20.GL_ FRAGMENT _SHADER编译Fragment Shader。
将Shader链接到program,
fun createAndLinkProgram(vertexCode: String, fragmentCode: String): Int {
//创建一个空的program
var programHandle = GLES20.glCreateProgram()
if (programHandle != ) {
//编译shader
val vertexShaderHandle = compileShader(GLES20.GL_VERTEX_SHADER, vertexCode)
val fragmentShaderHandle = compileShader(GLES20.GL_FRAGMENT_SHADER, fragmentCode)
//绑定shader和program
GLES20.glAttachShader(programHandle, vertexShaderHandle)
GLES20.glAttachShader(programHandle, fragmentShaderHandle)
//链接program
GLES20.glLinkProgram(programHandle)
val linkStatus = IntArray()
//检测program状态
GLES20.glGetProgramiv(programHandle, GLES20.GL_LINK_STATUS, linkStatus, )
if (linkStatus[] == ) {
Log.e(TAG, "Error link program:${GLES20.glGetProgramInfoLog(programHandle)}")
//删除program
GLES20.glDeleteProgram(programHandle)
programHandle =
}
}
if (programHandle == ) {
Log.e(TAG, "Error create program")
}
return programHandle
}
最终返回program的句柄,这2步是固定的,因为我们将其封装为工具类GLTools,供以后使用。
使用工具类GLTools创建program:
private fun createProgram() {
var vertexCode =
AssetsUtils.readAssetsTxt(
context = context,
filePath = "glsl/triangle_vertex.glsl"
)
var fragmentCode =
AssetsUtils.readAssetsTxt(
context = context,
filePath = "glsl/triangle_fragment.glsl"
)
mProgramHandle = GLTools.createAndLinkProgram(vertexCode, fragmentCode)
}
program的创建放在Renderer的onSurfaceCreated方法中,创建成功后,获取Shader中参数句柄及设置顶点数据。获取Vertex Shader中vPosition句柄:
val loc = GLES20.glGetAttribLocation(mProgramHandle, "vPosition")
顶点数据需要了解顶点坐标系统,如下图:
顶点坐标轴以屏幕中心为原点(0,0),z轴的正方向为穿透屏幕指向外面。三角形的顶点坐标设置如下:
var vertexBuffer = GLTools.array2Buffer(
floatArrayOf(
0.0f, 0.5f, 0.0f, // top
-0.5f, -0.5f, 0.0f, // bottom left
0.5f, -0.5f, 0.0f // bottom right
)
)
工具类GLTools中array2Buffer是将顶点数据转换为FloatBuffer,array2Buffer方法定义如下:
fun array2Buffer(array: FloatArray): FloatBuffer {
val bb = ByteBuffer.allocateDirect(array.size * )
bb.order(ByteOrder.nativeOrder())
var buffer = bb.asFloatBuffer()
buffer.put(array)
buffer.position()
return buffer
}

创建OpenGL ES绘制窗口通常是在onSurfaceChanged中设置,
GLES20.glViewport(, , width, height)
- 第一个参数(x):表示窗口x坐标,屏幕左上角为原点
- 第二个参数(y):表示窗口y坐标,(0,0)表示屏幕左上角
- 第三个参数(width):表示窗口的宽
- 第四个参数(height):表示窗口的高

绘制在onDrawFrame中执行,
override fun onDrawFrame(p0: GL10?) {
GLES20.glUseProgram(mProgramHandle)
GLTools.setAttributePointer(vPositionLoc, vertexBuffer, )
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, , )
}
GLES20.glUseProgram(mProgramHandle)表示启动当前program,mProgramHandle是上面创建Program返回的句柄。
GLTools.setAttributePointer(vPositionLoc, vertexBuffer, 3)表示将顶点数据设置给program,参数说明情况如下:
- Location是刚才获取vPosition的句柄
- buffers 是生成的顶点数据,
- pointSize 表示每个顶点个数,上面顶点的个数是3个,
setAttributePointer为封装的工具类方法:
fun setAttributePointer(location: Int, buffers: FloatBuffer, pointSize: Int) {
buffers.position()
GLES20.glEnableVertexAttribArray(location)
GLES20.glVertexAttribPointer(location, pointSize, GLES20.GL_FLOAT, false, , buffers)
}
glDrawArrays方法是绘制,参数说明情况如下:
- 第一个参数mode,表示绘制的方式,可选择的值有:GL_POINTS、GL_LINES、GL_LINE_LOOP、GL_LINE_STRIP、GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN。
- 第二个参数表示从数组缓存中的哪一位开始绘制,一般为0。
- 第三个参数表示绘制顶点的数量。
往期回顾
OpenGL ES for Android 总览
如果此文章对您有所帮助,欢迎扫码关注订阅号。
OpenGL ES for Android 环境搭建的更多相关文章
- Android 中建立一个OpenGL ES的开发环境
转自: http://wiki.eoe.cn/page/Building_an_OpenGL_ES_Environment.html 负责人:zhangql原文链接:http://docs.eoean ...
- OpenGL ES for Android
经过半年的准备OpenGL ES for Android系列文章终于要和大家见面了,在这里定一个小目标-先吸引1000个粉丝,万一实现了呢.写关于OpenGL ES的文章开始是有一些犹豫的,因为Ope ...
- Android 环境搭建
一.Android 环境搭建 开发工具: Android Studio(开发工具,前提是先装 java JDK) 下载地址:http://www.androiddevtools.cn/ Oracl ...
- 20145308刘昊阳 《Java程序设计》实验四 Android环境搭建 实验报告
20145308刘昊阳 <Java程序设计>实验四 Android环境搭建 实验报告 实验名称 Android环境搭建 实验内容 搭建Android环境 运行Android 修改代码,能输 ...
- android环境搭建——工欲善其事,必先利其器 2
前两天鼓捣android, 搭建环境,不想麻烦就用了 adt-bundle-windows-x86-20140702. rar , 起个模拟器哇塞,太爽了. 出去转一圈唠会回来正好启动成功!有个网友 ...
- 第一课 android环境搭建
android环境搭建需要的工具: 1.JDK 2.eclipse 3.SDK 4.ADT
- Android环境搭建的步骤
Android 环境搭建步骤 这里简单介绍一下学习Android之后如何搭建环境的问题 一. 在搭建环境之前,首先你要先下载Java JDK(根据系统位数选择下载是64位或32位的),Eclip ...
- golang开发android环境搭建_window
golang开发android环境搭建介绍 一 安装依赖软件: git:版本管理 go: go开发环境(版本>=1.5),可直接下载window版的go安装包. android studio: ...
- paip.android环境搭建与开发事例
paip.android环境搭建与开发事例 好长时间没有玩AndROID了..以前常常做ANDROID的,今天决定在下载一个要做个时间设置器 作者Attilax , EMAIL:1466519819 ...
随机推荐
- P1055 连通块问题
题目描述 给出一个n行m列的地图,'.'代表陆地,'W'代表水.现在需要你计算地图中有多少个水块.八个方向可以连通 比如:4*6的地图 ...WWW ...WW. WW.... .....W 中有3个 ...
- ES6,ES7重点介绍
1. 字符串模板 <!--旧版拼接字符串--> var str = '我是时间:'+new Date(); <!--新版拼接字符串--> let str = `我是时间${ne ...
- vue-learning:7-template-v-bind-with-class-and-style
绑定元素样式的指令v-bind:class 和v-bind:style 在HTML元素结构中,class和style特性(attribute)是非常突出的,可以为元素添加样式属性(property). ...
- CodeForces 375D Tree and Queries
传送门:https://codeforces.com/problemset/problem/375/D 题意: 给你一颗有根树,树上每个节点都有其对应的颜色,有m次询问,每次问你以点v为父节点的子树内 ...
- 魅族--魅蓝metal
评论:金属潮流平民化
- 使用Gson中的JsonElement所遇到的坑
使用Gson中的JsonElement所遇到的坑 原文链接: https://blog.csdn.net/weixin_30326515/article/details/98196013 声明:此博客 ...
- Mongdb的基本操作及java中用法
Mongdb中所有数据以Bson(类似JSON)的格式存在,可以存储集合,map,二进制文件等多种数据类型. 数据库的常用操作 use [数据库名称];//有就选中,没有就添加并选中show dbs; ...
- ORM之炀,打造自已独特的开发框架CRL
ORM一直是长久不衰的话题,各种重复造轮子的过程一直在进行,轮子都一样是圆的,你的又有什么特点呢? CRL这个轮子造了好多年,功能也越来越标准完备,在开发过程中,解决了很多问题,先上一张脑图描述CRL ...
- 洛谷$P3413$ 萌数 $SAC\#1$ 数位$dp$
正解:数位$dp$ 解题报告: 传送门! 非常套路的数位$dp$,,,?打起来就很爽昂,,,不要脑子,我就很爱嘻嘻嘻 然后$[l,r]$这种问题不显然考虑套路地搞成$[1,l-1]$和$[1,r]$嘛 ...
- C++单例模式的简单实现
c++单例模式的实现(一) 实现方法 1.将构造函数,析构函数私有化,这样保证在类外无法调用类的构造函数创建类的实例,只能通过类内部定义的方法进行创建: 2.在类内定义静态的,指向该类的指针变量ptr ...