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