欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 高考 > OpenGL ES ->帧缓冲对象(Frame Buffer Object)离屏渲染获取纹理贴图

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

2025/3/12 17:53:38 来源:https://blog.csdn.net/sunshine_guo/article/details/146154936  浏览:    关键词:OpenGL ES ->帧缓冲对象(Frame Buffer Object)离屏渲染获取纹理贴图

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.MyGLSurfaceViewandroid:id="@+id/gl_surface_view"android:layout_width="match_parent"android:layout_height="match_parent" /><!-- 用于显示FBO截图的ImageView --><ImageViewandroid: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图像的按钮 --><Buttonandroid: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: MyGLSurfaceViewprivate lateinit var imageView: ImageViewprivate lateinit var captureButton: Buttonoverride 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? = nullprivate var mWidth = 0private var mHeight = 0override 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 = widthmHeight = heightmDrawData?.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 = -1private var NO_OFFSET = 0private val VERTEX_POS_DATA_SIZE = 3private val TEXTURE_POS_DATA_SIZE = 2// FBO(Frame Buffer Object), 帧缓冲对象,用于存储渲染后的图像private var mFBO = IntArray(1)// VAO(Vertex Array Object), 顶点数组对象, 用于存储VBOprivate var mVAO = IntArray(1)// VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据private var mVBO = IntArray(2)// IBO(Index Buffer Object), 索引缓冲对象,用于存储顶点索引数据private var mIBO = IntArray(1)// 纹理IDprivate var mTextureID = IntArray(2)// FBO中的纹理IDprivate var mFBOTextureID = IntArray(1)// 最终变化矩阵private val mMVPMatrix = FloatArray(16)// 投影矩阵private val mProjectionMatrix = FloatArray(16)// 相机矩阵private val mViewMatrix = FloatArray(16)// 视口比例private var mViewPortRatio = 1f// 帧缓冲中的Bitmapprivate var mFrameBufferBitmap: Bitmap? = null// 帧缓冲宽高private var mFrameBufferWidth = 0private 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 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;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, IBOfun initVertexBuffer() {// 绑定VAOGLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)GLES30.glBindVertexArray(mVAO[0])// 绑定VBOGLES30.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)// 绑定IBOGLES30.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)// 解绑VAOGLES30.glBindVertexArray(0)// 解绑IBOGLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0)}// 计算GLSurfaceView变换矩阵fun computeMVPMatrix(width: Int, height: Int) {// 正交投影矩阵takeIf { width > height }?.let {mViewPortRatio = (width * 1f) / heightMatrix.orthoM(mProjectionMatrix, // 正交投影矩阵NO_OFFSET, // 偏移量-mViewPortRatio, // 近平面的坐标系左边界mViewPortRatio, // 近平面的坐标系右边界-1f, // 近平面的坐标系的下边界1f, // 近平面坐标系的上边界0f, // 近平面距离相机距离1f // 远平面距离相机距离)} ?: run {mViewPortRatio = (height * 1f) / widthMatrix.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, // 相机位置x0f, // 相机位置y1f, // 相机位置z0f, // 物体位置x0f, // 物体位置y0f, // 物体位置z0f, // 相机上方向x1f, // 相机上方向y0f // 相机上方向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(){// 创建FBOGLES30.glGenFramebuffers(mFBO.size, mFBO, NO_OFFSET)// 绑定FBOGLES30.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,用于绘制到FBOGLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, // FBOGLES30.GL_COLOR_ATTACHMENT0, // 颜色缓冲区GLES30.GL_TEXTURE_2D, // 纹理类型mFBOTextureID[0], // 纹理ID0)if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {Log.e("yang", "initFrameBuffer: FBO初始化失败")}// 解绑FBO纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)// 解绑FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}// 离屏渲染fun drawOffScreen() {takeIf { mFrameBufferBitmap == null }?.let {// 帧缓冲绘制纹理需要修改视口大小,这里需要进行视口的保存和恢复val viewport = IntArray(4)GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewport, NO_OFFSET)// 绑定FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])// 设置视口为FBO尺寸GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)// 计算帧缓冲纹理宽高的变换矩阵computeFrameBufferMVPMatrix()// 清除FBOGLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)// 绘制图形到FBO, FBO上的内容不会显示drawFrameBuffer()// 缓存FBO内容到BitmapgetBitmapFromFrameBuffer()// 解绑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)// 绑定VAOGLES30.glBindVertexArray(mVAO[0])// 绘制图形GLES30.glDrawElements(GLES30.GL_TRIANGLES,index.size,GLES30.GL_UNSIGNED_SHORT,NO_OFFSET)// 解绑VAOGLES30.glBindVertexArray(0)}fun drawFrameBuffer(){// 解析帧缓冲变换矩阵val matrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mFrameBufferMVPMatrix, NO_OFFSET)// 绑定VAOGLES30.glBindVertexArray(mVAO[0])// 绘制图形GLES30.glDrawElements(GLES30.GL_TRIANGLES,index.size,GLES30.GL_UNSIGNED_SHORT,NO_OFFSET)// 解绑VAOGLES30.glBindVertexArray(0)}fun enableTexture() {// 激活纹理编号0GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[0])val textureSampleHandle = GLES30.glGetUniformLocation(mProgram, "uTexture_0")GLES30.glUniform1i(textureSampleHandle, 0)// 激活纹理编号1GLES30.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转换为Bitmapval 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) / mFrameBufferHeightMatrix.orthoM(mProjectionMatrix, // 正交投影矩阵NO_OFFSET, // 偏移量-mViewPortRatio, // 近平面的坐标系左边界mViewPortRatio, // 近平面的坐标系右边界-1f, // 近平面的坐标系的下边界1f, // 近平面坐标系的上边界0f, // 近平面距离相机距离1f // 远平面距离相机距离)} ?: run {mViewPortRatio = (mFrameBufferHeight * 1f) / mFrameBufferWidthMatrix.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, // 相机位置x0f, // 相机位置y1f, // 相机位置z0f, // 物体位置x0f, // 物体位置y0f, // 物体位置z0f, // 相机上方向x1f, // 相机上方向y0f // 相机上方向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}}
}

效果图

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词