当前位置: 首页 > news >正文

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

XML文件

<?xml version="1.0" encoding="utf-8"?>
<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? = nullprivate var mRenderThread: RenderThread? = nullfun 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 = falseprivate var width = 0private var height = 0fun updateSize(width: Int, height: Int) {this.width = widththis.height = heightsizeChanged = true}fun shutdown() {running = falseinterrupt()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: EGL10private lateinit var mEGLDisplay: EGLDisplayprivate lateinit var mEGLConfig: EGLConfigprivate lateinit var mEGLContext: EGLContextprivate lateinit var mEGLSurface: EGLSurfacefun 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(): EGL10fun createEGLDisplay(egl: EGL10): EGLDisplayfun createEGLConfig(egl: EGL10, display: EGLDisplay): EGLConfigfun createEGLContext(egl: EGL10, display: EGLDisplay, config: EGLConfig): EGLContextfun createEGLSurface(egl: EGL10, display: EGLDisplay, config: EGLConfig, surface: Surface): EGLSurface
}// 具体工厂实现
class DefaultEGLFactory : EGLComponentFactory {override fun createEGL(): EGL10 = EGLContext.getEGL() as EGL10override 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 = -1private var mOutlineProgram : 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 // 每个顶点的总字节数// VAO(Vertex Array Object), 顶点数组对象, 用于存储VBOprivate var mVAO = IntArray(1)// VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据private var mVBO = IntArray(1) // 只需要一个VBO// 纹理IDprivate 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 = 0private 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 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 initOutlineShaderProgram() {val vertexShaderCode = """#version 300 esuniform mat4 uMVPMatrix; in vec4 aPosition;          void main() {gl_Position = uMVPMatrix * aPosition;  }""".trimIndent()val fragmentShaderCode = """#version 300 esprecision 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, VBOfun initVertexBuffer() {// 绑定VAOGLES30.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         // 偏移量,纹理数据在位置数据之后)// 解绑VAOGLES30.glBindVertexArray(0)// 解绑VBOGLES30.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)// 绑定VAOGLES30.glBindVertexArray(mVAO[0])// 绘制图形GLES30.glDrawArrays(GLES30.GL_TRIANGLES,NO_OFFSET,vertexData.size / (VERTEX_POS_DATA_SIZE + TEXTURE_POS_DATA_SIZE))// 解绑VAOGLES30.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)}// 计算GLSurfaceView变换矩阵fun computeMVPMatrix(width: Int, height: Int, cube: CubeInfo) {mSurfaceWidth = widthmSurfaceHeight = height// 更新立方体的旋转角度cube.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() / width// 计算包围图片的球半径val radius = sqrt(1f + mViewPortRatio * mViewPortRatio)val near = 0.1fval far = near + 2 * radiusval distance = near / (near + radius)// 视图矩阵View MatrixMatrix.setLookAtM(mViewMatrix, NO_OFFSET,0f, 0f, near + radius,  // 相机位置0f, 0f, 0f,             // 看向原点0f, 1f, 0f              // 上方向)// 投影矩阵Projection MatrixMatrix.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 ModelMatrix.multiplyMM(mMVPMatrix,NO_OFFSET,mViewMatrix,NO_OFFSET,mModelMatrix,NO_OFFSET)// 最终变换矩阵,第二次变换,模型矩阵 x 视图矩阵 x 投影矩阵 = Model x View x Projection, 但是OpenGL ES矩阵乘法是右乘,所以是Projection x View x ModelMatrix.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)// 设置模板操作:当片段通过时写入1GLES30.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() {// 删除VAOif (mVAO[0] != 0) {GLES30.glDeleteVertexArrays(1, mVAO, 0)mVAO[0] = 0}// 删除VBOif (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)

效果图

  • 原图
    在这里插入图片描述
  • 原图,绘制某个目标区域
    在这里插入图片描述

相关文章:

  • 2.Spring MVC与WebFlux响应式编程
  • Ubuntu与OpenHarmony OS 5.0显示系统架构比较
  • Trae国内版怎么用?Trae IDE 内置 MCP 市场配置使用指南
  • 软考软件设计师考试情况与大纲概述
  • 从零开始构建微博爬虫与数据分析系统
  • 蓝桥杯 19.合根植物
  • JavaScript性能优化实战(2):DOM操作优化策略
  • 使用FreeRTOS解决单片机串口异步打印
  • Spark-Streaming
  • 第一章-语言基础\2.竞赛常用库函数\其他库函数
  • vite详细打包配置,包含性能优化、资源处理...
  • 通过dogssl申请ssl免费证书
  • 如何一键提取多个 PPT 幻灯片中的备注到 TXT 记事本文件中
  • 通过AI工具或模型创建PPT的不同方式详解,结合 Assistants API、DALL·E 3 等工具的功能对比及表格总结
  • Word处理控件Spire.Doc系列教程:C# 为 Word 文档设置背景颜色或背景图片
  • 什么是snmp协议?在优雅草星云智控AI物联网监控系统中如何添加设备进行监控【星云智控手册01】-优雅草卓伊凡
  • HarmonyOS:网络HTTP数据请求
  • 离散化区间和 java c++
  • WebRTC服务器Coturn服务器相关测试工具
  • 2023蓝帽杯初赛内存取证-5
  • 马上评丨全面取消 “仅退款”,反内卷的必然
  • 叶辛秦文君进校园推广阅读
  • 泽连斯基:乌英法美将在伦敦讨论停火事宜
  • “HPV男女共防计划”北半马主题活动新闻发布会在京举办
  • 美国同日曝两起泄密事件:防长群聊向家人分享作战计划,白宫平面图被“共享”
  • 从南宋遗韵到海派风情,解码江南服饰美学基因