1. GlSurfaceView是什么
GlSurfaceView是Android中的一个类,继承自SurfaceView,用于显示OpenGL ES图形渲染的一个视图。
OpenGL ES是一种跨平台的图形API,用于渲染2D和3D图形,也可以将相机的画面显示到GlSurfaceView上,从而实现滤镜的效果。
GlSurfaceView提供了一个可以在Android应用程序中绘制OpenGL ES图形的界面,允许开发者将复杂的3D图形、动画和视觉效果嵌入到应用程序中。
GlSurfaceView处理了OpenGL ES渲染环境的创建、维护和更新,以及与其他Android视图和事件系统的交互。
2. android中怎使用GlSurfaceview
2.1 添加OpenGL ES版本支持
新建Android项目,在AndroidManifest.xml文件中,添加 OpenGL ES 版本支持。例如,要使用 OpenGL ES 2.0,请添加以下代码:
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
如果需要使用 OpenGL ES 3.0 则需要以下声明:0x00030000
如果需要使用 OpenGL ES 3.2 则需要以下声明:0x00030002
2.2 新建自定义的GlSurfaceView
新建一个MyGlSurfaceView类,继承自 GLSurfaceView,并进行初始化
import android.content.Context
import android.opengl.GLSurfaceView
import android.util.AttributeSet
class MyGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {
init {
// 设置 OpenGL ES 版本
setEGLContextClientVersion(2)
// 设置渲染器
val renderer = MyGLRenderer()
setRenderer(renderer)
}
}
2.3 新建自定义的Renderer
创建一个MyRenderer类,实现 GLSurfaceView.Renderer 接口,并实现其中的方法
class MyGLRenderer : GLSurfaceView.Renderer {
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
//设置清除屏幕时使用的颜色
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
}
override fun onDrawFrame(gl: GL10?) {
// 清除屏幕
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
// 设置视口大小
GLES20.glViewport(0, 0, width, height)
}
}
2.4 使用MyGLSurfaceView
在 Activity的xml中使用MyGLSurfaceView
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.heiko.myglsurfaceviewtest.MyGLSurfaceView
android:id="@+id/gl_surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
2.5 运行看下效果
运行后,可以看到一个使用 OpenGL ES 绘制的黑色屏幕

3. 在自定义的MyGLRenderer中绘制三角形
3.1 添加顶点着色器和片段着色器的代码
在 MyGLRenderer 类中,添加顶点着色器和片段着色器的代码。在这里,我们创建一个简单的顶点着色器和片段着色器,它们将顶点位置传递给渲染管线并使用固定颜色进行渲染:
private val vertexShaderCode = "attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = vPosition;" +
"}"
private val fragmentShaderCode = "precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}"
3.2 添加编译和链接着色器的方法
在 MyGLRenderer 类中,添加一个方法来编译和链接着色器,然后返回着色器程序的 ID
private fun loadShaderProgram(vertexCode: String, fragmentCode: String): Int {
// 编译顶点着色器
val vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER)
GLES20.glShaderSource(vertexShader, vertexCode)
GLES20.glCompileShader(vertexShader)
// 编译片段着色器
val fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER)
GLES20.glShaderSource(fragmentShader, fragmentCode)
GLES20.glCompileShader(fragmentShader)
// 链接着色器程序
val program = GLES20.glCreateProgram()
GLES20.glAttachShader(program, vertexShader)
GLES20.glAttachShader(program, fragmentShader)
GLES20.glLinkProgram(program)
return program
}
3.3 加载并创建着色器程序
在 MyGLRenderer 类中,添加成员变量来存储顶点数据、顶点缓冲区对象(VBO)和着色器程序 ID。
private val vertexData = floatArrayOf(
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
)
private var vertexBufferId = 0
private var shaderProgramId = 0
这里vertexData 为什么这么传,是和openGL的世界坐标系相关的

可以看到vertexData的第一个值0.0f,第二个值0.5f这个坐标点位于Y轴偏上的位置,第三个值0.0f我们不用管,这个是Z轴,在2D图形中我们不需要Z轴,所以这个统一传0.0f就好了。
同理,第四个值-0.5f、第五个值-0.5f位于X轴坐标的偏左侧,第六个值也是Z轴,我们不用管。
第七个值是0.5f,第八个值是-0.5f位于X轴坐标的偏右侧,第九个值也是Z轴,我们不用管。
具体如下图所示

然后,在 onSurfaceCreated 方法中初始化这些值,然后加载并创建着色器程序
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
// 设置清除屏幕时使用的颜色
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)
// 创建顶点缓冲区对象
val buffers = IntArray(1)
GLES20.glGenBuffers(1, buffers, 0)
vertexBufferId = buffers[0]
// 将顶点数据上传到缓冲区对象
val vertexBuffer = ByteBuffer.allocateDirect(vertexData.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
vertexBuffer.put(vertexData)
vertexBuffer.position(0)
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId)
GLES20.glBufferData(
GLES20.GL_ARRAY_BUFFER,
vertexData.size * 4,
vertexBuffer,
GLES20.GL_STATIC_DRAW
)
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0)
// 加载并创建着色器程序
shaderProgramId = loadShaderProgram(vertexShaderCode, fragmentShaderCode);
}
3.4 绘制三角形
在 onDrawFrame 方法中,使用创建的着色器程序和顶点缓冲区对象来绘制三角形:
override fun onDrawFrame(gl: GL10?) {
// 清除屏幕
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
// 使用着色器程序
GLES20.glUseProgram(shaderProgramId)
// 绑定顶点缓冲区对象并启用顶点属性
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferId)
val positionLocation = GLES20.glGetAttribLocation(shaderProgramId, "vPosition")
GLES20.glEnableVertexAttribArray(positionLocation)
GLES20.glVertexAttribPointer(positionLocation, 3, GLES20.GL_FLOAT, false, 0, 0)
// 设置片段着色器的颜色
val colorLocation = GLES20.glGetUniformLocation(shaderProgramId, "vColor")
GLES20.glUniform4f(colorLocation, 1.0f, 0.0f, 0.0f, 1.0f)
// 绘制三角形
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3) // GL_TRIANGLES:三角形 GL_POINTS:点
// 禁用顶点属性并解除顶点缓冲区对象的绑定
GLES20.glDisableVertexAttribArray(positionLocation)
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0)
}
3.5 设置视口大小
在 onSurfaceChanged 方法中,还是一样,设置视口大小:
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
// 设置视口大小
GLES20.glViewport(0, 0, width, height)
}
3.6 运行看下效果

4. Android中GLES20.java的API说明
上面我们已经实现了一个三角形的绘制,但是对于GLES20的API还是不太了解,接下来再来看下常用的GLES20 API
4.1 Android中GLES20 API的一些主要功能说明
- Shaders(着色器):GLES20使用可编程的着色器来渲染图形。顶点着色器处理顶点数据,片段着色器处理像素数据。着色器需要用GLSL(OpenGL Shading Language)编写。
- Buffers(缓冲区):GLES20使用缓冲区来存储顶点数据和索引数据。顶点缓冲区对象(VBO)存储顶点数据,元素缓冲区对象(EBO)存储索引数据。
- Textures(纹理):GLES20支持多种纹理类型,如2D纹理、立方体贴图等。纹理用于给3D对象添加详细的表面特征
- Framebuffers(帧缓冲区):GLES20使用帧缓冲区对象(FBO)来存储渲染结果。你可以将渲染结果渲染到纹理中,然后将纹理应用到其他对象上,实现高级渲染效果。
- Transformations(变换):GLES20支持多种变换操作,如平移、旋转、缩放等。变换矩阵用于在顶点着色器中处理顶点数据。
- Lighting(光照):GLES20支持基本的光照计算,如环境光、漫反射光、镜面反射光等。光照计算通常在顶点着色器或片段着色器中进行。
- Blending(混合):GLES20支持颜色混合,用于实现透明度、半透明等效果。混合操作可以根据源颜色和目标颜色按照指定的混合因子进行计算。
- Culling(剔除):GLES20支持面剔除,可以剔除不可见的面,提高渲染性能。面剔除可以根据面的正面或反面进行。
- Depth Testing(深度测试):GLES20支持深度测试,用于判断像素的可见性。深度测试可以根据像素的深度值进行比较,只渲染最前面的像素。
- Stencil Testing(模板测试):GLES20支持模板测试,用于实现遮罩、镜子等效果。模板测试可以根据模板缓冲区的值对像素进行掩盖或者保留。
4.2 GLES20 API中一些主要方法的说明
- glGenBuffers:生成缓冲区对象,如顶点缓冲区对象(VBO)和元素缓冲区对象(EBO)。
- glBindBuffer:绑定缓冲区对象,使其成为当前活动的缓冲区。
- glBufferData:将数据上传到缓冲区对象。
- glVertexAttribPointer:定义顶点属性数组,指定顶点数据在缓冲区中的布局。
- glEnableVertexAttribArray:启用顶点属性数组。
- glDisableVertexAttribArray:禁用顶点属性数组。
- glUseProgram:使用某个着色器程序进行渲染。
- glGetUniformLocation:获取着色器程序中uniform变量的位置。
- glUniformMatrix4fv:为uniform变量设置矩阵数据。
- glCreateShader:创建着色器对象。
- glShaderSource:为着色器对象设置源代码。
- glCompileShader:编译着色器对象。
- glGetShaderiv:获取着色器对象的编译状态。
- glAttachShader:将着色器对象附加到着色器程序。
- glLinkProgram:链接着色器程序。
- glGetProgramiv:获取着色器程序的链接状态。
- glDeleteShader:删除着色器对象。
- glGenTextures:生成纹理对象。
- glBindTexture:绑定纹理对象。
- glTexParameteri:设置纹理参数,如过滤模式、环绕模式等。
- glTexImage2D:上传纹理数据。
- glGenFramebuffers:生成帧缓冲区对象。
- glBindFramebuffer:绑定帧缓冲区对象。
- glFramebufferTexture2D:将纹理对象附加到帧缓冲区对象。
- glDrawArrays:绘制顶点数组,渲染图形。
- glDrawElements:绘制索引数组,渲染图形。
- glEnable:启用某个OpenGL功能,如深度测试、剔除、混合等。
- glDisable:禁用某个OpenGL功能。
- glBlendFunc:设置混合函数。
- glCullFace:设置剔除面的模式。
- glClearColor:设置清除颜色缓冲区时使用的颜色。
- glClear:清除颜色缓冲区、深度缓冲区和/或模板缓冲区。
5. 开启调试
GLSurfaceView.setDebugFlags() 方法可以激活 log或者错误检测,它们可以帮助调试 OpenGL ES 调用。具体使用时,在 GLSurfaceView 的构造函数中,调用 setRender() 之前调用GLSurfaceView.setDebugFlags()就可以了。下面是个例子:
class MyGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {
init {
// 设置 OpenGL ES 版本
setEGLContextClientVersion(2)
//打开调试和日志
setDebugFlags(DEBUG_CHECK_GL_ERROR or DEBUG_LOG_GL_CALLS)
// 设置渲染器
val renderer = MyGLRenderer()
setRenderer(renderer)
}
}
6. 源码下载
本文源码下载 : Android使用GlSurfaceView和OpenGL绘制三角形 Demo


















