XML
文件
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.myapplication.EGLSurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/image"
android:layout_marginTop="10dp"
android:layout_marginStart="10dp"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="@drawable/ic_launcher_background" />
</FrameLayout>
Activity
代码
private lateinit var mImage : ImageView
class MainActivity8 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main8)
mImage = findViewById<ImageView>(R.id.image)
}
}
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: MyDrawData2? = null
// 实现代码与前面相同
override fun onSurfaceCreated() {
GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)
mDrawData = MyDrawData2().apply {
initTexture0(context, 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()
}
}
搭建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 RenderBufferObjectDrawData {
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 // 每个顶点的总字节数
// VAO(Vertex Array Object), 顶点数组对象, 用于存储VBO
private var mVAO = IntArray(1)
// VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据
private var mVBO = IntArray(1) // 只需要一个VBO
// FBO(Frame Buffer Object), 帧缓冲对象,用于存储渲染后的图像数据
private var mFBO = IntArray(1)
private var mFBOTexture = IntArray(1)
// 纹理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)
}
// 创建VAO, VBO, IBO
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)
}
fun initRenderBufferObject() {
// 创建FBO
GLES30.glGenFramebuffers(mFBO.size, mFBO, NO_OFFSET)
// 绑定FBO
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])
// 创建空纹理
GLES30.glGenTextures(mFBOTexture.size, mFBOTexture, NO_OFFSET)
// 绑定空纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mFBOTexture[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
) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充
GLES30.glTexImage2D(
GLES30.GL_TEXTURE_2D, // 纹理类型
NO_OFFSET, // 偏移量
GLES30.GL_RGBA, // 颜色通道
mSurfaceWidth, // 纹理宽度
mSurfaceHeight, // 纹理高度
NO_OFFSET, // 偏移量
GLES30.GL_RGBA, // 颜色通道
GLES30.GL_UNSIGNED_BYTE, // 颜色数据类型
null // 不传入颜色数据
)
// 绑定空纹理到FBO,用于绘制到FBO
GLES30.glFramebufferTexture2D(
GLES30.GL_FRAMEBUFFER, // FBO
GLES30.GL_COLOR_ATTACHMENT0, // 颜色缓冲区
GLES30.GL_TEXTURE_2D, // 纹理类型
mFBOTexture[0], // 纹理ID
0
)
// 检查FBO状态
val status = GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER)
if (status != GLES30.GL_FRAMEBUFFER_COMPLETE) {
Log.e("yang", "initRenderBufferObjects: FBO初始化失败, 状态码: $status")
} else {
Log.d("yang", "initRenderBufferObjects: FBO初始化成功")
}
// 解绑FBO的纹理
GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
// 解绑FBO
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
}
// GLSurfaceView实时绘制
fun drawCurrentOutput() {
val state = saveGLState()
try {
GLES30.glUseProgram(mProgram)
enableTexture0(mProgram, mTextureID[0])
// 为每个立方体计算MVP矩阵并绘制
for (cube in cubes) {
resetMatrix()
computeMVPMatrix(mSurfaceWidth, mSurfaceHeight, cube)
drawSomething(mProgram, mMVPMatrix)
}
// 绑定FBO,绘制到FBO
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])
// 清除FBO的缓冲区
GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT or GLES30.GL_DEPTH_BUFFER_BIT)
// 为每个立方体计算MVP矩阵并绘制
for (cube in cubes) {
resetMatrix()
computeMVPMatrix(mSurfaceWidth, mSurfaceHeight, cube)
drawSomething(mProgram, mMVPMatrix)
}
getCurrentBitmap()?.let {
mImage.post{
mImage.setImageBitmap(it)
}
}
// 解绑FBO
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
disableTexture0()
} finally {
restoreGLState(state)
}
}
fun getCurrentBitmap() : Bitmap? {
// 绑定FBO, 读取像素数据
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])
// 分配像素缓冲区
val pixelBuffer = ByteBuffer.allocateDirect(mSurfaceWidth * mSurfaceHeight * 4)
pixelBuffer.order(ByteOrder.nativeOrder())
// 读取像素数据
GLES30.glReadPixels(
0,
0,
mSurfaceWidth,
mSurfaceHeight,
GLES30.GL_RGBA,
GLES30.GL_UNSIGNED_BYTE,
pixelBuffer
)
// 解绑FBO
GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
return try {
// 将像素数据转换为Bitmap
pixelBuffer.rewind() // 重要!确保缓冲区指针回到起始位置
val bitmap = Bitmap.createBitmap(mSurfaceWidth, mSurfaceHeight, Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(pixelBuffer)
// OpenGL和Android的坐标系不同,需要垂直翻转图像
val matrix = android.graphics.Matrix()
matrix.setScale(1f, -1f)
return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
} catch (e: Exception) {
e.printStackTrace()
null
}
}
// 保存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
}
// 删除FBO
if (mFBO[0] != 0) {
GLES30.glDeleteFramebuffers(1, mFBO, 0)
mFBO[0] = 0
}
// 删除FBO纹理
if (mFBOTexture[0] != 0) {
GLES30.glDeleteTextures(1, mFBOTexture, 0)
mFBOTexture[0] = 0
}
// 删除纹理
if (mTextureID[0] != 0) {
GLES30.glDeleteTextures(1, mTextureID, 0)
mTextureID[0] = 0
}
// 删除着色器程序
if (mProgram != -1) {
GLES30.glDeleteProgram(mProgram)
mProgram = -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
}
}
}
效果图
