XML
文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><com.example.myapplication.MyMatrixGLSurfaceViewandroid:id="@+id/glSurfaceView"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="4" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="旋转:"android:textSize="16sp" /><Buttonandroid:id="@+id/rotateXBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="X轴" /><Buttonandroid:id="@+id/rotateYBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Y轴" /><Buttonandroid:id="@+id/rotateZBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Z轴" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="缩放:"android:textSize="16sp" /><Buttonandroid:id="@+id/scaleXBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="X轴" /><Buttonandroid:id="@+id/scaleYBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Y轴" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="位移:"android:textSize="16sp" /><Buttonandroid:id="@+id/translateXPosBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="X轴" /><Buttonandroid:id="@+id/translateYPosBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Y轴" /></LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:orientation="horizontal"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="正交投影:" /><Buttonandroid:id="@+id/resetBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="重置" /></LinearLayout></LinearLayout>
Activity
代码
class MainActivity5 : AppCompatActivity(), View.OnClickListener {companion object {private const val ROTATION_DELTA = 10.0fprivate const val SCALE_DELTA = 0.25fprivate const val TRANSLATE_DELTA = 0.25f}private lateinit var glSurfaceView: MyMatrixGLSurfaceViewprivate var rotationX = 0fprivate var rotationY = 0fprivate var rotationZ = 0fprivate var scaleX = 1.0fprivate var scaleY = 1.0fprivate var translateX = 0fprivate var translateY = 0foverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main5)glSurfaceView = findViewById(R.id.glSurfaceView)findViewById<Button>(R.id.rotateXBtn).setOnClickListener(this)findViewById<Button>(R.id.rotateYBtn).setOnClickListener(this)findViewById<Button>(R.id.rotateZBtn).setOnClickListener(this)findViewById<Button>(R.id.scaleXBtn).setOnClickListener(this)findViewById<Button>(R.id.scaleYBtn).setOnClickListener(this)findViewById<Button>(R.id.translateXPosBtn).setOnClickListener(this)findViewById<Button>(R.id.translateYPosBtn).setOnClickListener(this)findViewById<Button>(R.id.resetBtn).setOnClickListener(this)}override fun onClick(v: View) {when (v.id) {R.id.rotateXBtn -> {rotationX += ROTATION_DELTAglSurfaceView.updateRotation(rotationX, AXIS.X)}R.id.rotateYBtn -> {rotationY += ROTATION_DELTAglSurfaceView.updateRotation(rotationY, AXIS.Y)}R.id.rotateZBtn -> {rotationZ += ROTATION_DELTAglSurfaceView.updateRotation(rotationZ, AXIS.Z)}R.id.scaleXBtn -> {scaleX += SCALE_DELTAglSurfaceView.updateScale(scaleX, AXIS.X)}R.id.scaleYBtn -> {scaleY += SCALE_DELTAglSurfaceView.updateScale(scaleY, AXIS.Y)}R.id.translateXPosBtn -> {translateX += TRANSLATE_DELTAglSurfaceView.updateTranslate(translateX, AXIS.X)}R.id.translateYPosBtn -> {translateY += TRANSLATE_DELTAglSurfaceView.updateTranslate(translateY, AXIS.Y)}R.id.resetBtn -> {resetTransformations()}}glSurfaceView.requestRender()}private fun resetTransformations() {rotationX = 0frotationY = 0frotationZ = 0fscaleX = 1.0fscaleY = 1.0ftranslateX = 0ftranslateY = 0fglSurfaceView?.reset()}
}enum class AXIS {X,Y,Z
}
GLSurfaceView
代码
class MyMatrixGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {private var mRenderer = MyMatrixRenderer(context)init {setEGLContextClientVersion(3)setRenderer(mRenderer)renderMode = RENDERMODE_WHEN_DIRTY}fun updateRotation(angle: Float, axis: AXIS) {mRenderer?.mDrawData?.apply {resetMatrix()computeRotateModelMatrix(angle, axis)computeMVPMatrix(width, height)drawCurrentOutput()}}fun updateScale(scale: Float, axis: AXIS) {mRenderer?.mDrawData?.apply {resetMatrix()computeScaleModelMatrix(scale, axis)computeMVPMatrix(width, height)drawCurrentOutput()}}fun updateTranslate(translate: Float, axis: AXIS) {mRenderer?.mDrawData?.apply {resetMatrix()computeTranslateModelMatrix(translate, axis)computeMVPMatrix(width, height)drawCurrentOutput()}}fun reset() {mRenderer?.mDrawData?.apply {resetMatrix()computeMVPMatrix(width, height)drawCurrentOutput()}}
}
GLSurfaceView.Renderer
代码
class MyMatrixRenderer(private val mContext: Context) : GLSurfaceView.Renderer {var mDrawData: MatrixDrawData? = nulloverride fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)mDrawData = MatrixDrawData().apply {initTexture0(mContext, R.drawable.picture)initShader()initVertexBuffer()}}override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {GLES30.glViewport(0, 0, width, height)mDrawData?.resetMatrix()mDrawData?.computeMVPMatrix(width, height)}override fun onDrawFrame(gl: GL10?) {GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)mDrawData?.drawCurrentOutput()}
}
GLSurfaceView.Renderer
需要的绘制数据
class MatrixDrawData {private var NO_OFFSET = 0private val VERTEX_POS_DATA_SIZE = 3private val TEXTURE_POS_DATA_SIZE = 2private var mProgram: Int = -1private var mVAO = IntArray(1)private var mVBO = IntArray(2)private var mIBO = IntArray(1)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 = 1fval vertex = floatArrayOf(-1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f, )val vertexBuffer = ByteBuffer.allocateDirect(vertex.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertex).position(NO_OFFSET)val textureCoords = floatArrayOf(0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, )val textureBuffer = ByteBuffer.allocateDirect(textureCoords.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(textureCoords).position(NO_OFFSET)val index = shortArrayOf(0, 1, 2, 1, 3, 2, )val indexBuffer = ByteBuffer.allocateDirect(index.size * 2).order(ByteOrder.nativeOrder()).asShortBuffer().put(index).position(NO_OFFSET)fun initShader() {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,vertex.size * 4,vertexBuffer,GLES30.GL_STATIC_DRAW)val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")GLES30.glEnableVertexAttribArray(positionHandle)GLES30.glVertexAttribPointer(positionHandle,VERTEX_POS_DATA_SIZE,GLES30.GL_FLOAT,false,0,NO_OFFSET)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[1])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,textureCoords.size * 4,textureBuffer,GLES30.GL_STATIC_DRAW)val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")GLES30.glEnableVertexAttribArray(textureHandle)GLES30.glVertexAttribPointer(textureHandle,TEXTURE_POS_DATA_SIZE,GLES30.GL_FLOAT,false,0,NO_OFFSET)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)GLES30.glGenBuffers(mIBO.size, mIBO, NO_OFFSET)GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, mIBO[0])GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,index.size * 2,indexBuffer,GLES30.GL_STATIC_DRAW)GLES30.glBindVertexArray(0)GLES30.glBindBuffer(GLES30.GL_ELEMENT_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.glDrawElements(GLES30.GL_TRIANGLES,index.size,GLES30.GL_UNSIGNED_SHORT,NO_OFFSET)GLES30.glBindVertexArray(0)}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) {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 * radiusMatrix.setLookAtM(mViewMatrix, NO_OFFSET,0f, 0f, near + radius, 0f, 0f, 0f, 0f, 1f, 0f )Matrix.orthoM(mProjectionMatrix, NO_OFFSET,if (isLandscape) -mViewPortRatio else -1f, if (isLandscape) mViewPortRatio else 1f, if (isLandscape) -1f else -mViewPortRatio, if (isLandscape) 1f else mViewPortRatio, 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 computeRotateModelMatrix(angleX: Float, axis: AXIS) {when (axis) {AXIS.X -> Matrix.rotateM(mModelMatrix, NO_OFFSET, angleX, 1f, 0f, 0f)AXIS.Y -> Matrix.rotateM(mModelMatrix, NO_OFFSET, angleX, 0f, 1f, 0f)AXIS.Z -> Matrix.rotateM(mModelMatrix, NO_OFFSET, angleX, 0f, 0f, 1f)}}fun computeScaleModelMatrix(scale: Float, axis: AXIS) {when (axis) {AXIS.X -> Matrix.scaleM(mModelMatrix, NO_OFFSET, scale, 1f, 1f)AXIS.Y -> Matrix.scaleM(mModelMatrix, NO_OFFSET, 1f, scale, 1f)else -> {}}}fun computeTranslateModelMatrix(translate: Float, axis: AXIS) {when (axis) {AXIS.X -> Matrix.translateM(mModelMatrix, NO_OFFSET, translate, 0f, 0f)AXIS.Y -> Matrix.translateM(mModelMatrix, NO_OFFSET, 0f, translate, 0f)else -> {}}}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])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)}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}}
}
效果图
