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? = null
private 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 = 0
private var mHeight = 0
@Volatile
private var mRunning = true
@Volatile
private var mSizeChanged = false
override 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 = width
mHeight = height
mSizeChanged = true
}
fun release() {
mRunning = false
mEGLRender.onSurfaceDestroyed()
mEGLHelper.releaseEGL()
interrupt()
}
}
}
EGL
工具类代码
class MyEGLHelper {
private lateinit var mEGL: EGL10
private lateinit var mEGLDisplay: EGLDisplay
private lateinit var mEGLContext: EGLContext
private lateinit var mEGLSurface: EGLSurface
fun initEGL(surface: Surface) {
if (::mEGL.isInitialized &&
::mEGLDisplay.isInitialized &&
::mEGLContext.isInitialized &&
::mEGLSurface.isInitialized) {
Log.e("yang", "EGL already initialized")
return
}
mEGL = EGLContext.getEGL() as EGL10
mEGLDisplay = 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? = null
override 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 = -1
private var NO_OFFSET = 0
private val VERTEX_POS_DATA_SIZE = 3
private val TEXTURE_POS_DATA_SIZE = 2
private 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 = 1f
private var mSurfaceWidth = 0
private var mSurfaceHeight = 0
val 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 es
uniform mat4 uMVPMatrix;
in vec4 aPosition;
in vec2 aTexCoord;
out vec2 vTexCoord;
void main() {
gl_Position = uMVPMatrix * aPosition;
vTexCoord = aTexCoord;
}""".trimIndent()
val fragmentShaderCode = """#version 300 es
precision 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 = width
mSurfaceHeight = 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 = width
mSurfaceHeight = height
cube.angle += cube.rotationSpeed
cube.angle %= 360
Matrix.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 > height
mViewPortRatio = if (isLandscape) width.toFloat() / height else height.toFloat() / width
val radius = sqrt(1f + mViewPortRatio * mViewPortRatio)
val near = 0.1f
val far = near + 2 * radius
val 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
}
}
}
效果图
