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

Android Opengl(九)FBO帧缓冲示例

FBO介绍

用于写入颜色值的颜色缓冲、用于写入深度信息的深度缓冲和允许我们根据一些条件丢弃特定片段的模板缓冲。这些缓冲结合起来叫做帧缓冲(Framebuffer)。

  • OpenGL允许我们定义我们自己的帧缓冲,也就是说我们能够定义我们自己的颜色缓冲,甚至是深度缓冲和模板缓冲。

  • 默认情况下,OpenGL渲染的目标是屏幕,但如果你不想直接渲染到屏幕上,还需要对渲染结果做某些后期处理、渲染到纹理、阴影映射等操作,便可以使用帧缓冲对象,实现离屏渲染。

  • 通过使用帧缓冲对象,可以实现离屏渲染、多重渲染目标(MRT)等高级渲染技术,而不必直接渲染到屏幕。

  • FBO本身不能用于渲染,只有添加了纹理或者渲染缓冲区之后,才能作为渲染目标。

FBO使用流程

  • 生成FBO,glGenFramebuffers()
  • 绑定FBO,glBindFramebuffer()
  • 生成空的颜色纹理,
       GLES30.glGenTextures(1, colorTexureId, 0)
       GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, colorTexureId[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)

            //生成2D纹理
       GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA,width, height,0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null)

  • FBO与空纹理绑定
  GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER,
            GLES30.GL_COLOR_ATTACHMENT0,
            GLES30.GL_TEXTURE_2D,
            frameBufferTextureIds[0],
            0)
  • FBO解绑 GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)

FBO实战示例

示例渲染过程:原图进行lut滤镜灰度后再进行转场,最终渲染到屏幕
在这里插入图片描述
Activity代码

class SimpleFBOMultiHandleActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.fragment_common_sample)
        Util.context = this
        val glSurfaceView = findViewById<GLSurfaceView>(R.id.glsurfaceview)
        glSurfaceView.setEGLContextClientVersion(3)
        glSurfaceView.setRenderer(SamplerFBOMultiRenderer(BitmapFactory.decodeResource(resources, R.drawable.flower)))
        glSurfaceView?.renderMode = GLSurfaceView.RENDERMODE_CONTINUOUSLY
    }
}

布局

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.opengl.GLSurfaceView
        android:id="@+id/glsurfaceview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

核心操作

SamplerFBOMultiRenderer


import android.graphics.Bitmap
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.opengl.Matrix
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10

class SamplerFBOMultiRenderer(val bitmap:Bitmap) : GLSurfaceView.Renderer {

    private val imageRender by lazy { GLImageRender(bitmap) }
    private val imageFilterRender by lazy { GLImageFilterRender() }
    private val imageTransitionRender by lazy { GLImageTransitionRender() }
    private val fboRender by lazy { GLFBOMultiRender() }
    private val fboRender2 by lazy { GLFBOMultiRender() }
    private val fboRender3 by lazy { GLFBOMultiRender() }

    override fun onDrawFrame(gl: GL10?) {
        // 绑定第一个FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fboRender.getFrameBufferId())
       // 绘制图片进行绘制
        imageRender.draw()
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE)

        // 绑定第二个FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fboRender2.getFrameBufferId())
        // 将第一个FBO的绘制结果,作为滤镜渲染的输入纹理
        imageFilterRender.updateTexure(fboRender.getTexureId())
        imageFilterRender.draw() // 进行滤镜处理
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE)

        // 绑定第三个FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, fboRender3.getFrameBufferId())
        //  将第2个FBO的绘制结果(灰度过的),作为转场渲染的输入纹理
        imageTransitionRender.updateTexure(fboRender2.getTexureId())
        imageTransitionRender.draw()
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, GLES30.GL_NONE)

        // 上屏绘制
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 6)

    }

    override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
        imageRender.onSurfaceChanged(gl,width,height)
        imageFilterRender.onSurfaceChanged(gl,width,height)
        fboRender.onSurfaceChanged(gl,width,height)
        fboRender2.onSurfaceChanged(gl,width,height)
        fboRender3.onSurfaceChanged(gl,width,height)
        imageTransitionRender.onSurfaceChanged(gl,width,height)
        val imgRatio = bitmap.width.toFloat().div(bitmap.height)
        val windowRatio = width.toFloat().div(height)
        GLES30.glViewport(0, 0, width, height)
        // 根据比例调整大小
        if (imgRatio > windowRatio) {
            Matrix.orthoM(imageTransitionRender.getTranslateMatrix(), 0, -1f, 1f, -imgRatio * 2f, imgRatio * 2f, 0f, 10f);
        } else {
            Matrix.orthoM(imageTransitionRender.getTranslateMatrix(), 0, -(1 / imgRatio) * 2f, (1 / imgRatio) * 2f, -1f, 1f, 0f, 10f);
        }
    }

    override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {
        imageRender.onSurfaceCreated(gl,config)
        imageFilterRender.onSurfaceCreated(gl,config)
        fboRender.onSurfaceCreated(gl,config)
        imageTransitionRender.onSurfaceCreated(gl,config)
        fboRender2.onSurfaceCreated(gl,config)
        fboRender3.onSurfaceCreated(gl,config)
    }
}

GLImageRender



import android.graphics.Bitmap
import android.opengl.GLES30
import android.opengl.Matrix
import com.df.openglstudydemo.util.ShaderUtil
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10


class GLImageRender(val bitmap: Bitmap) {/*
    * 顶点位置程序
    */

    private val vertexShaderCode =
        "uniform mat4 uTMatrix;" + "attribute vec4 aPosition;" + "attribute vec2 aTexCoord;" + "varying vec2 vTexCoord;" + "void main() { " + "   gl_Position = uTMatrix * aPosition;" + "   vTexCoord = aTexCoord;" + "}"

    private val fragmentShaderCode0_2 =
        "precision mediump float;" + "uniform sampler2D uSampler;" + "varying vec2 vTexCoord;" + "void main() { " + "   gl_FragColor = texture2D(uSampler,vTexCoord);" + "}"


    private val vertexData = floatArrayOf(-1f, -1f, -1f, 1f, 1f, 1f, -1f, -1f, 1f, 1f, 1f, -1f)
    private lateinit var vertexDataBuffer: FloatBuffer

    private val textureCoordinateData0 = floatArrayOf(0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 1f, 0f, 1f, 1f)
    private lateinit var textureCoordinateDataBuffer0: FloatBuffer

    private var programId0 = 0
    private var imageTexture = 0

    private var glSurfaceViewWidth = 0
    private var glSurfaceViewHeight = 0
    private var samplerHandle: Int = 0
    private var uMatrixHandle: Int = 0

    private val translateMatrix = FloatArray(16)

    fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {

        // 初始化坐标、图片数据
        // Init the coordinates and image texture
        initData()

        // 创建2个GL Program,第一个将图片的蓝色通道全部设为0.5,第二做水平方向模糊
        // Create two GL programs, and one is used for set the blue channel to 0.5, while the other is used for horizontal blur
        programId0 = ShaderUtil.createGlProgram(vertexShaderCode, fragmentShaderCode0_2)
        samplerHandle = GLES30.glGetUniformLocation(programId0, "uSampler")
        uMatrixHandle = GLES30.glGetUniformLocation(programId0, "uTMatrix")
        Matrix.setIdentityM(translateMatrix, 0)
    }

    private fun initData() {
        vertexDataBuffer =
            ByteBuffer.allocateDirect(vertexData.size * java.lang.Float.SIZE / 8).order(ByteOrder.nativeOrder())
                .asFloatBuffer()
        vertexDataBuffer.put(vertexData)
        vertexDataBuffer.position(0)

        textureCoordinateDataBuffer0 = ByteBuffer.allocateDirect(textureCoordinateData0.size * java.lang.Float.SIZE / 8)
            .order(ByteOrder.nativeOrder()).asFloatBuffer()
        textureCoordinateDataBuffer0.put(textureCoordinateData0)
        textureCoordinateDataBuffer0.position(0)

        // 创建图片纹理
        // Create texture
        val textures = IntArray(1)
        ShaderUtil.loadTexture(bitmap, textures)
        imageTexture = textures[0]
    }

    fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
        glSurfaceViewWidth = width
        glSurfaceViewHeight = height
        // 计算并设置窗口大小
        val imgRatio = bitmap.width.toFloat().div(bitmap.height)
        val windowRatio = width.toFloat().div(height)
        GLES30.glViewport(0, 0, glSurfaceViewWidth, glSurfaceViewHeight)
        if (imgRatio > windowRatio) {
            Matrix.orthoM(translateMatrix, 0, -1f, 1f, -imgRatio * 2f, imgRatio * 2f, 0f, 10f);
        } else {
            Matrix.orthoM(translateMatrix, 0, -(1 / imgRatio) * 2f, (1 / imgRatio) * 2f, -1f, 1f, 0f, 10f);
        }
    }

    fun draw() {
        realDraw(programId0, imageTexture, textureCoordinateDataBuffer0)
    }


    private fun realDraw(programId: Int, texture: Int, textureCoordinateDataBuffer: FloatBuffer) {

        // 应用GL程序
        GLES30.glUseProgram(programId)

        // 获取字段a_position在shader中的位置
        val aPositionLocation = GLES30.glGetAttribLocation(programId, "aPosition")

        // 启动对应位置的参数
        GLES30.glEnableVertexAttribArray(aPositionLocation)

        // 指定a_position所使用的顶点数据
        GLES30.glVertexAttribPointer(aPositionLocation, 2, GLES30.GL_FLOAT, false, 0, vertexDataBuffer)

        // 获取字段a_textureCoordinate在shader中的位置
        val aTextureCoordinateLocation = GLES30.glGetAttribLocation(programId, "aTexCoord")

        // 启动对应位置的参数
        GLES30.glEnableVertexAttribArray(aTextureCoordinateLocation)

        // 指定a_textureCoordinate所使用的顶点数据
        GLES30.glVertexAttribPointer(aTextureCoordinateLocation,
            2,
            GLES30.GL_FLOAT,
            false,
            0,
            textureCoordinateDataBuffer)
        GLES30.glUniformMatrix4fv(uMatrixHandle, 1, false, translateMatrix, 0)
        // 绑定纹理并设置u_texture参数
        // Bind the texture and set the u_texture parameter
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texture)
        val uTextureLocation = GLES30.glGetAttribLocation(programId, "vTexCoord")
        GLES30.glUniform1i(uTextureLocation, 0)
        GLES30.glUniform1i(samplerHandle, 0)
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 6)
    }
}

GLImageFilterRender


import android.opengl.GLES30
import android.opengl.Matrix
import com.df.openglstudydemo.util.ShaderUtil
import com.df.openglstudydemo.util.Util
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10


class GLImageFilterRender() {
    /*
    * 顶点位置程序
    */

    private val vertexShaderCode =
        "uniform mat4 uTMatrix;" +
                "attribute vec4 aPosition;" +
                "attribute vec2 aTexCoord;" +
                "varying vec2 vTexCoord;" +
                "void main() { " +
                "   gl_Position = uTMatrix * aPosition;" +
                "   vTexCoord = aTexCoord;" +
                "}"

    /**
     * 片元颜色程序
     */
    private val fragmentShader_LUT =
                "precision mediump float;" +
                "uniform sampler2D uSampler;" +
                "uniform sampler2D uLutSampler;" +
                "varying vec2 vTexCoord;" +
                "void main() { " +
                "   vec4 texColor = texture2D(uSampler,vTexCoord);" +
                "   float blueColor = texColor.b * 63.0;" +
                "   vec2 quad1,quad2;" +
                "   quad1.y = floor(floor(blueColor) /8.0);" +
                "   quad1.x = floor(blueColor)- (quad1.y * 8.0);" +
                "   quad2.y = floor(ceil(blueColor) /8.0);" +
                "   quad2.x = ceil(blueColor)- (quad2.y * 8.0);" +
                "   vec2 texPos1,texPos2;" +
                "   texPos1.x = (quad1.x * 0.125) + 0.5/512.0 +(0.125-1.0/512.0) * texColor.r;" +
                "   texPos1.y = (quad1.y * 0.125) + 0.5/512.0 +(0.125-1.0/512.0) * texColor.g;" +
                "   texPos2.x = (quad2.x * 0.125) + 0.5/512.0 +(0.125-1.0/512.0) * texColor.r;" +
                "   texPos2.y = (quad2.y * 0.125) + 0.5/512.0 +(0.125-1.0/512.0) * texColor.g;" +
                "   vec4 newColor1 = texture2D(uLutSampler,texPos1);" +
                "   vec4 newColor2 = texture2D(uLutSampler,texPos2);" +
                "   vec4 newColor = mix(newColor1,newColor2,fract(blueColor));" +
                "   gl_FragColor = newColor;" +
                "}"


    private val vertexData = floatArrayOf(-1f, -1f, -1f, 1f, 1f, 1f, -1f, -1f, 1f, 1f, 1f, -1f)
    private lateinit var vertexDataBuffer: FloatBuffer

    private val textureCoordinateData0 = floatArrayOf(0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 1f, 0f, 1f, 1f)
    private lateinit var textureCoordinateDataBuffer0: FloatBuffer

    private var programId0 = 0
    private var inputTexureId = 0
    private var lutTextureId= 0

    private var glSurfaceViewWidth = 0
    private var glSurfaceViewHeight = 0
    private var translateMatrix = FloatArray(16)

    fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {

        // 初始化坐标、图片数据
        // Init the coordinates and image texture
        initData()
        // 创建2个GL Program,第一个将图片的蓝色通道全部设为0.5,第二做水平方向模糊
        // Create two GL programs, and one is used for set the blue channel to 0.5, while the other is used for horizontal blur
        programId0 = ShaderUtil.createGlProgram(vertexShaderCode, fragmentShader_LUT)
        Matrix.setIdentityM(translateMatrix, 0)
    }

    private fun initData() {
        vertexDataBuffer =
            ByteBuffer.allocateDirect(vertexData.size * java.lang.Float.SIZE / 8).order(ByteOrder.nativeOrder())
                .asFloatBuffer()
        vertexDataBuffer.put(vertexData)
        vertexDataBuffer.position(0)

        textureCoordinateDataBuffer0 = ByteBuffer.allocateDirect(textureCoordinateData0.size * java.lang.Float.SIZE / 8)
            .order(ByteOrder.nativeOrder()).asFloatBuffer()
        textureCoordinateDataBuffer0.put(textureCoordinateData0)
        textureCoordinateDataBuffer0.position(0)

        // 加载默认的滤镜图片
        val textures = IntArray(1)
        ShaderUtil.loadTexture(Util.decodeBitmapFromAssets("lut_d.png"), textures)
        lutTextureId = textures[0]
    }



    fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
        glSurfaceViewWidth = width
        glSurfaceViewHeight = height
        GLES30.glViewport(0, 0, glSurfaceViewWidth, glSurfaceViewHeight)
    }

    fun updateTexure(inputTexure: Int) {
        this.inputTexureId = inputTexure
    }

    fun draw() {
        realDraw(programId0, inputTexureId, textureCoordinateDataBuffer0)
    }

    private fun realDraw(programId: Int, texture: Int, textureCoordinateDataBuffer: FloatBuffer) {

        // 应用GL程序
        // Use the GL program
        GLES30.glUseProgram(programId)

        val aPositionLocation = GLES30.glGetAttribLocation(programId, "aPosition")
        GLES30.glEnableVertexAttribArray(aPositionLocation)
        GLES30.glVertexAttribPointer(aPositionLocation, 2, GLES30.GL_FLOAT, false, 0, vertexDataBuffer)

        val aTextureCoordinateLocation = GLES30.glGetAttribLocation(programId, "aTexCoord")
        GLES30.glEnableVertexAttribArray(aTextureCoordinateLocation)
        GLES30.glVertexAttribPointer(aTextureCoordinateLocation,
            2, GLES30.GL_FLOAT, false, 0, textureCoordinateDataBuffer)

        val uMatrixHandle = GLES30.glGetUniformLocation(programId, "uTMatrix")
        GLES30.glUniformMatrix4fv(uMatrixHandle, 1, false, translateMatrix, 0)
        // 绑定纹理并设置u_texture参数
        val uSimplerHandle = GLES30.glGetUniformLocation(programId, "uSampler")
        val uLutSimplerHandle = GLES30.glGetUniformLocation(programId, "uLutSampler")
        // 激活纹理单元
        GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
        // 绑定纹理单元
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texture)
        // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE0,因此传递0
        GLES30.glUniform1i(uSimplerHandle, 1)

        // 激活纹理单元
        GLES30.glActiveTexture(GLES30.GL_TEXTURE2)
        // 绑定纹理单元
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, lutTextureId)
        // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE1,因此传递1
        GLES30.glUniform1i(uLutSimplerHandle, 2)
        GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0,6)
    }

}

GLImageTransitionRender(转场render,转场的顶点shader和fragment参考:
https://gl-transitions.com/gallery](https://gl-transitions.com/gallery)


import android.graphics.BitmapFactory
import android.opengl.GLES30
import android.opengl.Matrix
import com.df.openglstudydemo.R
import com.df.openglstudydemo.util.ShaderUtil
import com.df.openglstudydemo.util.Util
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10


class GLImageTransitionRender() {
    private val vertexData = floatArrayOf(-1f, -1f, -1f, 1f, 1f, 1f, -1f, -1f, 1f, 1f, 1f, -1f)
    private val VERTEX_COMPONENT_COUNT = 2
    private lateinit var vertexDataBuffer: FloatBuffer

    private val textureCoordinateData0 = floatArrayOf(0f, 1f, 0f, 0f, 1f, 0f, 0f, 1f, 1f, 0f, 1f, 1f)
    private lateinit var textureCoordinateDataBuffer0: FloatBuffer

    private var programId0 = 0
    private var inputTexureId = 0
    private var inputTexureIds= IntArray(1)
    private var dstTextureIds = IntArray(1)
    private var displacementMapIds = IntArray(1)

    private var glSurfaceViewWidth = 0
    private var glSurfaceViewHeight = 0
    private var translateMatrix = FloatArray(16)
    private var positionHandle: Int = -1
    private var texCoordHandle: Int = -1
    private var samplerHandle: Int = -1
    private var dstSamplerHandle: Int = -1
    private var displacementMapSamplerHandle: Int = -1
    private var progressHandle: Int = -1
    private var uMatrixHandle: Int = -1
    private val FRAME_SIZE = 240
    private var frameIndex = 0

    fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {

        // 初始化坐标、图片数据
        // Init the coordinates and image texture
        initData()

        val vertexShader = ShaderUtil.readFileFromAssets("vertex.glsl", Util.context)
        val fragmentShader = ShaderUtil.readFileFromAssets("fragment_circle.glsl", Util.context)
        programId0 = ShaderUtil.createGlProgram(vertexShader, fragmentShader)

        Matrix.setIdentityM(translateMatrix, 0)

        // 将数据传递shader
        positionHandle = GLES30.glGetAttribLocation(programId0, "aPosition")
        GLES30.glEnableVertexAttribArray(positionHandle)

        texCoordHandle = GLES30.glGetAttribLocation(programId0, "aTexCoord")
        GLES30.glEnableVertexAttribArray(texCoordHandle)

        uMatrixHandle = GLES30.glGetUniformLocation(programId0, "uTMatrix")
        samplerHandle = GLES30.glGetUniformLocation(programId0, "uSampler")
        dstSamplerHandle = GLES30.glGetUniformLocation(programId0, "uDstSampler")
        displacementMapSamplerHandle = GLES30.glGetUniformLocation(programId0, "displacementMap")
        progressHandle = GLES30.glGetUniformLocation(programId0, "progress")

        ShaderUtil.loadTexture(BitmapFactory.decodeResource(Util.context.resources,R.drawable.maomi), dstTextureIds)
        ShaderUtil.loadTexture(BitmapFactory.decodeResource(Util.context.resources, R.drawable.displacementmap),
            displacementMapIds)
    }

    private fun initData() {
        vertexDataBuffer =
            ByteBuffer.allocateDirect(vertexData.size * java.lang.Float.SIZE / 8).order(ByteOrder.nativeOrder())
                .asFloatBuffer()
        vertexDataBuffer.put(vertexData)
        vertexDataBuffer.position(0)

        textureCoordinateDataBuffer0 = ByteBuffer.allocateDirect(textureCoordinateData0.size * java.lang.Float.SIZE / 8)
            .order(ByteOrder.nativeOrder()).asFloatBuffer()
        textureCoordinateDataBuffer0.put(textureCoordinateData0)
        textureCoordinateDataBuffer0.position(0)
    }



    fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
        glSurfaceViewWidth = width
        glSurfaceViewHeight = height
        GLES30.glViewport(0, 0, glSurfaceViewWidth, glSurfaceViewHeight)
    }

    fun updateTexure(inputTexure: Int) {
        this.inputTexureId = inputTexure
    }


    fun draw() {
        bindGLProgram(programId0, inputTexureId, textureCoordinateDataBuffer0)
    }

    private fun bindGLProgram(programId: Int, texture: Int, textureCoordinateDataBuffer: FloatBuffer) {

        GLES30.glUseProgram(programId)
        programId?.let {
            GLES30.glEnableVertexAttribArray(positionHandle)
            GLES30.glVertexAttribPointer(positionHandle, 2, GLES30.GL_FLOAT, false, 0, vertexDataBuffer)

            GLES30.glEnableVertexAttribArray(texCoordHandle)
            GLES30.glVertexAttribPointer(texCoordHandle,
                2, GLES30.GL_FLOAT, false, 0, textureCoordinateDataBuffer)

        }

            GLES30.glUniformMatrix4fv(uMatrixHandle, 1, false, translateMatrix, 0)

            // 激活纹理单元
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            // 绑定纹理单元
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, texture)
            // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE0,因此传递0
            GLES30.glUniform1i(samplerHandle, 0)
            if (dstSamplerHandle > 0) {
                // 激活纹理单元
                GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
                // 绑定纹理单元
                GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, dstTextureIds[0])
                // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE1,因此传递1
                GLES30.glUniform1i(dstSamplerHandle, 1)
            }
            // 激活纹理单元
            GLES30.glActiveTexture(GLES30.GL_TEXTURE2)
            // 绑定纹理单元
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, displacementMapIds[0])
            // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE1,因此传递1
            GLES30.glUniform1i(displacementMapSamplerHandle, 2)

            val progress = (frameIndex++ % FRAME_SIZE) * 1.0 / FRAME_SIZE
            if (progressHandle > 0) {
                GLES30.glUniform1f(progressHandle, progress.toFloat())
            }
            GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 6)
    }

    fun getTranslateMatrix() = translateMatrix
}

vertex.glsl

uniform mat4 uTMatrix;
attribute vec4 aPosition;
attribute vec2 aTexCoord;
varying vec2 vTexCoord;
void main() {
    gl_Position = uTMatrix * aPosition;
    vTexCoord = aTexCoord;
}

fragment_circle.glsl

uniform sampler2D uSmapler;
uniform sampler2D uDstSampler;
varying vec2 vTexCoord;
uniform float progress;

vec4 getFromColor(vec2 uv){
    return texture2D(uSmapler, uv);
}
vec4 getDstColor(vec2 uv){
    return texture2D(uDstSampler, uv);
}
uniform sampler2D displacementMap;
vec4 transition (vec2 uv) {
    float displacement = texture2D(displacementMap, uv).r * 0.5;

    vec2 uvFrom = vec2(uv.x + progress * displacement, uv.y);
    vec2 uvTo = vec2(uv.x - (1.0 - progress) * displacement, uv.y);

    return mix(
    getFromColor(uvFrom),
    getDstColor(uvTo),
    progress
    );
}

void main(){
    gl_FragColor=transition(vTexCoord);
}

GLFBOMultiRender


import android.opengl.GLES30
import android.opengl.Matrix
import com.df.openglstudydemo.util.ShaderUtil
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10


class GLFBOMultiRender() {
    /*
 * 顶点位置程序
 */
    private val vertexShaderCode =
        "attribute vec4 aPosition;" +
                "attribute vec2 aTexCoord;" +
                "varying vec2 vTexCoord;" +
                "void main() { " +
                "   gl_Position = aPosition;" +
                "   vTexCoord = aTexCoord;" +
                "}"

    private val fragmentShaderCode0_2 =
                "precision mediump float;" +
                "uniform sampler2D uSampler;" +
                "varying vec2 vTexCoord;" +
                "void main() { " +
                "   gl_FragColor = texture2D(uSampler,vTexCoord);" +
                "}"

    private var programId1 = 0

    private val vertexData = floatArrayOf(-1f, -1f, -1f, 1f, 1f, 1f, -1f, -1f, 1f, 1f, 1f, -1f)
    private val VERTEX_COMPONENT_COUNT = 2
    private lateinit var vertexDataBuffer : FloatBuffer
    private val textureCoordinateData1 = floatArrayOf(0f, 0f, 0f, 1f, 1f, 1f, 0f, 0f, 1f, 1f, 1f, 0f)
    private lateinit var textureCoordinateDataBuffer1 : FloatBuffer
    private var glSurfaceViewWidth = 0
    private var glSurfaceViewHeight = 0

    // 帧缓存
    // frame buffer
    private var frameBuffer = 0
    private var frameBufferId = IntArray(1)

    // 帧缓绑定的texture
    // the texture bind on the frame buffer
    private var frameBufferTexture = 0
    private var frameBufferTextureIds = IntArray(1)
    private val translateMatrix = FloatArray(16)

    fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {

        // 初始化坐标、图片数据
        initData()

        programId1 = ShaderUtil.createGlProgram(vertexShaderCode,fragmentShaderCode0_2)
        Matrix.setIdentityM(translateMatrix, 0)
    }

    private fun initData() {

        // 将三角形顶点数据放入buffer中
        vertexDataBuffer = ByteBuffer.allocateDirect(vertexData.size * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
        vertexDataBuffer.put(vertexData)
        vertexDataBuffer.position(0)

        textureCoordinateDataBuffer1 = ByteBuffer.allocateDirect(textureCoordinateData1.size * 4)
            .order(ByteOrder.nativeOrder())
            .asFloatBuffer()
        textureCoordinateDataBuffer1.put(textureCoordinateData1)
        textureCoordinateDataBuffer1.position(0)
    }



    fun getFrameBufferId() = frameBufferId[0]

    fun getTexureId():Int {
        return frameBufferTextureIds[0]
    }


    fun onSurfaceChanged(p0: GL10?, width: Int, height: Int){
        glSurfaceViewWidth = width
        glSurfaceViewHeight = height
        GLES30.glGenFramebuffers(frameBufferId.size, frameBufferId, 0)
        ShaderUtil.createColorTexture(frameBufferTextureIds,glSurfaceViewWidth,glSurfaceViewWidth)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, frameBufferId[0])
        GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER,
            GLES30.GL_COLOR_ATTACHMENT0,
            GLES30.GL_TEXTURE_2D,
            frameBufferTextureIds[0],
            0)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
    }
}

工具类
ShaderUtil


import android.content.Context
import android.graphics.Bitmap
import android.opengl.GLES30
import android.opengl.GLUtils
import android.util.Log

class ShaderUtil {
    companion object {

        fun readFileFromAssets(fileName: String?, context: Context): String? {
            fileName ?: return null
            context ?: return null
            val result = StringBuilder()
            try {
                val myIs = context.assets.open(fileName)
                val buffer = ByteArray(1024)
                var count = 0
                while ((myIs.read(buffer).also { count = it })!= -1) {
                    result.append(String(buffer, 0, count))
                }
                myIs.close()
            } catch (e: Exception) {
                e.printStackTrace()
            }
            return result.toString()
        }

        /**
         * 创建shader,加载shader程序
         */
        private fun loadShader(type: Int, shaderCode: String): Int {
            var shader = GLES30.glCreateShader(type)
            if (shader != 0) {
                GLES30.glShaderSource(shader, shaderCode)
                GLES30.glCompileShader(shader)
                val compiled = IntArray(1)
                GLES30.glGetShaderiv(shader, GLES30.GL_COMPILE_STATUS, compiled, 0)
                if (compiled[0] == 0) {
                    Log.e("wdf", "编译失败=" + GLES30.glGetShaderInfoLog(shader))
                    GLES30.glDeleteShader(shader)
                    shader = 0
                }
            }
            return shader
        }

        /**
         * 加载纹理
         */
         fun loadTexture(bitmap: Bitmap, textureIdArray: IntArray) {
            GLES30.glGenTextures(1, textureIdArray, 0)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIdArray[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)
            // 加载bitmap到纹理中
            GLUtils.texImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA, bitmap, 0)
            GLES30.glGenerateMipmap(GLES30.GL_TEXTURE_2D)
            bitmap.recycle()
            // 取消绑定纹理
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
        }


        fun createGlProgram(vertexShader: String?, fragmentShader: String?): Int {
            if (vertexShader == null || fragmentShader == null) {
                return 0
            }
            val vertexShaderId = loadShader(GLES30.GL_VERTEX_SHADER, vertexShader)
            val fragmentShaderId = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShader)
            if (vertexShaderId == 0 || fragmentShaderId == 0) {
                return 0
            }
            var program = GLES30.glCreateProgram()
            if (program != 0) {
                program.let {
                    // 将顶点着色器加入程序
                    GLES30.glAttachShader(it, vertexShaderId)
                    // 将片元着色器加入程序
                    GLES30.glAttachShader(it, fragmentShaderId)
                    // 链接到着色器程序
                    GLES30.glLinkProgram(it)
                    val linkStatus = IntArray(1)
                    GLES30.glGetProgramiv(program, GLES30.GL_LINK_STATUS, linkStatus, 0)
                    if (linkStatus[0] != GLES30.GL_TRUE) {
                        val info = GLES30.glGetProgramInfoLog(it)
                        GLES30.glDeleteProgram(program)
                        // 打印链接程序日志
                        Log.e("wdf", "link失败==" + info)
                        program = 0
                    }
                }
            }

            return program
        }

        fun createColorTexture(colorTexureId: IntArray,width:Int,height:Int) {
            //绑定纹理缓存到纹理单元
            GLES30.glGenTextures(1, colorTexureId, 0)
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, colorTexureId[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)

            //生成2D纹理
            GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, 0, GLES30.GL_RGBA,width, height,0, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, null)

        }

        fun genBindFrameBuffer(frameBufferIds:IntArray){
            GLES30.glGenFramebuffers(frameBufferIds.size, frameBufferIds, 0)
        }
    }




}

Util


import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.opengl.GLES20
import android.opengl.GLES30
import android.util.Log
import java.io.File
import java.io.FileOutputStream
import java.nio.ByteBuffer
import java.util.UUID

class Util {

    companion object {

        lateinit var context: Context

        fun decodeBitmapFromAssets(filename : String) : Bitmap {
            val options = BitmapFactory.Options()
            options.inSampleSize = 1
            val bitmap = BitmapFactory.decodeStream(context.assets.open(filename))
            if (bitmap == null) {
                Log.e("debug", "bitmap decode fail, path = $filename")
            }
            return bitmap
        }

        fun checkGLError() {
            val error = GLES30.glGetError()
            if (error != GLES30.GL_NO_ERROR) {
                val hexErrorCode = Integer.toHexString(error)
                Log.e("debug", "glError: $hexErrorCode")
                throw RuntimeException("GLError")
            }
        }

        fun getScreenWidth() : Int {
            return 1440
        }

        fun getScreenHeight() : Int {
            return 2560
        }


        fun getSnapshot(width: Int, height: Int): Bitmap? {
            if (width == 0 || height == 0) {
                return null
            }

            val pitch = width * 4
            val byteArray = ByteArray(pitch * height)
            val byteBuffer = ByteBuffer.allocateDirect(pitch * height)
            GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, byteBuffer)
            byteBuffer.rewind()

            val temp = ByteArray(pitch)
            for (i in 0 until height) {
                byteBuffer.get(temp)
                System.arraycopy(temp, 0, byteArray, (height - i - 1) * pitch, pitch)
            }
            byteBuffer.clear()

            val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
            bitmap.copyPixelsFromBuffer(ByteBuffer.wrap(byteArray))

            return bitmap
        }

        fun saveBitmap(bitmap: Bitmap?) {
            bitmap ?: return
            try {
                val parentFile: File = File((context.externalCacheDir).toString())
                parentFile.mkdirs()
                val file: File = File(parentFile.path, UUID.randomUUID().toString() + ".jpg")
                Log.e("wdf", "path==" + file.path)
                val out = FileOutputStream(file)
                bitmap?.compress(Bitmap.CompressFormat.JPEG, 100, out)
                out.flush()
                out.close()
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
        }

    }
}

http://www.kler.cn/a/594185.html

相关文章:

  • UI设计中的对比与统一:构建和谐界面的原则
  • PyTorch模型转ONNX例子
  • Unity URP 实现场景和UI添加后处理
  • 知识库--Milvus
  • WordPress靶场攻略
  • php 要达到go的性能,应该如何优化php
  • 【蓝桥杯python研究生组备赛】005 数学与简单DP
  • 【CXX-Qt】2.1 构建系统
  • Python 编程题 第十一节:选择排序、插入排序、删除字符、目标移动、尾部的0
  • 如何通过 SQLyog 连接远程 MySQL 数据库?(附工具下载)
  • pdf文件分页按需查看
  • 【VolView】纯前端实现CT三维重建-CBCT
  • 数据结构-----队列
  • LM Studio、ollama本地部署运行多个AI
  • 玩转物联网-4G模块如何快速将数据上传到巴法云(TCP篇)
  • Java解析多层嵌套JSON数组并将数据存入数据库示例
  • 软考中级-软件设计师 准备
  • 【redis】AOF 的基本工作机制,顺序写入,文件同步,重写机制
  • JAVA URL和URI差异对比
  • 星型组网和路由器组网的区别