XML
文件
<?xml version="1.0" encoding="utf-8"?>
<com.example.myapplication.MySurfaceView xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent" />
Activity
代码
class MainActivity7 : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main7)}
}
data class CubeInfo(var x: Float, var y: Float, var angle: Float, var rotationSpeed: Float, var scale : Float
)
private val cubes = arrayOf(CubeInfo(x = -1.0f, y = 1.0f, angle = 0f, rotationSpeed = 0.3f, scale = 0.3f),CubeInfo(x = 1.0f, y = 1.0f, angle = 45f, rotationSpeed = 0.5f, scale = 0.4f),CubeInfo(x = 0f, y = 0f, angle = 90f, rotationSpeed = 0.7f, scale = 0.2f),CubeInfo(x = -1.0f, y = -1.0f, angle = 135f, rotationSpeed = 0.4f, scale = 0.5f),CubeInfo(x = 1.0f, y = -1.0f, angle = 180f, rotationSpeed = 0.2f, scale = 0.7f)
)
SurfaceView
代码+渲染线程代码
class MySurfaceView(context: Context, attrs: AttributeSet) : SurfaceView(context, attrs),SurfaceHolder.Callback {init {holder.addCallback(this)}private var mEGLThread: MyEGLThread? = nullprivate var mEGLHelper = MyEGLHelper()private var mEGLRender = MyEGLRender(context)override fun surfaceCreated(holder: SurfaceHolder) {mEGLThread = MyEGLThread(holder.surface).apply {start()}}override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {mEGLThread?.changeSize(width, height)}override fun surfaceDestroyed(holder: SurfaceHolder) {mEGLThread?.release()}inner class MyEGLThread(private val mSurface: Surface) : Thread() {private var mWidth = 0private var mHeight = 0@Volatileprivate var mRunning = true@Volatileprivate var mSizeChanged = falseoverride fun run() {super.run()try {mEGLHelper.initEGL(mSurface)mEGLRender.onSurfaceCreated()while (mRunning) {if (mSizeChanged) {mEGLRender.onSurfaceChanged(mWidth, mHeight)mSizeChanged = false}mEGLRender.onDrawFrame()mEGLHelper.swapBuffer()}} catch (e: Exception) {Log.e("yang", "EGL thread error ${e.message}")}}fun changeSize(width: Int, height: Int) {mWidth = widthmHeight = heightmSizeChanged = true}fun release() {mRunning = falsemEGLRender.onSurfaceDestroyed()mEGLHelper.releaseEGL()interrupt()}}
}
EGL
工具类代码
class MyEGLHelper {private lateinit var mEGL: EGL10private lateinit var mEGLDisplay: EGLDisplayprivate lateinit var mEGLContext: EGLContextprivate lateinit var mEGLSurface: EGLSurfacefun initEGL(surface: Surface) {if (::mEGL.isInitialized &&::mEGLDisplay.isInitialized &&::mEGLContext.isInitialized &&::mEGLSurface.isInitialized) {Log.e("yang", "EGL already initialized")return}mEGL = EGLContext.getEGL() as EGL10mEGLDisplay = mEGL.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)takeIf { mEGLDisplay == EGL10.EGL_NO_DISPLAY }?.apply {throw RuntimeException("eglGetDisplay failed")}val version = IntArray(2)takeIf { !mEGL.eglInitialize(mEGLDisplay, version) }?.apply {throw RuntimeException("eglInitialize failed")}val display_attribute_list = intArrayOf(EGL_RED_SIZE, 8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE, 8,EGL_DEPTH_SIZE, 8,EGL_STENCIL_SIZE, 4,EGL_NONE)val num_config = IntArray(1)takeIf {!mEGL.eglChooseConfig(mEGLDisplay,display_attribute_list,null,0,num_config)}?.apply {throw RuntimeException("eglChooseConfig failed")}takeIf { num_config[0] <= 0 }?.apply {throw RuntimeException("eglChooseConfig#1 failed")}val eglConfigs = arrayOfNulls<EGLConfig>(num_config[0])takeIf {!mEGL.eglChooseConfig(mEGLDisplay,display_attribute_list,eglConfigs,num_config[0],num_config)}?.apply {throw RuntimeException("eglChooseConfig#2 failed")}val context_display_list = intArrayOf(EGL_CONTEXT_CLIENT_VERSION, 3,EGL_NONE)takeIf { ::mEGLContext.isInitialized == false }?.apply {mEGLContext = mEGL.eglCreateContext(mEGLDisplay,eglConfigs[0],EGL10.EGL_NO_CONTEXT,context_display_list)}takeIf { mEGLContext == EGL10.EGL_NO_CONTEXT }?.apply {throw RuntimeException("eglCreateContext failed")}takeIf { ::mEGLSurface.isInitialized == false }?.apply {mEGLSurface = mEGL.eglCreateWindowSurface(mEGLDisplay, eglConfigs[0], surface, null)}takeIf { mEGLSurface == EGL10.EGL_NO_SURFACE }?.apply {throw RuntimeException("eglCreateWindowSurface failed")}takeIf { !mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext) }?.apply {throw RuntimeException("eglMakeCurrent failed")}}fun releaseEGL() {takeIf { ::mEGL.isInitialized }?.apply {mEGL.eglMakeCurrent(mEGLDisplay,EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_SURFACE,EGL10.EGL_NO_CONTEXT)mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface)mEGL.eglDestroyContext(mEGLDisplay, mEGLContext)mEGL.eglTerminate(mEGLDisplay)}}fun swapBuffer() {takeIf { ::mEGL.isInitialized && ::mEGLDisplay.isInitialized }?.apply {takeIf { !mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface) }?.apply {throw RuntimeException("eglSwapBuffers failed")}}}
}
渲染器接口
interface EGLRender {fun onSurfaceCreated()fun onSurfaceChanged(width: Int, height: Int)fun onDrawFrame()fun onSurfaceDestroyed()
}
渲染器实现类
class MyEGLRender(private val mContext: Context) : EGLRender {var mDrawData: MyDrawData2? = nulloverride fun onSurfaceCreated() {GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)mDrawData = MyDrawData2().apply {initTexture0(mContext, R.drawable.picture)initShaderProgram()initVertexBuffer()}}override fun onSurfaceChanged(width: Int, height: Int) {GLES30.glViewport(0, 0, width, height)mDrawData?.setSurfaceSize(width, height)}override fun onDrawFrame() {GLES30.glEnable(GLES30.GL_DEPTH_TEST)GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT)mDrawData?.drawCurrentOutput()}override fun onSurfaceDestroyed() {mDrawData?.release()}
}
渲染器实现类需要的绘制数据
class MyDrawData2 {private var mProgram: Int = -1private var NO_OFFSET = 0private val VERTEX_POS_DATA_SIZE = 3private val TEXTURE_POS_DATA_SIZE = 2private val STRIDE = (VERTEX_POS_DATA_SIZE + TEXTURE_POS_DATA_SIZE) * 4 private var mVAO = IntArray(1)private var mVBO = IntArray(1) private var mTextureID = IntArray(1)private var mMVPMatrix = FloatArray(16)private val mProjectionMatrix = FloatArray(16)private val mViewMatrix = FloatArray(16)private val mModelMatrix = FloatArray(16)private var mViewPortRatio = 1fprivate var mSurfaceWidth = 0private var mSurfaceHeight = 0val vertexData = floatArrayOf(-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.0f, 1.0f,-0.5f, 0.5f, 0.5f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, 0.5f, -0.5f, 1.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, 0.5f, 0.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,0.5f, -0.5f, -0.5f, 1.0f, 1.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,0.5f, -0.5f, 0.5f, 1.0f, 0.0f,-0.5f, -0.5f, 0.5f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 0.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f,0.5f, 0.5f, -0.5f, 1.0f, 1.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,0.5f, 0.5f, 0.5f, 1.0f, 0.0f,-0.5f, 0.5f, 0.5f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, 0.0f, 1.0f)val vertexDataBuffer = ByteBuffer.allocateDirect(vertexData.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexData).position(NO_OFFSET)fun initShaderProgram() {val vertexShaderCode = """#version 300 esuniform mat4 uMVPMatrix; in vec4 aPosition; in vec2 aTexCoord; out vec2 vTexCoord; void main() {gl_Position = uMVPMatrix * aPosition;vTexCoord = aTexCoord;}""".trimIndent()val fragmentShaderCode = """#version 300 esprecision mediump float;uniform sampler2D uTexture_0;in vec2 vTexCoord;out vec4 fragColor;void main() {fragColor = texture(uTexture_0, vTexCoord);}""".trimIndent()val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)val fragmentShader = LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)mProgram = GLES30.glCreateProgram()GLES30.glAttachShader(mProgram, vertexShader)GLES30.glAttachShader(mProgram, fragmentShader)GLES30.glLinkProgram(mProgram)GLES30.glDeleteShader(vertexShader)GLES30.glDeleteShader(fragmentShader)}fun initVertexBuffer() {GLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)GLES30.glBindVertexArray(mVAO[0])GLES30.glGenBuffers(mVBO.size, mVBO, NO_OFFSET)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertexData.size * 4,vertexDataBuffer,GLES30.GL_STATIC_DRAW)val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")GLES30.glEnableVertexAttribArray(positionHandle)GLES30.glVertexAttribPointer(positionHandle,VERTEX_POS_DATA_SIZE,GLES30.GL_FLOAT,false,STRIDE, NO_OFFSET )val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")GLES30.glEnableVertexAttribArray(textureHandle)GLES30.glVertexAttribPointer(textureHandle,TEXTURE_POS_DATA_SIZE,GLES30.GL_FLOAT,false,STRIDE, VERTEX_POS_DATA_SIZE * 4 )GLES30.glBindVertexArray(0)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)}fun drawSomething(program: Int, mvpMatrix: FloatArray) {val matrixHandle = GLES30.glGetUniformLocation(program, "uMVPMatrix")GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mvpMatrix, NO_OFFSET)GLES30.glBindVertexArray(mVAO[0])GLES30.glDrawArrays(GLES30.GL_TRIANGLES, NO_OFFSET, vertexData.size / (VERTEX_POS_DATA_SIZE + TEXTURE_POS_DATA_SIZE))GLES30.glBindVertexArray(0)}fun setSurfaceSize(width: Int, height: Int){mSurfaceWidth = widthmSurfaceHeight = height}fun resetMatrix() {Matrix.setIdentityM(mModelMatrix, NO_OFFSET)Matrix.setIdentityM(mViewMatrix, NO_OFFSET)Matrix.setIdentityM(mProjectionMatrix, NO_OFFSET)Matrix.setIdentityM(mMVPMatrix, NO_OFFSET)}fun computeMVPMatrix(width: Int, height: Int, cube: CubeInfo) {mSurfaceWidth = widthmSurfaceHeight = heightcube.angle += cube.rotationSpeedcube.angle %= 360Matrix.scaleM(mModelMatrix, NO_OFFSET, cube.scale, cube.scale, cube.scale)Matrix.translateM(mModelMatrix, NO_OFFSET, cube.x, cube.y, 0f)Matrix.rotateM(mModelMatrix, NO_OFFSET, cube.angle, 0.5f, 0.5f, 0f)val isLandscape = width > heightmViewPortRatio = if (isLandscape) width.toFloat() / height else height.toFloat() / widthval radius = sqrt(1f + mViewPortRatio * mViewPortRatio)val near = 0.1fval far = near + 2 * radiusval distance = near / (near + radius)Matrix.setLookAtM(mViewMatrix, NO_OFFSET,0f, 0f, near + radius, 0f, 0f, 0f, 0f, 1f, 0f )Matrix.frustumM(mProjectionMatrix, NO_OFFSET,if (isLandscape) (-mViewPortRatio * distance) else (-1f * distance), if (isLandscape) (mViewPortRatio * distance) else (1f * distance), if (isLandscape) (-1f * distance) else (-mViewPortRatio * distance), if (isLandscape) (1f * distance) else (mViewPortRatio * distance), near, far )Matrix.multiplyMM(mMVPMatrix,NO_OFFSET,mViewMatrix,NO_OFFSET,mModelMatrix,NO_OFFSET)Matrix.multiplyMM(mMVPMatrix,NO_OFFSET,mProjectionMatrix,NO_OFFSET,mMVPMatrix,NO_OFFSET)Matrix.scaleM(mMVPMatrix,NO_OFFSET,1f,-1f,1f,)}fun loadTexture(context: Context, resourceId: Int): Int {val textureId = IntArray(1)GLES30.glGenTextures(1, textureId, 0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId[0])GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_LINEAR) GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MAG_FILTER,GLES30.GL_LINEAR) GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_CLAMP_TO_EDGE) GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_CLAMP_TO_EDGE) val options = BitmapFactory.Options().apply {inScaled = false }val bitmap = BitmapFactory.decodeResource(context.resources, resourceId, options)GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0)bitmap.recycle()GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)Log.e("yang","loadTexture: 纹理加载成功 bitmap.width:${bitmap.width} bitmap.height:${bitmap.height}")return textureId[0]}fun enableTexture0(program: Int, id: Int) {GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, id)val textureSampleHandle = GLES30.glGetUniformLocation(program, "uTexture_0")if (textureSampleHandle != -1) {GLES30.glUniform1i(textureSampleHandle, 0)}}fun disableTexture0() {GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)}fun initTexture0(context: Context, resourceId: Int) {mTextureID[0] = loadTexture(context, resourceId)}fun drawCurrentOutput() {val state = saveGLState()try {GLES30.glUseProgram(mProgram)enableTexture0(mProgram, mTextureID[0])for (cube in cubes) {resetMatrix()computeMVPMatrix(mSurfaceWidth, mSurfaceHeight, cube)drawSomething(mProgram, mMVPMatrix)}disableTexture0()} finally {restoreGLState(state)}}private fun saveGLState(): GLState {val viewport = IntArray(4)val program = IntArray(1)val framebuffer = IntArray(1)GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewport, 0)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, program, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, framebuffer, 0)return GLState(viewport, program[0], framebuffer[0])}private fun restoreGLState(state: GLState) {GLES30.glViewport(state.viewport[0],state.viewport[1],state.viewport[2],state.viewport[3])GLES30.glUseProgram(state.program)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, state.framebuffer)}fun release(){if (mVAO[0] != 0) {GLES30.glDeleteVertexArrays(1, mVAO, 0)mVAO[0] = 0}if (mVBO[0] != 0) {GLES30.glDeleteBuffers(1, mVBO, 0)mVBO[0] = 0}if (mTextureID[0] != 0) {GLES30.glDeleteTextures(1, mTextureID, 0)mTextureID[0] = 0}if (mProgram != -1) {GLES30.glDeleteProgram(mProgram)mProgram = -1}vertexDataBuffer.clear()}data class GLState(val viewport: IntArray,val program: Int,val framebuffer: Int)object LoadShaderUtil {fun loadShader(type: Int, source: String): Int {val shader = GLES30.glCreateShader(type)GLES30.glShaderSource(shader, source)GLES30.glCompileShader(shader)return shader}}
}
效果图
