OpenGL ES ->帧缓冲对象(Frame Buffer Object)离屏渲染获取纹理贴图 ...

打印 上一主题 下一主题

主题 1009|帖子 1009|积分 3027

XML文件

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3.     android:layout_width="match_parent"
  4.     android:layout_height="match_parent">
  5.     <!-- OpenGL渲染区域 -->
  6.     <com.example.myapplication.MyGLSurfaceView
  7.         android:id="@+id/gl_surface_view"
  8.         android:layout_width="match_parent"
  9.         android:layout_height="match_parent" />
  10.     <!-- 用于显示FBO截图的ImageView -->
  11.     <ImageView
  12.         android:id="@+id/image_view"
  13.         android:layout_width="120dp"
  14.         android:layout_height="120dp"
  15.         android:layout_alignParentEnd="true"
  16.         android:layout_alignParentTop="true"
  17.         android:layout_margin="16dp"
  18.         android:background="#33000000"
  19.         android:contentDescription="FBO截图预览" />
  20.     <!-- 捕获FBO图像的按钮 -->
  21.     <Button
  22.         android:id="@+id/capture_button"
  23.         android:layout_width="wrap_content"
  24.         android:layout_height="wrap_content"
  25.         android:layout_alignParentBottom="true"
  26.         android:layout_centerHorizontal="true"
  27.         android:layout_marginBottom="16dp"
  28.         android:text="捕获FBO图像"
  29.         android:padding="12dp" />
  30. </RelativeLayout>
复制代码
Activity代码

  1. class MainActivity : AppCompatActivity() {
  2.     private lateinit var glSurfaceView: MyGLSurfaceView
  3.     private lateinit var imageView: ImageView
  4.     private lateinit var captureButton: Button
  5.     override fun onCreate(savedInstanceState: Bundle?) {
  6.         super.onCreate(savedInstanceState)
  7.         setContentView(R.layout.activity_main)
  8.         glSurfaceView = findViewById(R.id.gl_surface_view)
  9.         imageView = findViewById(R.id.image_view)
  10.         captureButton = findViewById(R.id.capture_button)
  11.         captureButton.setOnTouchListener(object : View.OnTouchListener {
  12.             override fun onTouch(v: View?, event: MotionEvent?): Boolean {
  13.                 when(event?.action){
  14.                     MotionEvent.ACTION_DOWN -> {
  15.                         getFrameBufferBitmap()?.let {
  16.                             imageView.setImageBitmap(it)
  17.                         }
  18.                     }
  19.                     MotionEvent.ACTION_UP -> {
  20.                         getFrameBufferBitmap()?.let {
  21.                             imageView.setImageBitmap(null)
  22.                         }
  23.                     }
  24.                     else -> {}
  25.                 }
  26.                 return true
  27.             }
  28.         })
  29.     }
  30.     private fun getFrameBufferBitmap() : Bitmap? {
  31.         return glSurfaceView?.getFrameBufferBitmap()
  32.     }
  33. }
复制代码
自定义GLSurfaceView代码

  1. class MyGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {
  2.     private var mRenderer = MyGLRenderer(context)
  3.     init {
  4.         // 设置 OpenGL ES 3.0 版本
  5.         setEGLContextClientVersion(3)
  6.         setRenderer(mRenderer)
  7.         // 设置渲染模式, 仅在需要重新绘制时才进行渲染,以节省资源
  8.         renderMode = RENDERMODE_WHEN_DIRTY
  9.     }
  10.     fun getFrameBufferBitmap(): Bitmap? {
  11.         return mRenderer?.getFrameBufferBitmap()
  12.     }
  13. }
复制代码
自定义GLSurfaceView.Renderer代码

  1. class MyGLRenderer(private val mContext: Context) : GLSurfaceView.Renderer {
  2.     private var mDrawData: DrawData? = null
  3.     private var mWidth = 0
  4.     private var mHeight = 0
  5.     override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
  6.         // 当 Surface 创建时调用, 进行 OpenGL ES 环境的初始化操作, 设置清屏颜色为青蓝色 (Red=0, Green=0.5, Blue=0.5, Alpha=1)
  7.         GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)
  8.         mDrawData = DrawData().apply {
  9.             initShader()
  10.             initVertexBuffer()
  11.             initTexture0(mContext, R.drawable.pic)
  12.             initTexture1(mContext, R.drawable.bitmap_shader)
  13.         }
  14.     }
  15.     override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
  16.         // 当 Surface 尺寸发生变化时调用,例如设备的屏幕方向发生改变, 设置视口为新的尺寸,视口是指渲染区域的大小
  17.         GLES30.glViewport(0, 0, width, height)
  18.         mWidth = width
  19.         mHeight = height
  20.         mDrawData?.computeMVPMatrix(width, height)
  21.         mDrawData?.initFrameBuffer(width, height)
  22.     }
  23.     override fun onDrawFrame(gl: GL10?) {
  24.         // 每一帧绘制时调用, 清除颜色缓冲区
  25.         GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
  26.         mDrawData?.enableTexture()
  27.         mDrawData?.drawOffScreen()
  28.         mDrawData?.disableTexture()
  29.     }
  30.     fun getFrameBufferBitmap(): Bitmap? {
  31.        return mDrawData?.getScreenBitmap()
  32.     }
  33. }
复制代码
GLSurfaceView.Renderer需要的绘制数据

  1. class DrawData {
  2.     private var mProgram: Int = -1
  3.     private var NO_OFFSET = 0
  4.     private val VERTEX_POS_DATA_SIZE = 3
  5.     private val TEXTURE_POS_DATA_SIZE = 2
  6.     // FBO(Frame Buffer Object), 帧缓冲对象,用于存储渲染后的图像
  7.     private var mFBO = IntArray(1)
  8.     // VAO(Vertex Array Object), 顶点数组对象, 用于存储VBO
  9.     private var mVAO = IntArray(1)
  10.     // VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据
  11.     private var mVBO = IntArray(2)
  12.     // IBO(Index Buffer Object), 索引缓冲对象,用于存储顶点索引数据
  13.     private var mIBO = IntArray(1)
  14.     // 纹理ID
  15.     private var mTextureID = IntArray(2)
  16.     // FBO中的纹理ID
  17.     private var mFBOTextureID = IntArray(1)
  18.     // 最终变化矩阵
  19.     private val mMVPMatrix = FloatArray(16)
  20.     // 投影矩阵
  21.     private val mProjectionMatrix = FloatArray(16)
  22.     // 相机矩阵
  23.     private val mViewMatrix = FloatArray(16)
  24.     // 视口比例
  25.     private var mViewPortRatio = 1f
  26.     // 帧缓冲中的Bitmap
  27.     private var mFrameBufferBitmap: Bitmap? = null
  28.     // 帧缓冲宽高
  29.     private var mFrameBufferWidth = 0
  30.     private var mFrameBufferHeight = 0
  31.     // 帧缓冲最终变换矩阵
  32.     private val mFrameBufferMVPMatrix = FloatArray(16)
  33.     // 准备顶点坐标,分配直接内存
  34.     // OpenGL ES坐标系:原点在中心,X轴向右为正,Y轴向上为正,Z轴向外为正
  35.     val vertex = floatArrayOf(
  36.         -1.0f, 1.0f, 0.0f, // 左上
  37.         -1.0f, -1.0f, 0.0f, // 左下
  38.         1.0f, 1.0f, 0.0f, // 右上
  39.         1.0f, -1.0f, 0.0f, // 右下
  40.     )
  41.     val vertexBuffer = ByteBuffer.allocateDirect(vertex.size * 4)
  42.         .order(ByteOrder.nativeOrder())
  43.         .asFloatBuffer()
  44.         .put(vertex)
  45.         .position(NO_OFFSET)
  46.     // 准备纹理坐标,分配直接内存
  47.     // 纹理坐标系:原点在左下角,X轴向右为正,Y轴向上为正
  48.     val textureCoords = floatArrayOf(
  49.         0.0f, 1.0f, // 左上
  50.         0.0f, 0.0f, // 左下
  51.         1.0f, 1.0f, // 右上
  52.         1.0f, 0.0f, // 右下
  53.     )
  54.     val textureBuffer = ByteBuffer.allocateDirect(textureCoords.size * 4)
  55.         .order(ByteOrder.nativeOrder())
  56.         .asFloatBuffer()
  57.         .put(textureCoords)
  58.         .position(NO_OFFSET)
  59.     // 索引坐标,分配直接内存
  60.     val index = shortArrayOf(
  61.         0, 1, 2, // 第一个三角形
  62.         1, 3, 2, // 第二个三角形
  63.     )
  64.     val indexBuffer = ByteBuffer.allocateDirect(index.size * 2)
  65.         .order(ByteOrder.nativeOrder())
  66.         .asShortBuffer()
  67.         .put(index)
  68.         .position(NO_OFFSET)
  69.     // 初始化着色器程序
  70.     fun initShader() {
  71.         val vertexShaderCode = """#version 300 es
  72.                 uniform mat4 uMVPMatrix; // 变换矩阵
  73.                 in vec4 aPosition; // 顶点坐标
  74.                 in vec2 aTexCoord; // 纹理坐标
  75.                 out vec2 vTexCoord;
  76.                 void main() {
  77.                     // 输出顶点坐标和纹理坐标到片段着色器
  78.                     gl_Position = uMVPMatrix * aPosition;
  79.                     vTexCoord = aTexCoord;
  80.                 }""".trimIndent()
  81.         val fragmentShaderCode = """#version 300 es
  82.              precision mediump float;
  83.              uniform sampler2D uTexture_0;
  84.              uniform sampler2D uTexture_1;
  85.              in vec2 vTexCoord;
  86.              out vec4 fragColor;
  87.              void main() {
  88.                  fragColor = texture(uTexture_0, vTexCoord) + texture(uTexture_1, vTexCoord);
  89.              }""".trimIndent()
  90.         // 加载顶点着色器和片段着色器, 并创建着色器程序
  91.         val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
  92.         val fragmentShader = LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
  93.         mProgram = GLES30.glCreateProgram()
  94.         GLES30.glAttachShader(mProgram, vertexShader)
  95.         GLES30.glAttachShader(mProgram, fragmentShader)
  96.         GLES30.glLinkProgram(mProgram)
  97.         GLES30.glUseProgram(mProgram)
  98.         // 删除着色器对象
  99.         GLES30.glDeleteShader(vertexShader)
  100.         GLES30.glDeleteShader(fragmentShader)
  101.     }
  102.     // 创建VAO, VBO, IBO
  103.     fun initVertexBuffer() {
  104.         // 绑定VAO
  105.         GLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)
  106.         GLES30.glBindVertexArray(mVAO[0])
  107.             // 绑定VBO
  108.             GLES30.glGenBuffers(mVBO.size, mVBO, NO_OFFSET)
  109.                 // 绑定顶点缓冲区数据到VBO[0]
  110.                 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])
  111.                 GLES30.glBufferData(
  112.                     GLES30.GL_ARRAY_BUFFER,
  113.                     vertex.size * 4,
  114.                     vertexBuffer,
  115.                     GLES30.GL_STATIC_DRAW
  116.                 )
  117.                 // 解析顶点缓冲区数据到VBO[0]
  118.                 val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")
  119.                 GLES30.glEnableVertexAttribArray(positionHandle)
  120.                 GLES30.glVertexAttribPointer(
  121.                     positionHandle,
  122.                     VERTEX_POS_DATA_SIZE,
  123.                     GLES30.GL_FLOAT,
  124.                     false,
  125.                     0,
  126.                     NO_OFFSET
  127.                 )
  128.                 // 解绑顶点缓冲区
  129.                 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
  130.                 // 绑定纹理缓冲区数据到VBO[1]
  131.                 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[1])
  132.                 GLES30.glBufferData(
  133.                     GLES30.GL_ARRAY_BUFFER,
  134.                     textureCoords.size * 4,
  135.                     textureBuffer,
  136.                     GLES30.GL_STATIC_DRAW
  137.                 )
  138.                 // 解析纹理缓冲区数据到VBO[1]
  139.                 val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")
  140.                 GLES30.glEnableVertexAttribArray(textureHandle)
  141.                 GLES30.glVertexAttribPointer(
  142.                     textureHandle,
  143.                     TEXTURE_POS_DATA_SIZE,
  144.                     GLES30.GL_FLOAT,
  145.                     false,
  146.                     0,
  147.                     NO_OFFSET
  148.                 )
  149.                 // 解绑纹理缓冲区
  150.                 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
  151.             // 绑定IBO
  152.             GLES30.glGenBuffers(mIBO.size, mIBO, NO_OFFSET)
  153.             // 绑定索引缓冲区数据到IBO[0]
  154.             GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, mIBO[0])
  155.             GLES30.glBufferData(
  156.                 GLES30.GL_ELEMENT_ARRAY_BUFFER,
  157.                 index.size * 2,
  158.                 indexBuffer,
  159.                 GLES30.GL_STATIC_DRAW
  160.             )
  161.         // 解绑VAO
  162.         GLES30.glBindVertexArray(0)
  163.         // 解绑IBO
  164.         GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0)
  165.     }
  166.     // 计算GLSurfaceView变换矩阵
  167.     fun computeMVPMatrix(width: Int, height: Int) {
  168.         // 正交投影矩阵
  169.         takeIf { width > height }?.let {
  170.             mViewPortRatio = (width * 1f) / height
  171.             Matrix.orthoM(
  172.                 mProjectionMatrix, // 正交投影矩阵
  173.                 NO_OFFSET, // 偏移量
  174.                 -mViewPortRatio, // 近平面的坐标系左边界
  175.                 mViewPortRatio, // 近平面的坐标系右边界
  176.                 -1f, // 近平面的坐标系的下边界
  177.                 1f, // 近平面坐标系的上边界
  178.                 0f, // 近平面距离相机距离
  179.                 1f // 远平面距离相机距离
  180.             )
  181.         } ?: run {
  182.             mViewPortRatio = (height * 1f) / width
  183.             Matrix.orthoM(
  184.                 mProjectionMatrix, // 正交投影矩阵
  185.                 NO_OFFSET, // 偏移量
  186.                 -1f, // 近平面坐标系左边界
  187.                 1f, // 近平面坐标系右边界
  188.                 -mViewPortRatio, // 近平面坐标系下边界
  189.                 mViewPortRatio, // 近平面坐标系上边界
  190.                 0f, // 近平面距离相机距离
  191.                 1f // 远平面距离相机距离
  192.             )
  193.         }
  194.         // 设置相机矩阵
  195.         // 相机位置(0f, 0f, 1f)
  196.         // 物体位置(0f, 0f, 0f)
  197.         // 相机方向(0f, 1f, 0f)
  198.         Matrix.setLookAtM(
  199.             mViewMatrix, // 相机矩阵
  200.             NO_OFFSET, // 偏移量
  201.             0f, // 相机位置x
  202.             0f, // 相机位置y
  203.             1f, // 相机位置z
  204.             0f, // 物体位置x
  205.             0f, // 物体位置y
  206.             0f, // 物体位置z
  207.             0f, // 相机上方向x
  208.             1f, // 相机上方向y
  209.             0f // 相机上方向z
  210.         )
  211.         // 最终变化矩阵
  212.         Matrix.multiplyMM(
  213.             mMVPMatrix, // 最终变化矩阵
  214.             NO_OFFSET, // 偏移量
  215.             mProjectionMatrix, // 投影矩阵
  216.             NO_OFFSET, // 投影矩阵偏移量
  217.             mViewMatrix, // 相机矩阵
  218.             NO_OFFSET // 相机矩阵偏移量
  219.         )
  220.         // 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系
  221.         // 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变
  222.         Matrix.scaleM(
  223.             mMVPMatrix,
  224.             NO_OFFSET,
  225.             1f,
  226.             -1f,
  227.             1f,
  228.         )
  229.     }
  230.     // 初始化帧缓冲
  231.     fun initFrameBuffer(){
  232.         // 创建FBO
  233.         GLES30.glGenFramebuffers(mFBO.size, mFBO, NO_OFFSET)
  234.         // 绑定FBO
  235.         GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])
  236.             // 创建空纹理
  237.             GLES30.glGenTextures(mFBOTextureID.size, mFBOTextureID, NO_OFFSET)
  238.             // 绑定空纹理
  239.             GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mFBOTextureID[0])
  240.             // 设置纹理参数
  241.             GLES30.glTexParameteri(
  242.                 GLES30.GL_TEXTURE_2D,
  243.                 GLES30.GL_TEXTURE_MIN_FILTER,
  244.                 GLES30.GL_LINEAR
  245.             ) // 纹理缩小时使用线性插值
  246.             GLES30.glTexParameteri(
  247.                 GLES30.GL_TEXTURE_2D,
  248.                 GLES30.GL_TEXTURE_MAG_FILTER,
  249.                 GLES30.GL_LINEAR
  250.             ) // 纹理放大时使用线性插值
  251.             GLES30.glTexParameteri(
  252.                 GLES30.GL_TEXTURE_2D,
  253.                 GLES30.GL_TEXTURE_WRAP_S,
  254.                 GLES30.GL_CLAMP_TO_EDGE
  255.             ) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充
  256.             GLES30.glTexParameteri(
  257.                 GLES30.GL_TEXTURE_2D,
  258.                 GLES30.GL_TEXTURE_WRAP_T,
  259.                 GLES30.GL_CLAMP_TO_EDGE
  260.             ) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充
  261.             GLES30.glTexImage2D(
  262.                 GLES30.GL_TEXTURE_2D, // 纹理类型
  263.                 NO_OFFSET, // 偏移量
  264.                 GLES30.GL_RGBA, // 颜色通道
  265.                 mFrameBufferWidth, // 纹理宽度
  266.                 mFrameBufferHeight, // 纹理高度
  267.                 NO_OFFSET, // 偏移量
  268.                 GLES30.GL_RGBA, // 颜色通道
  269.                 GLES30.GL_UNSIGNED_BYTE, // 颜色数据类型
  270.                 null // 不传入颜色数据
  271.             )
  272.         // 绑定空纹理到FBO,用于绘制到FBO
  273.         GLES30.glFramebufferTexture2D(
  274.             GLES30.GL_FRAMEBUFFER, // FBO
  275.             GLES30.GL_COLOR_ATTACHMENT0, // 颜色缓冲区
  276.             GLES30.GL_TEXTURE_2D, // 纹理类型
  277.             mFBOTextureID[0], // 纹理ID
  278.             0
  279.         )
  280.         if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
  281.             Log.e("yang", "initFrameBuffer: FBO初始化失败")
  282.         }
  283.         // 解绑FBO纹理
  284.         GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
  285.         // 解绑FBO
  286.         GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
  287.     }
  288.     // 离屏渲染
  289.     fun drawOffScreen() {
  290.         takeIf { mFrameBufferBitmap == null }?.let {
  291.             // 帧缓冲绘制纹理需要修改视口大小,这里需要进行视口的保存和恢复
  292.             val viewport = IntArray(4)
  293.             GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewport, NO_OFFSET)
  294.                 // 绑定FBO
  295.                 GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])
  296.                     // 设置视口为FBO尺寸
  297.                     GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)
  298.                     // 计算帧缓冲纹理宽高的变换矩阵
  299.                     computeFrameBufferMVPMatrix()
  300.                     // 清除FBO
  301.                     GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
  302.                     // 绘制图形到FBO, FBO上的内容不会显示
  303.                     drawFrameBuffer()
  304.                     // 缓存FBO内容到Bitmap
  305.                     getBitmapFromFrameBuffer()
  306.                 // 解绑FBO,恢复默认帧缓冲
  307.                 GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
  308.             // 恢复原始视口
  309.             GLES30.glViewport(viewport[0], viewport[1], viewport[2], viewport[3])
  310.         }
  311.     }
  312.     // 使用着色器程序绘制图形
  313.     fun drawSomething() {
  314.         // 解析变换矩阵
  315.         val matrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")
  316.         GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mMVPMatrix, NO_OFFSET)
  317.         // 绑定VAO
  318.         GLES30.glBindVertexArray(mVAO[0])
  319.             // 绘制图形
  320.             GLES30.glDrawElements(
  321.                 GLES30.GL_TRIANGLES,
  322.                 index.size,
  323.                 GLES30.GL_UNSIGNED_SHORT,
  324.                 NO_OFFSET
  325.             )
  326.         // 解绑VAO
  327.         GLES30.glBindVertexArray(0)
  328.     }
  329.     fun drawFrameBuffer(){
  330.         // 解析帧缓冲变换矩阵
  331.         val matrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")
  332.         GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mFrameBufferMVPMatrix, NO_OFFSET)
  333.         // 绑定VAO
  334.         GLES30.glBindVertexArray(mVAO[0])
  335.             // 绘制图形
  336.             GLES30.glDrawElements(
  337.                 GLES30.GL_TRIANGLES,
  338.                 index.size,
  339.                 GLES30.GL_UNSIGNED_SHORT,
  340.                 NO_OFFSET
  341.             )
  342.         // 解绑VAO
  343.         GLES30.glBindVertexArray(0)
  344.     }
  345.     fun enableTexture() {
  346.         // 激活纹理编号0
  347.         GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
  348.         GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[0])
  349.         val textureSampleHandle = GLES30.glGetUniformLocation(mProgram, "uTexture_0")
  350.         GLES30.glUniform1i(textureSampleHandle, 0)
  351.         // 激活纹理编号1
  352.         GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
  353.         GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[1])
  354.         val textureSampleHandle1 = GLES30.glGetUniformLocation(mProgram, "uTexture_1")
  355.         GLES30.glUniform1i(textureSampleHandle1, 1)
  356.     }
  357.     fun disableTexture() {
  358.         GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
  359.     }
  360.     fun initTexture0(context: Context, resourceId: Int){
  361.         mTextureID[0] = loadTexture(context, resourceId)
  362.     }
  363.     fun initTexture1(context: Context, resourceId: Int){
  364.         mTextureID[1] = loadTexture(context, resourceId)
  365.     }
  366.     // 加载纹理
  367.     fun loadTexture(context: Context, resourceId: Int): Int {
  368.         val textureId = IntArray(1)
  369.         // 生成纹理
  370.         GLES30.glGenTextures(1, textureId, 0)
  371.         // 绑定纹理
  372.         GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureId[0])
  373.         // 设置纹理参数
  374.         GLES30.glTexParameteri(
  375.             GLES30.GL_TEXTURE_2D,
  376.             GLES30.GL_TEXTURE_MIN_FILTER,
  377.             GLES30.GL_LINEAR
  378.         ) // 纹理缩小时使用线性插值
  379.         GLES30.glTexParameteri(
  380.             GLES30.GL_TEXTURE_2D,
  381.             GLES30.GL_TEXTURE_MAG_FILTER,
  382.             GLES30.GL_LINEAR
  383.         ) // 纹理放大时使用线性插值
  384.         GLES30.glTexParameteri(
  385.             GLES30.GL_TEXTURE_2D,
  386.             GLES30.GL_TEXTURE_WRAP_S,
  387.             GLES30.GL_CLAMP_TO_EDGE
  388.         ) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充
  389.         GLES30.glTexParameteri(
  390.             GLES30.GL_TEXTURE_2D,
  391.             GLES30.GL_TEXTURE_WRAP_T,
  392.             GLES30.GL_CLAMP_TO_EDGE
  393.         ) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充
  394.         // 加载图片
  395.         val options = BitmapFactory.Options().apply {
  396.             inScaled = false // 不进行缩放
  397.         }
  398.         val bitmap = BitmapFactory.decodeResource(context.resources, resourceId, options)
  399.         // 将图片数据加载到纹理中
  400.         GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, bitmap, 0)
  401.         // 释放资源
  402.         bitmap.recycle()
  403.         // 解绑纹理
  404.         GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
  405.         mFrameBufferWidth = max(mFrameBufferWidth, bitmap.width)
  406.         mFrameBufferHeight = max(mFrameBufferHeight, bitmap.height)
  407.         Log.e("yang", "loadTexture: 纹理加载成功 bitmap.width:${bitmap.width} bitmap.height:${bitmap.height}")
  408.         return textureId[0]
  409.     }
  410.     fun getBitmapFromFrameBuffer(){
  411.         // 分配缓冲区来存储像素数据
  412.         val pixelBuffer = ByteBuffer.allocateDirect(mFrameBufferWidth * mFrameBufferHeight * 4)
  413.             .order(ByteOrder.LITTLE_ENDIAN)
  414.         // 读取像素数据
  415.         GLES30.glReadPixels(
  416.             0, 0, mFrameBufferWidth, mFrameBufferHeight,
  417.             GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE,
  418.             pixelBuffer
  419.         )
  420.         // 将ByteBuffer转换为Bitmap
  421.         val bitmap = Bitmap.createBitmap(mFrameBufferWidth, mFrameBufferHeight, Bitmap.Config.ARGB_8888)
  422.         pixelBuffer.rewind()
  423.         bitmap.copyPixelsFromBuffer(pixelBuffer)
  424.         // OpenGL和Android的Y轴方向相反,需要围绕中心垂直翻转
  425.         val matrix = android.graphics.Matrix().apply {
  426.             setScale(1f, -1f, mFrameBufferWidth / 2f, mFrameBufferHeight / 2f)
  427.         }
  428.         mFrameBufferBitmap = Bitmap.createBitmap(
  429.             bitmap, 0, 0, bitmap.width, bitmap.height,
  430.             matrix, true
  431.         )
  432.         bitmap.recycle()
  433.     }
  434.     fun getScreenBitmap() : Bitmap?{
  435.         return mFrameBufferBitmap
  436.     }
  437.     fun computeFrameBufferMVPMatrix() {
  438.         // 正交投影矩阵
  439.         takeIf { mFrameBufferWidth > mFrameBufferHeight }?.let {
  440.             mViewPortRatio = (mFrameBufferWidth * 1f) / mFrameBufferHeight
  441.             Matrix.orthoM(
  442.                 mProjectionMatrix, // 正交投影矩阵
  443.                 NO_OFFSET, // 偏移量
  444.                 -mViewPortRatio, // 近平面的坐标系左边界
  445.                 mViewPortRatio, // 近平面的坐标系右边界
  446.                 -1f, // 近平面的坐标系的下边界
  447.                 1f, // 近平面坐标系的上边界
  448.                 0f, // 近平面距离相机距离
  449.                 1f // 远平面距离相机距离
  450.             )
  451.         } ?: run {
  452.             mViewPortRatio = (mFrameBufferHeight * 1f) / mFrameBufferWidth
  453.             Matrix.orthoM(
  454.                 mProjectionMatrix, // 正交投影矩阵
  455.                 NO_OFFSET, // 偏移量
  456.                 -1f, // 近平面坐标系左边界
  457.                 1f, // 近平面坐标系右边界
  458.                 -mViewPortRatio, // 近平面坐标系下边界
  459.                 mViewPortRatio, // 近平面坐标系上边界
  460.                 0f, // 近平面距离相机距离
  461.                 1f // 远平面距离相机距离
  462.             )
  463.         }
  464.         // 设置相机矩阵
  465.         // 相机位置(0f, 0f, 1f)
  466.         // 物体位置(0f, 0f, 0f)
  467.         // 相机方向(0f, 1f, 0f)
  468.         Matrix.setLookAtM(
  469.             mViewMatrix, // 相机矩阵
  470.             NO_OFFSET, // 偏移量
  471.             0f, // 相机位置x
  472.             0f, // 相机位置y
  473.             1f, // 相机位置z
  474.             0f, // 物体位置x
  475.             0f, // 物体位置y
  476.             0f, // 物体位置z
  477.             0f, // 相机上方向x
  478.             1f, // 相机上方向y
  479.             0f // 相机上方向z
  480.         )
  481.         // 最终变化矩阵
  482.         Matrix.multiplyMM(
  483.             mFrameBufferMVPMatrix, // 最终变化矩阵
  484.             NO_OFFSET, // 偏移量
  485.             mProjectionMatrix, // 投影矩阵
  486.             NO_OFFSET, // 投影矩阵偏移量
  487.             mViewMatrix, // 相机矩阵
  488.             NO_OFFSET // 相机矩阵偏移量
  489.         )
  490.         // 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系
  491.         // 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变
  492.         Matrix.scaleM(
  493.             mFrameBufferMVPMatrix,
  494.             NO_OFFSET,
  495.             1f,
  496.             -1f,
  497.             1f,
  498.         )
  499.     }
  500.     object LoadShaderUtil {
  501.         // 创建着色器对象
  502.         fun loadShader(type: Int, source: String): Int {
  503.             val shader = GLES30.glCreateShader(type)
  504.             GLES30.glShaderSource(shader, source)
  505.             GLES30.glCompileShader(shader)
  506.             return shader
  507.         }
  508.     }
  509. }
复制代码
结果图



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

杀鸡焉用牛刀

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表