XML
文件
<com.example.myapplication.EGLSurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Activity
代码
class MainActivity8 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main8)
}
}
SurfaceView
代码
class EGLSurfaceView(context: Context, attrs: AttributeSet? = null) : SurfaceView(context, attrs) {
private val renderer = EGLRenderer(context)
init {
holder.addCallback(renderer.surfaceCallback)
}
fun setRenderStrategy(strategy: RenderStrategy) {
renderer.setRenderStrategy(strategy)
}
}
渲染器代码
class EGLRenderer(context: Context) {
private val strategy: RenderStrategy = MyRenderStrategy(context)
private val engine: EGLRenderEngine = EGLRenderEngine(strategy)
fun setRenderStrategy(strategy: RenderStrategy) {
}
val surfaceCallback = object : SurfaceHolder.Callback {
override fun surfaceCreated(holder: SurfaceHolder) {
engine.onSurfaceCreated(holder.surface)
}
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
engine.onSurfaceChanged(width, height)
}
override fun surfaceDestroyed(holder: SurfaceHolder) {
engine.onSurfaceDestroyed()
}
}
}
渲染引擎代码
class EGLRenderEngine(private val strategy: RenderStrategy) {
private var mEGLEnvironment: EGLEnvironment? = null
private var mRenderThread: RenderThread? = null
fun onSurfaceCreated(surface: Surface) {
mRenderThread = RenderThread(surface, strategy).apply {
start()
}
}
fun onSurfaceChanged(width: Int, height: Int) {
mRenderThread?.updateSize(width, height)
}
fun onSurfaceDestroyed() {
mRenderThread?.shutdown()
mRenderThread = null
}
private inner class RenderThread(
private val surface: Surface,
private val strategy: RenderStrategy
) : Thread() {
@Volatile private var running = true
@Volatile private var sizeChanged = false
private var width = 0
private var height = 0
fun updateSize(width: Int, height: Int) {
this.width = width
this.height = height
sizeChanged = true
}
fun shutdown() {
running = false
interrupt()
strategy.onSurfaceDestroyed()
mEGLEnvironment?.release()
}
override fun run() {
try {
mEGLEnvironment = EGLEnvironmentBuilder().build(surface)
strategy.onSurfaceCreated()
while (running) {
if (sizeChanged) {
strategy.onSurfaceChanged(width, height)
sizeChanged = false
}
strategy.onDrawFrame()
mEGLEnvironment?.swapBuffers()
}
} catch (e: Exception) {
Log.e("RenderEngine", "Render thread error: ${e.message}")
}
}
}
}
自定义渲染策略代码
interface RenderStrategy {
fun onSurfaceCreated()
fun onSurfaceChanged(width: Int, height: Int)
fun onDrawFrame()
fun onSurfaceDestroyed()
}
class MyRenderStrategy(private val context: Context) : RenderStrategy {
var mDrawData: EGLDrawData? = null
override fun onSurfaceCreated() {
GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)
mDrawData = EGLDrawData().apply {
initTexture0(context, R.drawable.picture)
initShaderProgram()
initOutlineShaderProgram()
initVertexBuffer()
}
}
override fun onSurfaceChanged(width: Int, height: Int) {
GLES30.glViewport(0, 0, width, height)
mDrawData?.setSurfaceSize(width, height)
}
override fun onDrawFrame() {
mDrawData?.drawCurrentOutput()
}
override fun onSurfaceDestroyed() {
mDrawData?.release()
}
}
搭建EGL
环境代码
class EGLEnvironmentBuilder(private val factory: EGLComponentFactory = DefaultEGLFactory()) {
private lateinit var mEGL: EGL10
private lateinit var mEGLDisplay: EGLDisplay
private lateinit var mEGLConfig: EGLConfig
private lateinit var mEGLContext: EGLContext
private lateinit var mEGLSurface: EGLSurface
fun build(surface: Surface): EGLEnvironment {
mEGL = factory.createEGL()
mEGLDisplay = factory.createEGLDisplay(mEGL)
mEGLConfig = factory.createEGLConfig(mEGL, mEGLDisplay)
mEGLContext = factory.createEGLContext(mEGL, mEGLDisplay, mEGLConfig)
mEGLSurface = factory.createEGLSurface(mEGL, mEGLDisplay, mEGLConfig, surface)
if (!mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext)) {
throw RuntimeException("eglMakeCurrent failed")
}
return EGLEnvironment(mEGL, mEGLDisplay, mEGLContext, mEGLSurface)
}
}
class EGLEnvironment(
val egl: EGL10,
val display: EGLDisplay,
val context: EGLContext,
val surface: EGLSurface
) {
fun swapBuffers() {
if (!egl.eglSwapBuffers(display, surface)) {
throw RuntimeException("eglSwapBuffers failed")
}
}
fun release() {
egl.eglMakeCurrent(display, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT)
egl.eglDestroySurface(display, surface)
egl.eglDestroyContext(display, context)
egl.eglTerminate(display)
}
}
interface EGLComponentFactory {
fun createEGL(): EGL10
fun createEGLDisplay(egl: EGL10): EGLDisplay
fun createEGLConfig(egl: EGL10, display: EGLDisplay): EGLConfig
fun createEGLContext(egl: EGL10, display: EGLDisplay, config: EGLConfig): EGLContext
fun createEGLSurface(egl: EGL10, display: EGLDisplay, config: EGLConfig, surface: Surface): EGLSurface
}
class DefaultEGLFactory : EGLComponentFactory {
override fun createEGL(): EGL10 = EGLContext.getEGL() as EGL10
override fun createEGLDisplay(egl: EGL10): EGLDisplay {
val eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
if (eglDisplay == EGL10.EGL_NO_DISPLAY) {
throw RuntimeException("eglGetDisplay failed")
}
val version = IntArray(2)
if (!egl.eglInitialize(eglDisplay, version)) {
throw RuntimeException("eglInitialize failed")
}
return eglDisplay
}
override fun createEGLConfig(egl: EGL10, display: EGLDisplay): EGLConfig {
val attributes = 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 numConfigs = IntArray(1)
egl.eglChooseConfig(display, attributes, null, 0, numConfigs)
if (numConfigs[0] <= 0) {
throw RuntimeException("No matching EGL configs")
}
val configs = arrayOfNulls<EGLConfig>(numConfigs[0])
egl.eglChooseConfig(display, attributes, configs, numConfigs[0], numConfigs)
return configs[0] ?: throw RuntimeException("No suitable EGL config found")
}
override fun createEGLContext(egl: EGL10, display: EGLDisplay, config: EGLConfig): EGLContext {
val contextAttrs = intArrayOf(
EGL_CONTEXT_CLIENT_VERSION, 3,
EGL_NONE
)
val eglContext = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, contextAttrs)
if (eglContext == EGL10.EGL_NO_CONTEXT) {
throw RuntimeException("eglCreateContext failed")
}
return eglContext
}
override fun createEGLSurface(egl: EGL10, display: EGLDisplay, config: EGLConfig, surface: Surface): EGLSurface {
val eglSurface = egl.eglCreateWindowSurface(display, config, surface, null)
if (eglSurface == EGL10.EGL_NO_SURFACE) {
throw RuntimeException("eglCreateWindowSurface failed")
}
return eglSurface
}
}
渲染数据代码
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)
)
class EGLDrawData {
private var mProgram: Int = -1
private var mOutlineProgram : 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 initOutlineShaderProgram() {
val vertexShaderCode = """#version 300 es
uniform mat4 uMVPMatrix;
in vec4 aPosition;
void main() {
gl_Position = uMVPMatrix * aPosition;
}
""".trimIndent()
val fragmentShaderCode = """#version 300 es
precision mediump float;
out vec4 fragColor;
void main() {
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
""".trimIndent()
val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
val fragmentShader = LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
mOutlineProgram = GLES30.glCreateProgram()
GLES30.glAttachShader(mOutlineProgram, vertexShader)
GLES30.glAttachShader(mOutlineProgram, fragmentShader)
GLES30.glLinkProgram(mOutlineProgram)
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.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT or GLES30.GL_STENCIL_BUFFER_BIT)
GLES30.glEnable(GLES30.GL_DEPTH_TEST)
GLES30.glEnable(GLES30.GL_STENCIL_TEST)
GLES30.glColorMask(false, false, false, false)
GLES30.glDepthMask(false)
GLES30.glStencilFunc(GLES30.GL_ALWAYS, 1, 0xFF)
GLES30.glStencilOp(GLES30.GL_KEEP, GLES30.GL_KEEP, GLES30.GL_REPLACE)
drawStencilRect()
GLES30.glColorMask(true, true, true, true)
GLES30.glDepthMask(true)
GLES30.glStencilFunc(GLES30.GL_EQUAL, 1, 0xFF)
GLES30.glStencilOp(GLES30.GL_KEEP, GLES30.GL_KEEP, GLES30.GL_KEEP)
GLES30.glUseProgram(mProgram)
enableTexture0(mProgram, mTextureID[0])
for (cube in cubes) {
resetMatrix()
computeMVPMatrix(mSurfaceWidth, mSurfaceHeight, cube)
drawSomething(mProgram, mMVPMatrix)
}
disableTexture0()
GLES30.glDisable(GLES30.GL_STENCIL_TEST)
GLES30.glDisable(GLES30.GL_DEPTH_TEST)
} finally {
restoreGLState(state)
}
}
fun drawStencilRect() {
GLES30.glUseProgram(mOutlineProgram)
val vertices = floatArrayOf(
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.5f, 0.5f, 0.0f,
-0.5f, 0.5f, 0.0f
)
val indices = shortArrayOf(0, 1, 2, 0, 2, 3)
val vertexBuffer = ByteBuffer.allocateDirect(vertices.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(vertices)
.position(0)
val indexBuffer = ByteBuffer.allocateDirect(indices.size * 2)
.order(ByteOrder.nativeOrder())
.asShortBuffer()
.put(indices)
.position(0)
val identityMatrix = FloatArray(16)
Matrix.setIdentityM(identityMatrix, 0)
val positionHandle = GLES30.glGetAttribLocation(mOutlineProgram, "aPosition")
val matrixHandle = GLES30.glGetUniformLocation(mOutlineProgram, "uMVPMatrix")
if (positionHandle == -1 || matrixHandle == -1) {
Log.e("yang", "着色器变量未找到: aPosition=$positionHandle, uMVPMatrix =$matrixHandle")
return
}
GLES30.glEnableVertexAttribArray(positionHandle)
GLES30.glVertexAttribPointer(positionHandle, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)
GLES30.glUniformMatrix4fv(matrixHandle, 1, false, identityMatrix, 0)
GLES30.glDrawElements(GLES30.GL_TRIANGLES, indices.size, GLES30.GL_UNSIGNED_SHORT, indexBuffer)
GLES30.glDisableVertexAttribArray(positionHandle)
}
private fun drawRect(program: Int, left: Float, bottom: Float, right: Float, top: Float) {
val rectVertices = floatArrayOf(
left, bottom, 0.0f,
right, bottom, 0.0f,
right, top, 0.0f,
left, top, 0.0f
)
val indices = shortArrayOf(0, 1, 2, 0, 2, 3)
val vertexBuffer = ByteBuffer.allocateDirect(rectVertices.size * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer()
.put(rectVertices)
.position(0)
val indexBuffer = ByteBuffer.allocateDirect(indices.size * 2)
.order(ByteOrder.nativeOrder())
.asShortBuffer()
.put(indices)
.position(0)
val identityMatrix = FloatArray(16)
Matrix.setIdentityM(identityMatrix, 0)
val positionHandle = GLES30.glGetAttribLocation(program, "aPosition")
val matrixHandle = GLES30.glGetUniformLocation(program, "uMVPMatrix")
GLES30.glEnableVertexAttribArray(positionHandle)
GLES30.glVertexAttribPointer(positionHandle, 3, GLES30.GL_FLOAT, false, 0, vertexBuffer)
GLES30.glUniformMatrix4fv(matrixHandle, 1, false, identityMatrix, 0)
GLES30.glDrawElements(GLES30.GL_TRIANGLES, indices.size, GLES30.GL_UNSIGNED_SHORT, indexBuffer)
GLES30.glDisableVertexAttribArray(positionHandle)
}
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
}
if (mOutlineProgram != -1){
GLES30.glDeleteProgram(mOutlineProgram)
mOutlineProgram = -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
}
}
}
模版测试通用流程
GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT or GLES30.GL_STENCIL_BUFFER_BIT)
GLES30.glEnable(GLES30.GL_DEPTH_TEST)
GLES30.glEnable(GLES30.GL_STENCIL_TEST)
GLES30.glColorMask(false, false, false, false)
GLES30.glDepthMask(false)
GLES30.glStencilFunc(GLES30.GL_ALWAYS, 1, 0xFF)
GLES30.glStencilOp(GLES30.GL_KEEP, GLES30.GL_KEEP, GLES30.GL_REPLACE)
drawStencilRect()
GLES30.glColorMask(true, true, true, true)
GLES30.glDepthMask(true)
GLES30.glStencilFunc(GLES30.GL_EQUAL, 1, 0xFF)
GLES30.glStencilOp(GLES30.GL_KEEP, GLES30.GL_KEEP, GLES30.GL_KEEP)
GLES30.drawXX()
GLES30.glDisable(GLES30.GL_STENCIL_TEST)
GLES30.glDisable(GLES30.GL_DEPTH_TEST)
效果图