一、GLSurfaceView
GLSurfaceView 类提供了用于管理 EGL 上下文、在线程间通信以及与 activity 生命周期交互的辅助程序类。您无需使用 GLSurfaceView 即可使用 GLES。
例如,GLSurfaceView 会创建一个渲染线程,并在线程上配置 EGL 上下文。当 Activity 暂停时,状态将自动清除。大多数应用无需了解有关 EGL 的任何信息即可通过 GLSurfaceView 来使用GLES。
二、绘制三角形
OpenGl绘制图形的基本步
-
使用GLSurfaceView创建OpenGl ES环境
-
编译着色器程序(顶点着色器+片元着色器)
创建shader对象,GLES30.glCreateShader(type)
指定Shader源码,GLES30.glShaderSource()
编译Shader,GLES30.glCompileShader(shader)
-
链接OpenGL程序
创建OpenGl程序,GLES30.glCreateProgram()
绑定Shader到OpenGL程序,GLES30.glAttachShader()
链接OpenGL程序,GLES30.glLinkProgram()
-
使用OpenGL程序
使用OpenGL程序,GLES30.glUseProgram()
传递顶点数据
传递片元数据
绘制三角形大体步骤
1、使用GLSurfaceView作为布局展示
2、对GLSurfaceView初始化,初始化时需要使用到GLSurfaceView.Renderer。
3、GLSurfaceView.Renderer中回调
- onSurfaceCreated
- onSurfaceChanged
- onDrawFrame
在上面三个函数中进行处理绘制三角形逻辑。
1、使用GLSurfaceView作为布局展示
kotlin
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.opengl.GLSurfaceView
android:id="@+id/glSurfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
2、对GLSurfaceView初始化,初始化时需要使用到GLSurfaceView.Renderer。
下面的代码是在Activity中的。
kotlin
private fun initGlSurfaceView() {
// 设置OpenGL版本号
glSurfaceView?.setEGLContextClientVersion(2)
val render = DemoGlRender()
// 设置openGl的Render
glSurfaceView?.setRenderer(render)
// 默认渲染方式为RENDERMODE_CONTINUOUSLY
// 当设置为RENDERMODE_WHEN_DIRTY时只有在创建和调用requestRender()时才会刷新。
glSurfaceView?.renderMode = RENDERMODE_WHEN_DIRTY
// 当设置为RENDERMODE_CONTINUOUSLY时渲染器会不停地渲染场景
// glSurfaceView?.renderMode = RENDERMODE_CONTINUOUSLY
}
override fun onResume() {
super.onResume()
glSurfaceView?.onResume()
}
override fun onPause() {
super.onPause()
glSurfaceView?.onPause()
}
默认渲染方式为RENDERMODE_CONTINUOUSLY
-
RENDERMODE_CONTINUOUSLY模式
当设置为RENDERMODE_CONTINUOUSLY时渲染器会不停地渲染场景,
-
RENDERMODE_WHEN_DIRTY模式
当设置为RENDERMODE_WHEN_DIRTY时只有在创建和调用requestRender()时才会刷新。
3、GLSurfaceView.Renderer实现类中处理onSurfaceCreated、onSurfaceChanged、onDrawFrame
onSurfaceCreated:设置屏幕颜色、加载shader程序、挂载program
onDrawFrame:激活顶点坐标、绑定定点数据、绑定颜色数据、绘制
kotlin
class DemoGlRender : GLSurfaceView.Renderer {
/*
* 顶点位置程序
*/
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;" +
"}"
/**
* 三角形顶点位置
*/
private val triangleCoors = floatArrayOf(
0.5f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f
)
/**
* 三角形颜色值
*/
private val color = floatArrayOf(
1.0f, 1.0f, 1.0f, 1.0f
)
private var program: Int? = null
private var vertexBuffer: FloatBuffer? = null
override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
// 设置背景颜色
GLES20.glClearColor(1f, 0f, 0f, 1.0f)
// 清理缓存
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
createFloatBuffer()
// 创建定点着色程序
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
// 创建片元着色程序
val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
if (vertexShader == 0 || fragmentShader == 0) {
return;
}
linkProgram(vertexShader, fragmentShader)
}
private fun createFloatBuffer() {
// 申请物理层空间
val byteBuffer = ByteBuffer.allocateDirect(triangleCoors.size * 4).apply {
this.order(ByteOrder.nativeOrder())
}
// 坐标数据转换
vertexBuffer = byteBuffer.asFloatBuffer()
vertexBuffer?.put(triangleCoors, 0, triangleCoors.size)
vertexBuffer?.position(0)
}
private fun linkProgram(vertexShader: Int, fragmentShader: Int) {
// 创建空的opengl es 程序
program = GLES20.glCreateProgram()
program?.let {
// 将顶点着色器加入程序
GLES20.glAttachShader(it, vertexShader)
// 将片元着色器加入程序
GLES20.glAttachShader(it, fragmentShader)
// 链接到着色器程序
GLES20.glLinkProgram(it)
// 将程序加入到opengl30环境中
GLES20.glUseProgram(it)
val info = GLES20.glGetProgramInfoLog(it)
// 打印链接程序日志
Log.e("wdf", "info==" + info)
}
}
override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
// 设置绘制窗口
GLES20.glViewport(0, 0, width, height)
}
override fun onDrawFrame(p0: GL10?) {
program ?: return
if (program == 0) {
return
}
program?.let {
GLES20.glClear(GLES30.GL_COLOR_BUFFER_BIT)
GLES20.glUseProgram(it)
// 获取顶点着色器的vPosition,与程序中声明vPosition变量名保持一致
val vPosition = GLES20.glGetAttribLocation(it, "vPosition")
GLES20.glEnableVertexAttribArray(vPosition)
// 3*4是指 跨3个读下一个顶点
GLES20.glVertexAttribPointer(0, 3, GLES20.GL_FLOAT, false, 3 * 4, vertexBuffer)
// 设置三角形颜色,与程序中声明vColor变量名保持一致
val vColor = GLES20.glGetUniformLocation(it, "vColor")
GLES20.glUniform4fv(vColor, 1, color, 0)
// 绘制
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3)
GLES20.glDisableVertexAttribArray(vPosition)
}
}
/**
* 创建shader,加载shader程序
*/
private fun loadShader(type: Int, shaderCode: String): Int {
val shader = GLES20.glCreateShader(type)
GLES20.glShaderSource(shader, shaderCode)
GLES20.glCompileShader(shader)
return shader
}
}
三、为什么要给GLSurfaceView设置GLSurfaceView.Renderer
在这里插入图片描述