Open GL ES -> 模版测试,绘制SurfaceView中某个目标区域

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) {
        // 允许用户自定义渲染策略
    }


    // 适配器模式 提供给SurfaceView使用的回调
    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 {
                // 使用构建者创建EGL环境
                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环境代码

// 构建者模式:配置和构建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)
    }
}

// EGL环境类
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)
    }
}

// 抽象工厂模式:负责创建EGL相关组件族
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,       // x位置
    var y: Float,       // y位置
    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 // 每个顶点的总字节数

    // VAO(Vertex Array Object), 顶点数组对象, 用于存储VBO
    private var mVAO = IntArray(1)

    // VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据
    private var mVBO = IntArray(1) // 只需要一个VBO

    // 纹理ID
    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

    // Surface宽高
    private var mSurfaceWidth = 0
    private var mSurfaceHeight = 0

    // 顶点和纹理坐标合并在一个数组中
    // 格式:x, y, z, u, v (顶点坐标后跟纹理坐标)
    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)
    }


    // 创建VAO, VBO
    fun initVertexBuffer() {
        // 绑定VAO
        GLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)
        GLES30.glBindVertexArray(mVAO[0])

        // 绑定VBO - 只需要一个VBO存储所有数据
        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,     // 步长,每个顶点5个float (x,y,z,u,v)
            NO_OFFSET   // 偏移量,位置数据在前
        )

        // 设置顶点属性指针 - 纹理坐标
        val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")
        GLES30.glEnableVertexAttribArray(textureHandle)
        GLES30.glVertexAttribPointer(
            textureHandle,
            TEXTURE_POS_DATA_SIZE,
            GLES30.GL_FLOAT,
            false,
            STRIDE,                          // 步长,每个顶点5个float (x,y,z,u,v)
            VERTEX_POS_DATA_SIZE * 4         // 偏移量,纹理数据在位置数据之后
        )

        // 解绑VAO
        GLES30.glBindVertexArray(0)
        // 解绑VBO
        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)

        // 绑定VAO
        GLES30.glBindVertexArray(mVAO[0])
        // 绘制图形
        GLES30.glDrawArrays(
            GLES30.GL_TRIANGLES,
            NO_OFFSET,
            vertexData.size / (VERTEX_POS_DATA_SIZE + TEXTURE_POS_DATA_SIZE)
        )
        // 解绑VAO
        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)
    }

    // 计算GLSurfaceView变换矩阵
    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)
        // 视图矩阵View Matrix
        Matrix.setLookAtM(
            mViewMatrix, NO_OFFSET,
            0f, 0f, near + radius,  // 相机位置
            0f, 0f, 0f,             // 看向原点
            0f, 1f, 0f              // 上方向
        )

        // 投影矩阵Projection Matrix
        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 // 远平面
        )

        // 最终变换矩阵,第一次变换,模型矩阵 x 视图矩阵 = Model x View, 但是OpenGL ES矩阵乘法是右乘,所以是View x Model
        Matrix.multiplyMM(
            mMVPMatrix,
            NO_OFFSET,
            mViewMatrix,
            NO_OFFSET,
            mModelMatrix,
            NO_OFFSET
        )

        // 最终变换矩阵,第二次变换,模型矩阵 x 视图矩阵 x 投影矩阵 = Model x View x Projection, 但是OpenGL ES矩阵乘法是右乘,所以是Projection x View x Model
        Matrix.multiplyMM(
            mMVPMatrix,
            NO_OFFSET,
            mProjectionMatrix,
            NO_OFFSET,
            mMVPMatrix,
            NO_OFFSET
        )

        // 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系
        // 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变
        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)
    }

    // SurfaceView实时绘制
    fun drawCurrentOutput() {
        val state = saveGLState()
        try {
            // 1. 清除所有缓冲区
            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)

            // 2. 启用深度测试
            GLES30.glEnable(GLES30.GL_DEPTH_TEST)

            // 3. 绘制模板遮罩 - 第一阶段
            GLES30.glEnable(GLES30.GL_STENCIL_TEST)

            // 禁用颜色和深度写入,只写入模板缓冲区
            GLES30.glColorMask(false, false, false, false)
            GLES30.glDepthMask(false)

            // 设置模板操作:当片段通过时写入1
            GLES30.glStencilFunc(GLES30.GL_ALWAYS, 1, 0xFF) // 总是通过测试
            GLES30.glStencilOp(GLES30.GL_KEEP, GLES30.GL_KEEP, GLES30.GL_REPLACE) // 通过时替换为参考值1

            // 绘制中间矩形到模板缓冲区
            drawStencilRect()

            // 4. 恢复颜色和深度写入
            GLES30.glColorMask(true, true, true, true)
            GLES30.glDepthMask(true)

            // 5. 设置模板测试条件:只在模板值为1的区域绘制
            GLES30.glStencilFunc(GLES30.GL_EQUAL, 1, 0xFF) // 只有当模板值等于1时通过
            GLES30.glStencilOp(GLES30.GL_KEEP, GLES30.GL_KEEP, GLES30.GL_KEEP) // 保持模板值不变

            // 6. 绘制立方体 - 只在模板值为1的区域可见
            GLES30.glUseProgram(mProgram)
            enableTexture0(mProgram, mTextureID[0])

            for (cube in cubes) {
                resetMatrix()
                computeMVPMatrix(mSurfaceWidth, mSurfaceHeight, cube)
                drawSomething(mProgram, mMVPMatrix)
            }

            disableTexture0()

            // 7. 禁用模板测试和深度测试
            GLES30.glDisable(GLES30.GL_STENCIL_TEST)
            GLES30.glDisable(GLES30.GL_DEPTH_TEST)
        } finally {
            restoreGLState(state)
        }
    }

    // 绘制模板遮罩 - 只在中间0.5区域设置模板值
    fun drawStencilRect() {
        // 使用简单着色器
        GLES30.glUseProgram(mOutlineProgram)

        // 创建中间0.5矩形的顶点
        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)
    }

    // 保存OpenGL状态
    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])
    }

    // 恢复OpenGL状态
    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() {
        // 删除VAO
        if (mVAO[0] != 0) {
            GLES30.glDeleteVertexArrays(1, mVAO, 0)
            mVAO[0] = 0
        }

        // 删除VBO
        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()
    }

    // OpenGL状态数据类
    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
        }
    }
}

模版测试通用流程

// 1. 清除所有缓冲区
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)

// 2. 启用深度测试
GLES30.glEnable(GLES30.GL_DEPTH_TEST)

// 3. 绘制模板遮罩 - 第一阶段
GLES30.glEnable(GLES30.GL_STENCIL_TEST)

// 禁用颜色和深度写入,只写入模板缓冲区
GLES30.glColorMask(false, false, false, false)
GLES30.glDepthMask(false)

// 设置模板操作:当片段通过时写入1
GLES30.glStencilFunc(GLES30.GL_ALWAYS, 1, 0xFF) // 总是通过测试
GLES30.glStencilOp(GLES30.GL_KEEP, GLES30.GL_KEEP, GLES30.GL_REPLACE) // 通过时替换为参考值1

// 绘制目标区域到模板缓冲区
drawStencilRect()

// 4. 恢复颜色和深度写入
GLES30.glColorMask(true, true, true, true)
GLES30.glDepthMask(true)

// 5. 设置模板测试条件:只在模板值为1的区域绘制
GLES30.glStencilFunc(GLES30.GL_EQUAL, 1, 0xFF) // 只有当模板值等于1时通过
GLES30.glStencilOp(GLES30.GL_KEEP, GLES30.GL_KEEP, GLES30.GL_KEEP) // 保持模板值不变

// 6. 绘制原始内容
GLES30.drawXX()

// 7. 禁用模板测试和深度测试
GLES30.glDisable(GLES30.GL_STENCIL_TEST)
GLES30.glDisable(GLES30.GL_DEPTH_TEST)

效果图

  • 原图
  • 原图,绘制某个目标区域

你可能感兴趣的:(OpenGL,ES,android,android,studio)