OpenGL ES for Android 环境搭建
在Android上运行OpenGL ES程序需要用到GLSurfaceView控件,GLSurfaceView继承自SurfaceView并实现了GLThread,通过OpenGL ES进行绘制。
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= tools:context=.MainActivity"> <android.opengl.GLSurfaceView android:id=@+id/glSurfaceView"/> </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(2) 设置renderer glSurfaceView.setRenderer(MyRenderer(context = baseContext)) 设置渲染模式 glSurfaceView.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY } override fun onResume() { super.onResume() glSurfaceView.onResume() } 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表示一直渲染。 Renderer必须实现GLSurfaceView.Renderer接口,并实现onSurfaceCreated,onDrawFrame,onSurfaceChanged方法,OpenGL ES的渲染工作由此Renderer实现。 MyRenderer的实现: 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,onSurfaceChanged方法说明如下:
在OpenGL ES中Shader和Program是两个非常重要的概念,Program需要Vertex Shader(顶点Shader和Fragment Shader(片段Shader),Renderer的渲染就是在执行Program。
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; main() { gl_FragColor = vec4(1,0,1)">1); } ? 上面代码表示顶点区域内绘制为红色,vec4内的值表示r,g,b,a。 private fun compileShader(shaderType: Int,shaderSource: String): Int { 创建一个空shader var shaderHandle: Int = GLES20.glCreateShader(shaderType) if (shaderHandle != 0) { 加载shader源码 GLES20.glShaderSource(shaderHandle,shaderSource) 编译shader GLES20.glCompileShader(shaderHandle) val compileStatus = IntArray(检查shader状态 GLES20.glGetShaderiv(shaderHandle,GLES20.GL_COMPILE_STATUS,compileStatus,1)">if (compileStatus[0] == 输入shader异常日志 Log.e(TAG,1)">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。 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,1)">if (linkStatus[Error link program:${GLES20.glGetProgramInfoLog(programHandle)}删除program GLES20.glDeleteProgram(programHandle) programHandle = if (programHandle == Error create program programHandle } 最终返回program的句柄,这2步是固定的,因为我们将其封装为工具类GLTools,供以后使用。 fun createProgram() { var vertexCode = AssetsUtils.readAssetsTxt( context = context,filePath = glsl/triangle_vertex.glsl ) var fragmentCode =glsl/triangle_fragment.glsl ) mProgramHandle = GLTools.createAndLinkProgram(vertexCode,fragmentCode) } ?
val loc = GLES20.glGetAttribLocation(mProgramHandle,1)">vPosition")
? 顶点数据需要了解顶点坐标系统,如下图: 顶点坐标轴以屏幕中心为原点(0,0),z轴的正方向为穿透屏幕指向外面。三角形的顶点坐标设置如下: var vertexBuffer = GLTools.array2Buffer( floatArrayOf( 0.0f,1)">0.5f, top - bottom left 0.0f bottom right ) ) ?
fun array2Buffer(array: FloatArray): FloatBuffer { val bb = ByteBuffer.allocateDirect(array.size * 4) bb.order(ByteOrder.nativeOrder()) var buffer = bb.asFloatBuffer() buffer.put(array) buffer.position( buffer } ? 创建OpenGL ES绘制窗口通常是在onSurfaceChanged中设置, GLES20.glViewport( |