Open GL ES ->GLSurfaceView+离屏渲染滤镜作用的Bitmap+动态顺序叠加滤镜作用链的RecyclerView

XML文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!-- GLSurfaceView 用于显示滤镜效果 -->
    <com.example.myapplication.FilterSurfaceView
        android:id="@+id/glSurfaceView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="4" />

    <!-- 滤镜列表 -->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/filterRecyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginStart="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="20dp"
        android:layout_marginBottom="20dp"
        android:layout_weight="1" />

    <!-- 显示当前强度值 -->
    <TextView
        android:id="@+id/intensityTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="强度: 100%"
        android:textSize="20sp" />

    <!-- 强度调节滑动条 -->
    <SeekBar
        android:id="@+id/intensitySeekBar"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginStart="20dp"
        android:layout_marginEnd="20dp"
        android:layout_weight="1"
        android:max="100"
        android:progress="100" />

</LinearLayout>

Activity代码

class MainActivity4 : AppCompatActivity() {
    private lateinit var glSurfaceView: FilterSurfaceView
    private lateinit var intensityTextView: TextView
    private lateinit var intensitySeekBar: SeekBar
    private lateinit var filterRecyclerView: RecyclerView
    private var mCurrentFilterType = FilterType.ORIGINAL

    private val filters = listOf(
        FilterItem(FilterType.ORIGINAL, "原图"),
        FilterItem(FilterType.COLOR, "颜色变换"),
        FilterItem(FilterType.PIXEL, "像素化"),
        FilterItem(FilterType.EDGE, "边缘检测")
    )

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main4)
        // 初始化UI
        initView()
        // 设置强度滑动条
        initFilterSeekBar()
    }

    private fun initView() {
        glSurfaceView = findViewById(R.id.glSurfaceView)
        intensityTextView = findViewById(R.id.intensityTextView)
        intensitySeekBar = findViewById(R.id.intensitySeekBar)
        filterRecyclerView = findViewById(R.id.filterRecyclerView)
        intensityTextView.visibility = View.GONE
        intensitySeekBar.visibility = View.GONE
    }
    
    private fun initFilterSeekBar() {
        intensitySeekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                val drawData = glSurfaceView?.getDrawData() ?: return
                // 更新当前滤镜的强度值
                drawData.currentFilterStrength = progress.toFloat()

                intensityTextView.text = " ${mCurrentFilterType} -> 强度: $progress%"
                glSurfaceView?.requestRender()
            }

            override fun onStartTrackingTouch(seekBar: SeekBar?) {}

            override fun onStopTrackingTouch(seekBar: SeekBar?) {}
        })
    }

    fun initFilterRecyclerView() {
        filters?.forEach { filter ->
            when (filter.type) {
                FilterType.ORIGINAL -> {
                    filter.thumbnailBitmap = glSurfaceView?.getDrawData()?.mOriginBitmap
                }

                FilterType.EDGE -> {
                    filter.thumbnailBitmap = glSurfaceView?.getDrawData()?.mEdgeFilterBitmap
                }

                FilterType.PIXEL -> {
                    filter.thumbnailBitmap = glSurfaceView?.getDrawData()?.mPixelFilterBitmap
                }

                FilterType.COLOR -> {
                    filter.thumbnailBitmap = glSurfaceView?.getDrawData()?.mColorFilterBitmap
                }
            }
        }

        filterRecyclerView.apply {
            adapter = FilterAdapter(filters, object : FilterItemClickListener {
                override fun onFilterItemClick(filterItem: FilterItem) {
                    val drawData = glSurfaceView?.getDrawData() ?: return

                    // 保存当前滤镜的强度值到对应的配置中
                    val currentFilterType = drawData.currentFilterType
                    drawData.filterConfigs[currentFilterType]?.strength = drawData.currentFilterStrength

                    // 更新当前滤镜类型
                    mCurrentFilterType = filterItem.type
                    drawData.currentFilterType = mCurrentFilterType

                    // 从配置中恢复该滤镜之前保存的强度值
                    val savedStrength = drawData.filterConfigs[mCurrentFilterType]?.strength ?: 100f
                    drawData.currentFilterStrength = savedStrength

                    // 更新UI
                    if (mCurrentFilterType != FilterType.ORIGINAL) {
                        intensityTextView.visibility = View.VISIBLE
                        intensitySeekBar.visibility = View.VISIBLE

                        val strengthInt = savedStrength.toInt()
                        intensitySeekBar.progress = strengthInt
                        intensityTextView.text = "${filterItem.type} -> 强度: $strengthInt%"
                    } else {
                        intensityTextView.visibility = View.GONE
                        intensitySeekBar.visibility = View.GONE
                    }

                    // 请求重新渲染
                    glSurfaceView?.requestRender()
                }
            })

            layoutManager = LinearLayoutManager(this@MainActivity4, LinearLayoutManager.HORIZONTAL, false)
        }
    }

    override fun onResume() {
        super.onResume()
        glSurfaceView.onResume()
    }

    override fun onPause() {
        super.onPause()
        glSurfaceView.onPause()
    }
}

滤镜数据类

// 滤镜类型枚举
enum class FilterType {
    ORIGINAL, EDGE, PIXEL, COLOR
}

// 滤镜项数据类
data class FilterItem(
    val type: FilterType,
    val name: String,
    var thumbnailBitmap: Bitmap? = null
)

RecyclerViewAdapterViewHolder

interface FilterItemClickListener {
    fun onFilterItemClick(filterItem: FilterItem)
}

class FilterAdapter(private val filters: List<FilterItem>, private val itemClickListener: FilterItemClickListener) : RecyclerView.Adapter<FilterAdapter.FilterViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FilterViewHolder {
        return FilterViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.filter_item, parent, false)
        )
    }

    override fun onBindViewHolder(holder: FilterViewHolder, position: Int) {
        val filterItem = filters[position]
        holder.bind(filterItem)
        holder.itemView.setOnClickListener {
            itemClickListener.onFilterItemClick(filterItem)
        }
    }

    override fun getItemCount(): Int = filters.size

    class FilterViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val ivThumbnail: ImageView = itemView.findViewById(R.id.ivThumbnail)
        private val tvFilterName: TextView = itemView.findViewById(R.id.tvFilterName)

        fun bind(item: FilterItem) {
            // 设置缩略图
            item.thumbnailBitmap?.let {
                ivThumbnail.setImageBitmap(it)
            }
            // 设置名称
            tvFilterName.text = item.name
        }
    }
}

GLSurfaceView代码

class FilterSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {
    private var mRenderer = FilterRenderer(context)

    init {
        // 设置 OpenGL ES 3.0 版本
        setEGLContextClientVersion(3)
        setRenderer(mRenderer)
        // 设置渲染模式, 仅在需要重新绘制时才进行渲染,以节省资源
        renderMode = RENDERMODE_WHEN_DIRTY
    }

    fun getDrawData(): FilterDrawData? {
        return mRenderer?.getDrawData()
    }
}

GLSurfaceView.Renderer代码

class FilterRenderer(private val mContext: Context) : GLSurfaceView.Renderer {
    private var mDrawData: FilterDrawData? = null

    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 = FilterDrawData().apply {
            initTexture0(mContext, R.drawable.picture)
            initShader()
            initVertexBuffer()
            initEdgeFilterShader()
            initPixelFilterShader()
            initColorFilterShader()
            initOriginFrameBuffer()
            initColorFilterBuffer()
            initEdgeFilterBuffer()
            initPixelFilterBuffer()
            initTempFrameBuffer()
            initOutputFrameBuffer()
        }
    }

    override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {
        // 当 Surface 尺寸发生变化时调用,例如设备的屏幕方向发生改变, 设置视口为新的尺寸,视口是指渲染区域的大小
        GLES30.glViewport(0, 0, width, height)
        mDrawData?.computeMVPMatrix(width, height)
    }

    override fun onDrawFrame(gl: GL10?) {
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        mDrawData?.drawFilter()
        takeIf { mDrawData?.filterBitmapIsNull() == true }?.apply {
            mDrawData?.drawOriginBitmap()
            mDrawData?.drawPixelFilterBitmap()
            mDrawData?.drawColorFilterBitmap()
            mDrawData?.drawEdgeFilterBitmap()
            Handler(Looper.getMainLooper()).post {
                (mContext as MainActivity4)?.initFilterRecyclerView()
            }
        }
    }

    fun getDrawData(): FilterDrawData? {
        return mDrawData
    }
}

GLSurfaceView.Renderer需要的绘制数据

class FilterDrawData {
    private var NO_OFFSET = 0
    private val VERTEX_POS_DATA_SIZE = 3
    private val TEXTURE_POS_DATA_SIZE = 2
    private var mProgram: Int = -1
    private var mEdgeProgram: Int = -1 // 边缘检测
    private var mPixelProgram: Int = -1 // 像素着色
    private var mColorProgram: Int = -1 // 彩色旋涡

    // FBO(Frame Buffer Object), 帧缓冲对象,用于存储渲染后的图像
    private var mOriginFBO = IntArray(1)
    private var mColorFBO = IntArray(1)
    private var mEdgeFBO = IntArray(1)
    private var mPixelFBO = IntArray(1)
    private var mOutputFBO = IntArray(1)
    private val mTempFBO = IntArray(1)

    // FBO中的纹理ID
    private var mOriginTextureID = IntArray(1)
    private var mColorTextureID = IntArray(1)
    private var mEdgeTextureID = IntArray(1)
    private var mPixelTextureID = IntArray(1)
    private var mOutputTextureID = IntArray(1)
    private val mTempTextureID = 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(1)

    // 有上下翻转的变换矩阵
    private var mMVPMatrix = FloatArray(16)

    // 投影矩阵
    private val mProjectionMatrix = FloatArray(16)

    // 相机矩阵
    private val mViewMatrix = FloatArray(16)

    // 视口比例
    private var mViewPortRatio = 1f

    // 帧缓冲宽高
    private var mFrameBufferWidth = 0
    private var mFrameBufferHeight = 0

    // 帧缓冲最终变换矩阵
    private val mFrameBufferMVPMatrix = FloatArray(16)

    private var mSurfaceWidth = 0
    private var mSurfaceHeight = 0

    // 准备顶点坐标,分配直接内存
    // 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)

    var mOriginBitmap: Bitmap? = null
    var mEdgeFilterBitmap: Bitmap? = null
    var mPixelFilterBitmap: Bitmap? = null
    var mColorFilterBitmap: Bitmap? = null

    // 滤镜状态类 - 存储每个滤镜的配置
    data class FilterConfig(
        val type: FilterType,
        var strength: Float = 100f,      // 滤镜强度 (0-100)
        var isActive: Boolean = false,   // 是否当前激活
        var lastAppliedStrength: Float = 100f // 上次应用的强度值
    )

    // 滤镜配置存储
    val filterConfigs = mutableMapOf<FilterType, FilterConfig>().apply {
        put(FilterType.ORIGINAL, FilterConfig(FilterType.ORIGINAL))
        put(FilterType.EDGE, FilterConfig(FilterType.EDGE))
        put(FilterType.PIXEL, FilterConfig(FilterType.PIXEL))
        put(FilterType.COLOR, FilterConfig(FilterType.COLOR))
    }

    // 已应用的滤镜链
    private val activeFilters = mutableListOf<FilterType>()

    // 当前滤镜类型
    var currentFilterType = FilterType.ORIGINAL

    // 当前滤镜强度
    var currentFilterStrength: Float
        get() = filterConfigs[currentFilterType]?.strength ?: 0f
        set(value) {
            filterConfigs[currentFilterType]?.strength = value
        }

    // 初始化着色器程序
    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;
         in vec2 vTexCoord;
         out vec4 fragColor;
         void main() {
             fragColor = texture(uTexture_0, vTexCoord);
         }""".trimIndent()

        // 加载顶点着色器和片段着色器, 并创建着色器程序
        val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader =
            LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
        mProgram = GLES30.glCreateProgram()
        GLES30.glAttachShader(mProgram, vertexShader)
        GLES30.glAttachShader(mProgram, fragmentShader)
        GLES30.glLinkProgram(mProgram)
        GLES30.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)
    }

    // 初始化原图帧缓冲
    fun initOriginFrameBuffer() {
        // 创建FBO
        GLES30.glGenFramebuffers(mOriginFBO.size, mOriginFBO, NO_OFFSET)
        // 绑定FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mOriginFBO[0])
        // 创建空纹理
        GLES30.glGenTextures(mOriginTextureID.size, mOriginTextureID, NO_OFFSET)
        // 绑定空纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mOriginTextureID[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, // 纹理类型
            mOriginTextureID[0], // 纹理ID
            0
        )

        if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
            Log.e("yang", "init FrameBuffer failed")
        }
        // 解绑FBO纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
        // 解绑FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
    }

    fun initTempFrameBuffer() {
        GLES30.glGenFramebuffers(mTempFBO.size, mTempFBO, NO_OFFSET)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mTempFBO[0])
        GLES30.glGenTextures(mTempTextureID.size, mTempTextureID, NO_OFFSET)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTempTextureID[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,
            0,
            GLES30.GL_RGBA,
            mFrameBufferWidth,
            mFrameBufferHeight,
            0,
            GLES30.GL_RGBA,
            GLES30.GL_UNSIGNED_BYTE,
            null
        )
        GLES30.glFramebufferTexture2D(
            GLES30.GL_FRAMEBUFFER,
            GLES30.GL_COLOR_ATTACHMENT0,
            GLES30.GL_TEXTURE_2D,
            mTempTextureID[0],
            0
        )
        if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
            Log.e("yang", "init FrameBuffer failed")
        }
        // 解绑FBO纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
        // 解绑FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
    }

    fun drawOriginBitmap() {
        val state = saveGLState()
        try {
            GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)
            enableTexture0(mProgram, mTextureID[0])
            // 绑定FBO
            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mOriginFBO[0])
            GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
            computeFrameBufferMVPMatrix()
            drawSomething(mProgram, mFrameBufferMVPMatrix)
            mOriginBitmap = savePixelBufferBitmap()
            // 解绑FBO
            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
            disableTexture0()
        } finally {
            restoreGLState(state)
        }
    }

    fun drawEdgeFilterBitmap() {
        takeIf { mEdgeFilterBitmap == null }?.let {
            val state = saveGLState()
            try {
                GLES30.glUseProgram(mEdgeProgram)
                GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)
                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mEdgeFBO[0])
                GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
                enableTexture0(mEdgeProgram, mOriginTextureID[0])
                // 纹理大小
                val textureSizeHandle = GLES30.glGetUniformLocation(mEdgeProgram, "uTextureSize")
                if (textureSizeHandle != -1) {
                    GLES30.glUniform2f(
                        textureSizeHandle,
                        mFrameBufferWidth.toFloat(),
                        mFrameBufferHeight.toFloat()
                    )
                }
                // 设置滤镜强度
                val intensityLocation = GLES30.glGetUniformLocation(mEdgeProgram, "uIntensity")
                if (intensityLocation != -1) {
                    GLES30.glUniform1f(intensityLocation, 1f)
                }
                drawSomething(mEdgeProgram, mFrameBufferMVPMatrix)
                mEdgeFilterBitmap = savePixelBufferBitmap()
                disableTexture0()
                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
            } finally {
                restoreGLState(state)
            }
        }
    }

    fun drawPixelFilterBitmap() {
        takeIf { mPixelFilterBitmap == null }?.let {
            val state = saveGLState()
            try {
                GLES30.glUseProgram(mPixelProgram)
                GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)
                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mPixelFBO[0])
                GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
                enableTexture0(mPixelProgram, mOriginTextureID[0])
                // 纹理大小
                val textureSizeHandle = GLES30.glGetUniformLocation(mPixelProgram, "uTextureSize")
                if (textureSizeHandle != -1) {
                    GLES30.glUniform2f(
                        textureSizeHandle,
                        mFrameBufferWidth.toFloat(),
                        mFrameBufferWidth.toFloat()
                    )
                }
                // 像素尺寸
                val pixelSizeHandle = GLES30.glGetUniformLocation(mPixelProgram, "uPixelSize")
                if (pixelSizeHandle != -1) {
                    GLES30.glUniform1f(pixelSizeHandle, 15.0f)
                }
                // 设置滤镜强度
                val intensityLocation = GLES30.glGetUniformLocation(mPixelProgram, "uIntensity")
                if (intensityLocation != -1) {
                    GLES30.glUniform1f(intensityLocation, 1f)
                }
                drawSomething(mPixelProgram, mFrameBufferMVPMatrix)
                mPixelFilterBitmap = savePixelBufferBitmap()
                disableTexture0()
                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
            } finally {
                restoreGLState(state)
            }
        }
    }

    fun drawColorFilterBitmap() {
        takeIf { mColorFilterBitmap == null }?.let {
            val state = saveGLState()
            try {
                GLES30.glUseProgram(mColorProgram)
                GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)
                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mColorFBO[0])
                GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
                enableTexture0(mColorProgram, mOriginTextureID[0])
                // 流动时间
                val timeHandle = GLES30.glGetUniformLocation(mColorProgram, "uTime")
                if (timeHandle != -1) { // 检查是否存在该参数
                    GLES30.glUniform1f(timeHandle, (System.currentTimeMillis() % 10000) / 10000.0f)
                }

                // 设置旋涡强度
                val twistIntensityHandle =
                    GLES30.glGetUniformLocation(mColorProgram, "uTwistIntensity")
                if (twistIntensityHandle != -1) {
                    GLES30.glUniform1f(twistIntensityHandle, 0.15f)
                }
                val intensityLocation = GLES30.glGetUniformLocation(mColorProgram, "uIntensity")
                if (intensityLocation != -1) {
                    GLES30.glUniform1f(intensityLocation, 1f)
                }
                drawSomething(mColorProgram, mFrameBufferMVPMatrix)
                mColorFilterBitmap = savePixelBufferBitmap()
                disableTexture0()
                GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
            } finally {
                restoreGLState(state)
            }
        }
    }

    // 使用着色器程序绘制图形
    fun drawSomething(program: Int, mvpMatrix: FloatArray) {
        // 解析变换矩阵
        val matrixHandle = GLES30.glGetUniformLocation(program, "uMVPMatrix")
        GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mvpMatrix, NO_OFFSET)

        // 绑定VAO
        GLES30.glBindVertexArray(mVAO[0])
        // 绘制图形
        GLES30.glDrawElements(
            GLES30.GL_TRIANGLES,
            index.size,
            GLES30.GL_UNSIGNED_SHORT,
            NO_OFFSET
        )
        // 解绑VAO
        GLES30.glBindVertexArray(0)
    }

    // 边缘检测着色器
    fun initEdgeFilterShader() {
        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 vec2 uTextureSize;
                uniform float uIntensity; // 强度参数 (0.0-1.0)
                in vec2 vTexCoord;
                out vec4 fragColor;
                
                void main() {
                    // 获取原始颜色
                    vec4 originalColor = texture(uTexture_0, vTexCoord);
                    
                    // 计算边缘检测效果
                    float dx = 1.0 / uTextureSize.x;
                    float dy = 1.0 / uTextureSize.y;
                    
                    vec4 left = texture(uTexture_0, vTexCoord - vec2(dx, 0.0));
                    vec4 right = texture(uTexture_0, vTexCoord + vec2(dx, 0.0));
                    vec4 top = texture(uTexture_0, vTexCoord - vec2(0.0, dy));
                    vec4 bottom = texture(uTexture_0, vTexCoord + vec2(0.0, dy));
                    
                    // 计算边缘
                    vec4 horizontal = abs(right - left);
                    vec4 vertical = abs(bottom - top);
                    float edge = (horizontal.r + horizontal.g + horizontal.b + 
                                 vertical.r + vertical.g + vertical.b) / 6.0;
                    
                    // 边缘增强效果
                    vec4 edgeColor = vec4(vec3(1.0 - edge * 3.0), 1.0);
                    
                    // 线性混合,当uIntensity为0时完全是原始颜色
                    fragColor = mix(originalColor, edgeColor, uIntensity);
                }
                """.trimIndent()

        // 加载着色器并创建程序
        val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader =
            LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
        mEdgeProgram = GLES30.glCreateProgram()
        GLES30.glAttachShader(mEdgeProgram, vertexShader)
        GLES30.glAttachShader(mEdgeProgram, fragmentShader)
        GLES30.glLinkProgram(mEdgeProgram)

        // 删除着色器对象
        GLES30.glDeleteShader(vertexShader)
        GLES30.glDeleteShader(fragmentShader)
    }

    // 像素着色器
    fun initPixelFilterShader() {
        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 vec2 uTextureSize;
                uniform float uIntensity; // 强度参数 (0.0-1.0)
                in vec2 vTexCoord;
                out vec4 fragColor;
                
                void main() {
                    // 获取原始颜色
                    vec4 originalColor = texture(uTexture_0, vTexCoord);
                    
                    // 根据强度计算像素块大小 (最小2.0到最大25.0)
                    float pixelSize = 2.0 + 23.0 * uIntensity;
                    
                    // 计算像素化效果
                    float dx = pixelSize / uTextureSize.x;
                    float dy = pixelSize / uTextureSize.y;
                    
                    // 计算像素块的中心坐标
                    vec2 pixelatedCoord;
                    pixelatedCoord.x = dx * floor(vTexCoord.x / dx) + dx * 0.5;
                    pixelatedCoord.y = dy * floor(vTexCoord.y / dy) + dy * 0.5;
                    
                    // 采样像素块中心的颜色
                    vec4 pixelColor = texture(uTexture_0, pixelatedCoord);
                    
                    // 减少颜色深度,增强复古效果
                    float colorLevels = mix(16.0, 5.0, uIntensity); // 从16级到5级
                    pixelColor = floor(pixelColor * colorLevels) / colorLevels;
                    
                    // 添加微小的像素边框
                    vec2 pixelPos = fract(vTexCoord / vec2(dx, dy));
                    float borderThreshold = mix(0.95, 0.80, uIntensity); // 从0.95到0.80
                    float borderFactor = step(borderThreshold, max(pixelPos.x, pixelPos.y));
                    float borderDarkness = mix(0.95, 0.7, uIntensity); // 从0.95到0.7
                    pixelColor.rgb *= mix(1.0, borderDarkness, borderFactor);
                    
                    // 线性混合,当uIntensity为0时完全是原始颜色
                    fragColor = mix(originalColor, pixelColor, uIntensity);
                }""".trimIndent()

        // 加载着色器并创建程序
        val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader =
            LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
        mPixelProgram = GLES30.glCreateProgram()
        GLES30.glAttachShader(mPixelProgram, vertexShader)
        GLES30.glAttachShader(mPixelProgram, fragmentShader)
        GLES30.glLinkProgram(mPixelProgram)

        // 删除着色器对象
        GLES30.glDeleteShader(vertexShader)
        GLES30.glDeleteShader(fragmentShader)
    }

    // 彩色旋涡滤镜
    fun initColorFilterShader() {
        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 float uIntensity; // 强度参数 (0.0-1.0)
            in vec2 vTexCoord;
            out vec4 fragColor;
            
            // RGB转HSV函数
            vec3 rgb2hsv(vec3 c) {
                vec4 K = vec4(0.0, -1.0/3.0, 2.0/3.0, -1.0);
                vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
                vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
                
                float d = q.x - min(q.w, q.y);
                float e = 1.0e-10;
                return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
            }
            
            // HSV转RGB函数
            vec3 hsv2rgb(vec3 c) {
                vec4 K = vec4(1.0, 2.0/3.0, 1.0/3.0, 3.0);
                vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
                return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
            }
            
            void main() {
                // 获取原始颜色
                vec4 originalColor = texture(uTexture_0, vTexCoord);
                
                // 将纹理坐标转换为中心为原点的坐标
                vec2 center = vec2(0.5, 0.5);
                vec2 texCoordFromCenter = vTexCoord - center;
                
                // 计算距离和角度
                float distance = length(texCoordFromCenter);
                float angle = atan(texCoordFromCenter.y, texCoordFromCenter.x);
                
                // 应用旋涡扭曲 - 随强度平滑变化
                float twistFactor = 0.25 * uIntensity;
                angle += twistFactor * (1.0 - distance);
                
                // 将极坐标转换回纹理坐标
                vec2 newCoord;
                newCoord.x = center.x + distance * cos(angle);
                newCoord.y = center.y + distance * sin(angle);
                
                // 采样扭曲后的颜色
                vec4 distortedColor = texture(uTexture_0, newCoord);
                
                // 增强颜色
                vec3 hsv = rgb2hsv(distortedColor.rgb);
                
                // 增加饱和度 - 随强度平滑变化
                hsv.y = hsv.y * (1.0 + 0.3 * uIntensity);
                
                // 调整亮度和对比度 - 随强度平滑变化
                hsv.z = mix(hsv.z, hsv.z * 0.95 + 0.05, uIntensity);
                
                // 添加彩虹色效果 - 随强度平滑变化
                hsv.x = hsv.x + distance * 0.4 * uIntensity;
                
                // 转回RGB
                vec4 colorizedColor = vec4(hsv2rgb(hsv), distortedColor.a);
                
                // 线性混合,当uIntensity为0时完全是原始颜色
                fragColor = mix(originalColor, colorizedColor, uIntensity);
            }
            """.trimIndent()

        // 加载着色器并创建程序
        val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
        val fragmentShader =
            LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
        mColorProgram = GLES30.glCreateProgram()
        GLES30.glAttachShader(mColorProgram, vertexShader)
        GLES30.glAttachShader(mColorProgram, fragmentShader)
        GLES30.glLinkProgram(mColorProgram)

        // 删除着色器对象
        GLES30.glDeleteShader(vertexShader)
        GLES30.glDeleteShader(fragmentShader)
    }

    fun initColorFilterBuffer() {
        // 创建FBO
        GLES30.glGenFramebuffers(mColorFBO.size, mColorFBO, NO_OFFSET)
        // 绑定FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mColorFBO[0])
        // 创建空纹理
        GLES30.glGenTextures(mColorTextureID.size, mColorTextureID, NO_OFFSET)
        // 绑定空纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorTextureID[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, // 纹理类型
            mColorTextureID[0], // 纹理ID
            0
        )

        if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
            Log.e("yang", "init FrameBuffer failed")
        }
        // 解绑FBO纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
        // 解绑FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
    }

    fun initEdgeFilterBuffer() {
        // 创建FBO
        GLES30.glGenFramebuffers(mEdgeFBO.size, mEdgeFBO, NO_OFFSET)
        // 绑定FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mEdgeFBO[0])
        // 创建空纹理
        GLES30.glGenTextures(mEdgeTextureID.size, mEdgeTextureID, NO_OFFSET)
        // 绑定空纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mEdgeTextureID[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, // 纹理类型
            mEdgeTextureID[0], // 纹理ID
            0
        )

        if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
            Log.e("yang", "init FrameBuffer failed")
        }
        // 解绑FBO纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
        // 解绑FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
    }

    fun initPixelFilterBuffer() {
        // 创建FBO
        GLES30.glGenFramebuffers(mPixelFBO.size, mPixelFBO, NO_OFFSET)
        // 绑定FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mPixelFBO[0])
        // 创建空纹理
        GLES30.glGenTextures(mPixelTextureID.size, mPixelTextureID, NO_OFFSET)
        // 绑定空纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mPixelTextureID[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, // 纹理类型
            mPixelTextureID[0], // 纹理ID
            0
        )

        if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
            Log.e("yang", "init FrameBuffer failed")
        }
        // 解绑FBO纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
        // 解绑FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
    }

    fun initOutputFrameBuffer() {
        // 创建FBO
        GLES30.glGenFramebuffers(mOutputFBO.size, mOutputFBO, NO_OFFSET)
        // 绑定FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mOutputFBO[0])
        // 创建空纹理
        GLES30.glGenTextures(mOutputTextureID.size, mOutputTextureID, NO_OFFSET)
        // 绑定空纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mOutputTextureID[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, // 纹理类型
            mOutputTextureID[0], // 纹理ID
            0
        )

        if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {
            Log.e("yang", "init FrameBuffer failed")
        }
        // 解绑FBO纹理
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
        // 解绑FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)
    }

    fun savePixelBufferBitmap(): Bitmap? {
        // 分配缓冲区来存储像素数据
        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)

        return bitmap
    }

    // 计算GLSurfaceView变换矩阵
    fun computeMVPMatrix(width: Int, height: Int) {
        mSurfaceWidth = width
        mSurfaceHeight = height
        Matrix.setIdentityM(mProjectionMatrix, NO_OFFSET)
        Matrix.setIdentityM(mViewMatrix, NO_OFFSET)
        Matrix.setIdentityM(mMVPMatrix, NO_OFFSET)

        // 正交投影矩阵
        takeIf { width > height }?.let {
            mViewPortRatio = (width * 1f) / height
            Matrix.orthoM(
                mProjectionMatrix, // 正交投影矩阵
                NO_OFFSET, // 偏移量
                -mViewPortRatio, // 近平面的坐标系左边界
                mViewPortRatio, // 近平面的坐标系右边界
                -1f, // 近平面的坐标系的下边界
                1f, // 近平面坐标系的上边界
                -1f, // 近平面距离相机距离
                1f // 远平面距离相机距离
            )
        } ?: run {
            mViewPortRatio = (height * 1f) / width
            Matrix.orthoM(
                mProjectionMatrix, // 正交投影矩阵
                NO_OFFSET, // 偏移量
                -1f, // 近平面坐标系左边界
                1f, // 近平面坐标系右边界
                -mViewPortRatio, // 近平面坐标系下边界
                mViewPortRatio, // 近平面坐标系上边界
                -1f, // 近平面距离相机距离
                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 computeFrameBufferMVPMatrix() {
        Matrix.setIdentityM(mProjectionMatrix, NO_OFFSET)
        Matrix.setIdentityM(mViewMatrix, NO_OFFSET)
        Matrix.setIdentityM(mFrameBufferMVPMatrix, NO_OFFSET)

        // 正交投影矩阵
        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 // 相机矩阵偏移量
        )
    }

    // 加载纹理
    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 enableTexture0(program: Int, id: Int) {
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, id)
        val textureSampleHandle = GLES30.glGetUniformLocation(program, "uTexture_0")
        if (textureSampleHandle != -1) {
            GLES30.glUniform1i(textureSampleHandle, 0)
        }
    }

    fun disableTexture0() {
        GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)
    }

    fun initTexture0(context: Context, resourceId: Int) {
        mTextureID[0] = loadTexture(context, resourceId)
    }

    fun filterBitmapIsNull(): Boolean {
        return mOriginBitmap == null && mEdgeFilterBitmap == null && mPixelFilterBitmap == null && mColorFilterBitmap == null
    }

    fun drawFilter() {
        when (currentFilterType) {
            // 切换到原图,清楚激活的滤镜作用链,只保留作用数值
            FilterType.ORIGINAL -> {
                activeFilters.clear()
                drawCurrentOutput(mTextureID[0])
            }

            else -> {
                // 获取当前滤镜配置
                val config = filterConfigs.getOrPut(currentFilterType) {
                    FilterConfig(currentFilterType)
                }

                // 强度接近0时移除滤镜
                if (config.strength < 1f) {
                    activeFilters.remove(currentFilterType)
                    renderFilterChain()
                    return
                }

                // 添加到激活滤镜链
                if (!activeFilters.contains(currentFilterType)) {
                    activeFilters.add(currentFilterType)
                }

                // 渲染整个滤镜链
                renderFilterChain()
            }
        }
    }

    // 渲染滤镜链
    private fun renderFilterChain() {
        // 如果没有激活的滤镜,直接显示原图
        if (activeFilters.isEmpty()) {
            drawCurrentOutput(mOriginTextureID[0])
            return
        }

        // 使用 ping-pong 渲染技术
        var sourceFBO = 0                       // 0 表示使用原始纹理
        var sourceTexture = mOriginTextureID[0] // 初始输入为原始纹理

        // 依次应用每个滤镜
        activeFilters.forEachIndexed { index, filterType ->
            // 确定目标缓冲区 (最后一个滤镜使用输出FBO,其他使用临时FBO)
            val isLastFilter = index == activeFilters.size - 1
            val targetFBO = if (isLastFilter) mOutputFBO[0] else mTempFBO[0]
            val targetTexture = if (isLastFilter) mOutputTextureID[0] else mTempTextureID[0]

            // 绑定目标FBO并清除
            GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, targetFBO)
            GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)

            // 获取滤镜配置和程序
            val config = filterConfigs[filterType] ?: return
            val program = getFilterProgram(filterType)

            // 应用滤镜
            drawFilter(program, sourceTexture, config)

            // 为下一次迭代更新源
            sourceTexture = targetTexture
            sourceFBO = targetFBO
        }

        // 解绑FBO
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)

        // 显示最终结果
        drawCurrentOutput(mOutputTextureID[0])
    }

    // GLSurfaceView实时绘制
    private fun drawCurrentOutput(id: Int) {
        val state = saveGLState()
        try {
            GLES30.glUseProgram(mProgram)
            enableTexture0(mProgram, id)
                drawSomething(mProgram, mMVPMatrix)
            disableTexture0()
        } finally {
            restoreGLState(state)
        }
    }

    // 根据滤镜类型获取对应的着色器程序
    private fun getFilterProgram(filterType: FilterType): Int {
        return when (filterType) {
            FilterType.EDGE -> mEdgeProgram
            FilterType.PIXEL -> mPixelProgram
            FilterType.COLOR -> mColorProgram
            FilterType.ORIGINAL -> mProgram
        }
    }

    // 应用单个滤镜
    private fun drawFilter(program: Int, inputTexture: Int, config: FilterConfig) {
        val state = saveGLState()
        try {
            GLES30.glUseProgram(program)
            GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)
            enableTexture0(program, inputTexture)
            // 设置滤镜强度
            val intensityLocation = GLES30.glGetUniformLocation(program, "uIntensity")
            if (intensityLocation != -1) {
                GLES30.glUniform1f(intensityLocation, config.strength / 100f)
            }
            drawSomething(program, mFrameBufferMVPMatrix)
            disableTexture0()
        } finally {
            restoreGLState(state)
        }
    }

    // 保存OpenGL状态
    private fun saveGLState(): GLState {
        val viewport = IntArray(4)
        val program = IntArray(1)
        val framebuffer = IntArray(1)
        GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewport, 0)
        GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, program, 0)
        GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, framebuffer, 0)
        return GLState(viewport, program[0], framebuffer[0])
    }

    // 恢复OpenGL状态
    private fun restoreGLState(state: GLState) {
        GLES30.glViewport(
            state.viewport[0],
            state.viewport[1],
            state.viewport[2],
            state.viewport[3]
        )
        GLES30.glUseProgram(state.program)
        GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, state.framebuffer)
    }

    // OpenGL状态数据类
    data class GLState(
        val viewport: IntArray,
        val program: Int,
        val framebuffer: Int
    )

    object LoadShaderUtil {
        // 创建着色器对象
        fun loadShader(type: Int, source: String): Int {
            val shader = GLES30.glCreateShader(type)
            GLES30.glShaderSource(shader, source)
            GLES30.glCompileShader(shader)
            return shader
        }
    }
}

效果图

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值