XML文件
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <!-- OpenGL渲染区域 -->
- <com.example.myapplication.MyGLSurfaceView
- android:id="@+id/gl_surface_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent" />
- <!-- 用于显示FBO截图的ImageView -->
- <ImageView
- android:id="@+id/image_view"
- android:layout_width="120dp"
- android:layout_height="120dp"
- android:layout_alignParentEnd="true"
- android:layout_alignParentTop="true"
- android:layout_margin="16dp"
- android:background="#33000000"
- android:contentDescription="FBO截图预览" />
- <!-- 捕获FBO图像的按钮 -->
- <Button
- android:id="@+id/capture_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="16dp"
- android:text="捕获FBO图像"
- android:padding="12dp" />
- </RelativeLayout>
复制代码 Activity代码
- class MainActivity : AppCompatActivity() {
- private lateinit var glSurfaceView: MyGLSurfaceView
- private lateinit var imageView: ImageView
- private lateinit var captureButton: Button
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.activity_main)
- glSurfaceView = findViewById(R.id.gl_surface_view)
- imageView = findViewById(R.id.image_view)
- captureButton = findViewById(R.id.capture_button)
- captureButton.setOnTouchListener(object : View.OnTouchListener {
- override fun onTouch(v: View?, event: MotionEvent?): Boolean {
- when(event?.action){
- MotionEvent.ACTION_DOWN -> {
- getFrameBufferBitmap()?.let {
- imageView.setImageBitmap(it)
- }
- }
- MotionEvent.ACTION_UP -> {
- getFrameBufferBitmap()?.let {
- imageView.setImageBitmap(null)
- }
- }
- else -> {}
- }
- return true
- }
- })
- }
- private fun getFrameBufferBitmap() : Bitmap? {
- return glSurfaceView?.getFrameBufferBitmap()
- }
- }
复制代码 自定义GLSurfaceView代码
- class MyGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {
- private var mRenderer = MyGLRenderer(context)
- init {
- // 设置 OpenGL ES 3.0 版本
- setEGLContextClientVersion(3)
- setRenderer(mRenderer)
- // 设置渲染模式, 仅在需要重新绘制时才进行渲染,以节省资源
- renderMode = RENDERMODE_WHEN_DIRTY
- }
- fun getFrameBufferBitmap(): Bitmap? {
- return mRenderer?.getFrameBufferBitmap()
- }
- }
复制代码 自定义GLSurfaceView.Renderer代码
- class MyGLRenderer(private val mContext: Context) : GLSurfaceView.Renderer {
- private var mDrawData: DrawData? = null
- private var mWidth = 0
- private var mHeight = 0
- override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
- // 当 Surface 创建时调用, 进行 OpenGL ES 环境的初始化操作, 设置清屏颜色为青蓝色 (Red=0, Green=0.5, Blue=0.5, Alpha=1)
- GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)
- mDrawData = DrawData().apply {
- initShader()
- initVertexBuffer()
- initTexture0(mContext, R.drawable.pic)
- initTexture1(mContext, R.drawable.bitmap_shader)
- }
- }
- override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
- // 当 Surface 尺寸发生变化时调用,例如设备的屏幕方向发生改变, 设置视口为新的尺寸,视口是指渲染区域的大小
- GLES30.glViewport(0, 0, width, height)
- mWidth = width
- mHeight = height
- mDrawData?.computeMVPMatrix(width, height)
- mDrawData?.initFrameBuffer(width, height)
- }
- override fun onDrawFrame(gl: GL10?) {
- // 每一帧绘制时调用, 清除颜色缓冲区
- GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
- mDrawData?.enableTexture()
- mDrawData?.drawOffScreen()
- mDrawData?.disableTexture()
- }
- fun getFrameBufferBitmap(): Bitmap? {
- return mDrawData?.getScreenBitmap()
- }
- }
复制代码 GLSurfaceView.Renderer需要的绘制数据
- class DrawData {
- private var mProgram: Int = -1
- private var NO_OFFSET = 0
- private val VERTEX_POS_DATA_SIZE = 3
- private val TEXTURE_POS_DATA_SIZE = 2
- // FBO(Frame Buffer Object), 帧缓冲对象,用于存储渲染后的图像
- private var mFBO = IntArray(1)
- // VAO(Vertex Array Object), 顶点数组对象, 用于存储VBO
- private var mVAO = IntArray(1)
- // VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据
- private var mVBO = IntArray(2)
- // IBO(Index Buffer Object), 索引缓冲对象,用于存储顶点索引数据
- private var mIBO = IntArray(1)
- // 纹理ID
- private var mTextureID = IntArray(2)
- // FBO中的纹理ID
- private var mFBOTextureID = IntArray(1)
- // 最终变化矩阵
- private val mMVPMatrix = FloatArray(16)
- // 投影矩阵
- private val mProjectionMatrix = FloatArray(16)
- // 相机矩阵
- private val mViewMatrix = FloatArray(16)
- // 视口比例
- private var mViewPortRatio = 1f
- // 帧缓冲中的Bitmap
- private var mFrameBufferBitmap: Bitmap? = null
- // 帧缓冲宽高
- private var mFrameBufferWidth = 0
- private var mFrameBufferHeight = 0
- // 帧缓冲最终变换矩阵
- private val mFrameBufferMVPMatrix = FloatArray(16)
- // 准备顶点坐标,分配直接内存
- // OpenGL ES坐标系:原点在中心,X轴向右为正,Y轴向上为正,Z轴向外为正
- val 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)
- // 准备纹理坐标,分配直接内存
- // 纹理坐标系:原点在左下角,X轴向右为正,Y轴向上为正
- 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 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;
- uniform sampler2D uTexture_1;
- in vec2 vTexCoord;
- out vec4 fragColor;
- void main() {
- fragColor = texture(uTexture_0, vTexCoord) + texture(uTexture_1, 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.glUseProgram(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
- GLES30.glGenBuffers(mVBO.size, mVBO, NO_OFFSET)
- // 绑定顶点缓冲区数据到VBO[0]
- GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])
- GLES30.glBufferData(
- GLES30.GL_ARRAY_BUFFER,
- vertex.size * 4,
- vertexBuffer,
- GLES30.GL_STATIC_DRAW
- )
- // 解析顶点缓冲区数据到VBO[0]
- 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)
- // 绑定纹理缓冲区数据到VBO[1]
- GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[1])
- GLES30.glBufferData(
- GLES30.GL_ARRAY_BUFFER,
- textureCoords.size * 4,
- textureBuffer,
- GLES30.GL_STATIC_DRAW
- )
- // 解析纹理缓冲区数据到VBO[1]
- 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)
- // 绑定IBO
- GLES30.glGenBuffers(mIBO.size, mIBO, NO_OFFSET)
- // 绑定索引缓冲区数据到IBO[0]
- GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, mIBO[0])
- GLES30.glBufferData(
- GLES30.GL_ELEMENT_ARRAY_BUFFER,
- index.size * 2,
- indexBuffer,
- GLES30.GL_STATIC_DRAW
- )
- // 解绑VAO
- GLES30.glBindVertexArray(0)
- // 解绑IBO
- GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0)
- }
- // 计算GLSurfaceView变换矩阵
- fun computeMVPMatrix(width: Int, height: Int) {
- // 正交投影矩阵
- takeIf { width > height }?.let {
- mViewPortRatio = (width * 1f) / height
- Matrix.orthoM(
- mProjectionMatrix, // 正交投影矩阵
- NO_OFFSET, // 偏移量
- -mViewPortRatio, // 近平面的坐标系左边界
- mViewPortRatio, // 近平面的坐标系右边界
- -1f, // 近平面的坐标系的下边界
- 1f, // 近平面坐标系的上边界
- 0f, // 近平面距离相机距离
- 1f // 远平面距离相机距离
- )
- } ?: run {
- mViewPortRatio = (height * 1f) / width
- Matrix.orthoM(
- mProjectionMatrix, // 正交投影矩阵
- NO_OFFSET, // 偏移量
- -1f, // 近平面坐标系左边界
- 1f, // 近平面坐标系右边界
- -mViewPortRatio, // 近平面坐标系下边界
- mViewPortRatio, // 近平面坐标系上边界
- 0f, // 近平面距离相机距离
- 1f // 远平面距离相机距离
- )
- }
- // 设置相机矩阵
- // 相机位置(0f, 0f, 1f)
- // 物体位置(0f, 0f, 0f)
- // 相机方向(0f, 1f, 0f)
- Matrix.setLookAtM(
- mViewMatrix, // 相机矩阵
- NO_OFFSET, // 偏移量
- 0f, // 相机位置x
- 0f, // 相机位置y
- 1f, // 相机位置z
- 0f, // 物体位置x
- 0f, // 物体位置y
- 0f, // 物体位置z
- 0f, // 相机上方向x
- 1f, // 相机上方向y
- 0f // 相机上方向z
- )
- // 最终变化矩阵
- Matrix.multiplyMM(
- mMVPMatrix, // 最终变化矩阵
- NO_OFFSET, // 偏移量
- mProjectionMatrix, // 投影矩阵
- NO_OFFSET, // 投影矩阵偏移量
- mViewMatrix, // 相机矩阵
- NO_OFFSET // 相机矩阵偏移量
- )
- // 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系
- // 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变
- Matrix.scaleM(
- mMVPMatrix,
- NO_OFFSET,
- 1f,
- -1f,
- 1f,
- )
- }
- // 初始化帧缓冲
- fun initFrameBuffer(){
- // 创建FBO
- GLES30.glGenFramebuffers(mFBO.size, mFBO, NO_OFFSET)
- // 绑定FBO
- GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])
- // 创建空纹理
- GLES30.glGenTextures(mFBOTextureID.size, mFBOTextureID, NO_OFFSET)
- // 绑定空纹理
- GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mFBOTextureID[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, // 颜色通道
- mFrameBufferWidth, // 纹理宽度
- mFrameBufferHeight, // 纹理高度
- 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, // 纹理类型
- mFBOTextureID[0], // 纹理ID
- 0
- )
- if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
- Log.e("yang", "initFrameBuffer: FBO初始化失败")
- }
- // 解绑FBO纹理
- GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
- // 解绑FBO
- GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
- }
- // 离屏渲染
- fun drawOffScreen() {
- takeIf { mFrameBufferBitmap == null }?.let {
- // 帧缓冲绘制纹理需要修改视口大小,这里需要进行视口的保存和恢复
- val viewport = IntArray(4)
- GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewport, NO_OFFSET)
- // 绑定FBO
- GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])
- // 设置视口为FBO尺寸
- GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)
- // 计算帧缓冲纹理宽高的变换矩阵
- computeFrameBufferMVPMatrix()
- // 清除FBO
- GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
- // 绘制图形到FBO, FBO上的内容不会显示
- drawFrameBuffer()
- // 缓存FBO内容到Bitmap
- getBitmapFromFrameBuffer()
- // 解绑FBO,恢复默认帧缓冲
- GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
- // 恢复原始视口
- GLES30.glViewport(viewport[0], viewport[1], viewport[2], viewport[3])
- }
- }
- // 使用着色器程序绘制图形
- fun drawSomething() {
- // 解析变换矩阵
- val matrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")
- GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mMVPMatrix, NO_OFFSET)
- // 绑定VAO
- GLES30.glBindVertexArray(mVAO[0])
- // 绘制图形
- GLES30.glDrawElements(
- GLES30.GL_TRIANGLES,
- index.size,
- GLES30.GL_UNSIGNED_SHORT,
- NO_OFFSET
- )
- // 解绑VAO
- GLES30.glBindVertexArray(0)
- }
- fun drawFrameBuffer(){
- // 解析帧缓冲变换矩阵
- val matrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")
- GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mFrameBufferMVPMatrix, NO_OFFSET)
- // 绑定VAO
- GLES30.glBindVertexArray(mVAO[0])
- // 绘制图形
- GLES30.glDrawElements(
- GLES30.GL_TRIANGLES,
- index.size,
- GLES30.GL_UNSIGNED_SHORT,
- NO_OFFSET
- )
- // 解绑VAO
- GLES30.glBindVertexArray(0)
- }
- fun enableTexture() {
- // 激活纹理编号0
- GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
- GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[0])
- val textureSampleHandle = GLES30.glGetUniformLocation(mProgram, "uTexture_0")
- GLES30.glUniform1i(textureSampleHandle, 0)
- // 激活纹理编号1
- GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
- GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[1])
- val textureSampleHandle1 = GLES30.glGetUniformLocation(mProgram, "uTexture_1")
- GLES30.glUniform1i(textureSampleHandle1, 1)
- }
- fun disableTexture() {
- GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
- }
- fun initTexture0(context: Context, resourceId: Int){
- mTextureID[0] = loadTexture(context, resourceId)
- }
- fun initTexture1(context: Context, resourceId: Int){
- mTextureID[1] = loadTexture(context, resourceId)
- }
- // 加载纹理
- 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)
- mFrameBufferWidth = max(mFrameBufferWidth, bitmap.width)
- mFrameBufferHeight = max(mFrameBufferHeight, bitmap.height)
- Log.e("yang", "loadTexture: 纹理加载成功 bitmap.width:${bitmap.width} bitmap.height:${bitmap.height}")
- return textureId[0]
- }
- fun getBitmapFromFrameBuffer(){
- // 分配缓冲区来存储像素数据
- val pixelBuffer = ByteBuffer.allocateDirect(mFrameBufferWidth * mFrameBufferHeight * 4)
- .order(ByteOrder.LITTLE_ENDIAN)
- // 读取像素数据
- GLES30.glReadPixels(
- 0, 0, mFrameBufferWidth, mFrameBufferHeight,
- GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE,
- pixelBuffer
- )
- // 将ByteBuffer转换为Bitmap
- val bitmap = Bitmap.createBitmap(mFrameBufferWidth, mFrameBufferHeight, Bitmap.Config.ARGB_8888)
- pixelBuffer.rewind()
- bitmap.copyPixelsFromBuffer(pixelBuffer)
- // OpenGL和Android的Y轴方向相反,需要围绕中心垂直翻转
- val matrix = android.graphics.Matrix().apply {
- setScale(1f, -1f, mFrameBufferWidth / 2f, mFrameBufferHeight / 2f)
- }
- mFrameBufferBitmap = Bitmap.createBitmap(
- bitmap, 0, 0, bitmap.width, bitmap.height,
- matrix, true
- )
- bitmap.recycle()
- }
- fun getScreenBitmap() : Bitmap?{
- return mFrameBufferBitmap
- }
- fun computeFrameBufferMVPMatrix() {
- // 正交投影矩阵
- takeIf { mFrameBufferWidth > mFrameBufferHeight }?.let {
- mViewPortRatio = (mFrameBufferWidth * 1f) / mFrameBufferHeight
- Matrix.orthoM(
- mProjectionMatrix, // 正交投影矩阵
- NO_OFFSET, // 偏移量
- -mViewPortRatio, // 近平面的坐标系左边界
- mViewPortRatio, // 近平面的坐标系右边界
- -1f, // 近平面的坐标系的下边界
- 1f, // 近平面坐标系的上边界
- 0f, // 近平面距离相机距离
- 1f // 远平面距离相机距离
- )
- } ?: run {
- mViewPortRatio = (mFrameBufferHeight * 1f) / mFrameBufferWidth
- Matrix.orthoM(
- mProjectionMatrix, // 正交投影矩阵
- NO_OFFSET, // 偏移量
- -1f, // 近平面坐标系左边界
- 1f, // 近平面坐标系右边界
- -mViewPortRatio, // 近平面坐标系下边界
- mViewPortRatio, // 近平面坐标系上边界
- 0f, // 近平面距离相机距离
- 1f // 远平面距离相机距离
- )
- }
- // 设置相机矩阵
- // 相机位置(0f, 0f, 1f)
- // 物体位置(0f, 0f, 0f)
- // 相机方向(0f, 1f, 0f)
- Matrix.setLookAtM(
- mViewMatrix, // 相机矩阵
- NO_OFFSET, // 偏移量
- 0f, // 相机位置x
- 0f, // 相机位置y
- 1f, // 相机位置z
- 0f, // 物体位置x
- 0f, // 物体位置y
- 0f, // 物体位置z
- 0f, // 相机上方向x
- 1f, // 相机上方向y
- 0f // 相机上方向z
- )
- // 最终变化矩阵
- Matrix.multiplyMM(
- mFrameBufferMVPMatrix, // 最终变化矩阵
- NO_OFFSET, // 偏移量
- mProjectionMatrix, // 投影矩阵
- NO_OFFSET, // 投影矩阵偏移量
- mViewMatrix, // 相机矩阵
- NO_OFFSET // 相机矩阵偏移量
- )
- // 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系
- // 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变
- Matrix.scaleM(
- mFrameBufferMVPMatrix,
- NO_OFFSET,
- 1f,
- -1f,
- 1f,
- )
- }
- object LoadShaderUtil {
- // 创建着色器对象
- fun loadShader(type: Int, source: String): Int {
- val shader = GLES30.glCreateShader(type)
- GLES30.glShaderSource(shader, source)
- GLES30.glCompileShader(shader)
- return shader
- }
- }
- }
复制代码 结果图
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |