OpenGL ES是OpenGL(Open Graphics Library)的精简子集,是以手持和嵌入式设备为目标的高级3D图形API,我们开始尝试在Android开发中通过OpenGL ES快速绘制形状
创建GLSurfaceView
GLSurfaceView就是OpenGL的视图容器
package com.kotlin.demo.gl
import android.content.Context
import android.opengl.GLSurfaceView
import android.util.AttributeSet
class MyGLSurfaceView(context: Context?,attrs: AttributeSet?) : GLSurfaceView(context,attrs) {
val mRenderer:MyGLRenderer
init {
setEGLContextClientVersion(2)
mRenderer = MyGLRenderer()
setRenderer(mRenderer)
// RENDERMODE_WHEN_DIRTY:surface在创建后调用一次, 或者在开发者主动调用MyGLSurfaceView的requestRender时才会调用GLSurfaceView.Renderer去绘制
//RENDERMODE_CONTINUOUSLY:自动连续调用GLSurfaceView.Renderer去绘制,绘制时会执行GLSurfaceView.Renderer的onDrawFrame()方法;
renderMode = RENDERMODE_WHEN_DIRTY
}
}
定义绘制多边形
OpenGL使用FloatBuffer来管理顶点数据提高效率,所以需要将坐标系转换成FloatBuffer对象
package com.kotlin.demo.gl
import android.opengl.GLES20
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
class ShapeGL(pointsList:ArrayList<FloatArray>,colorList:ArrayList<FloatArray>) {
val TAG = "ShapeGL"
val mPointsList = pointsList
val mColorList = colorList
var vertexBuffers = mutableListOf<FloatBuffer>()
/**
* 顶点着色器
*/
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;" +
"}"
/**
* 着色器程序ID引用
*/
private var mProgram: Int
init {
for (points in mPointsList){
var vertexBuffer: FloatBuffer = ByteBuffer.allocateDirect(points.size * 4)
.order(ByteOrder.nativeOrder()).asFloatBuffer().apply {
// 把坐标添加到FloatBuffer
put(points)
// 设置buffer的位置为起始点0
position(0)
}
vertexBuffers.add(vertexBuffer)
}
// 编译顶点着色器和片段着色器
val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
// glCreateProgram函数创建一个着色器程序,并返回新创建程序对象的ID引用
mProgram = GLES20.glCreateProgram().also {
// 把顶点着色器添加到程序对象
GLES20.glAttachShader(it, vertexShader)
// 把片段着色器添加到程序对象
GLES20.glAttachShader(it, fragmentShader)
// 连接并创建一个可执行的OpenGL ES程序对象
GLES20.glLinkProgram(it)
}
}
private fun loadShader(type: Int, shaderCode: String): Int {
// glCreateShader函数创建一个顶点着色器或者片段着色器,并返回新创建着色器的ID引用
val shader = GLES20.glCreateShader(type)
// 把着色器和代码关联,然后编译着色器
GLES20.glShaderSource(shader, shaderCode)
GLES20.glCompileShader(shader)
return shader
}
val COORDS_PER_VERTEX = 3
private val vertexStride: Int = COORDS_PER_VERTEX * 4 // 4 bytes per vertex
fun draw() {
// 激活着色器程序,把程序添加到OpenGL ES环境
GLES20.glUseProgram(mProgram)
// 获取顶点着色器中的vPosition变量
val position = GLES20.glGetAttribLocation(mProgram, "vPosition")
// 允许操作顶点对象position
GLES20.glEnableVertexAttribArray(position)
// 将顶点数据传递给position指向的vPosition变量;将顶点属性与顶点缓冲对象关联
for (i in 0 until vertexBuffers.size){
GLES20.glVertexAttribPointer(
position, COORDS_PER_VERTEX, GLES20.GL_FLOAT,
false, vertexStride, vertexBuffers[i])
// 获取片段着色器中的vColor变量
val colorHandle = GLES20.glGetUniformLocation(mProgram, "vColor")
// 通过colorHandle设置绘制的颜色值
GLES20.glUniform4fv(colorHandle, 1, mColorList[i], 0)
// 绘制顶点数组;
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, mPointsList[i].size/ COORDS_PER_VERTEX)
}
// 操作完后,取消允许操作顶点对象position
GLES20.glDisableVertexAttribArray(position)
}
}
创建Renderer
Renderer负责实际的绘制工作,这里定义两个图形进行绘制,传入两组坐标和颜色
package com.kotlin.demo.gl
import android.opengl.GLES20
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
class MyGLRenderer:GLSurfaceView.Renderer {
private lateinit var shapeGL: ShapeGL
var points = floatArrayOf(
-0.5f, 0.3f, 0f,
-0.7f, -0.2f, 0f,
-0f, -0f, 0f,
0.7f, -0.3f, 0f,
0.9f, 0.2f, 0f,
)
val color = floatArrayOf(148.0f, 0.0f, 211f,1.0f)
var points1 = floatArrayOf(
-0.5f, 0.9f, 0f,
-0.7f, 0.5f, 0f,
0.7f, 0.6f, 0f
)
val color1 = floatArrayOf(0.0f, 0.0f, 255f,1.0f)
override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
// 设置背景色
GLES20.glClearColor(0.0f, 255.0f, 255.0f, 1.0f)
shapeGL = ShapeGL(arrayListOf(points,points1), arrayListOf(color,color1))
}
override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
// 上面提到OpenGL使用的是标准化设备坐标;
GLES20.glViewport(0, 0, width, height)
}
override fun onDrawFrame(gl: GL10?) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
shapeGL.draw()
}
}
引用OpenGL
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.kotlin.demo">
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<uses-permission android:name="android.permission.INTERNET" />
<?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="com.kotlin.GLActivity">
<com.kotlin.demo.gl.MyGLSurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>