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

Android OpenGL(七)实现滤镜效果(特效渲染)

实现滤镜的2种方式:

  1. Lut:实现多种色彩风格特效
  2. 算子:实现黑白、素描、雕像等多种特效

算子

黑白特效

高质量黑白特效算法

color=tc.r * 0.3 +tc.g*0.59+tc.b *0.11

更换成黑白像素:RGB中,如果每个分量值相同,则为灰色

gl_FragColor = vec4(color,color,color,1.0)

应用在opengl中主要是修改glsl代码,修改片元shader(片元着色器)

   private val fragmentShaderCode_WB_Effect =
        "precision mediump float;" +
                "uniform sampler2D uSampler;" +
                "varying vec2 vTexCoord;" +
                "void main() { " +
                "   vec4 tc = texture2D(uSampler,vTexCoord);" +
                "   float color = tc.r * 0.3 +tc.g*0.59+tc.b *0.11;" +
                "   gl_FragColor = vec4(color,color,color,1.0);" +
                "}"

具体应用

在这里插入图片描述


import android.graphics.Bitmap
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.opengl.GLUtils
import android.opengl.Matrix
import android.util.Log
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 GLEffectRender(val bitmap:Bitmap) : GLSurfaceView.Renderer {
    /*
     * 顶点位置程序
     */
    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 fragmentShaderCode =
        "precision mediump float;" +
        "uniform sampler2D uSampler;" +
        "varying vec2 vTexCoord;" +
        "void main() { " +
        "   gl_FragColor = texture2D(uSampler,vTexCoord);" +
        "}"

    private val fragmentShaderCode_WB_Effect =
        "precision mediump float;" +
                "uniform sampler2D uSampler;" +
                "varying vec2 vTexCoord;" +
                "void main() { " +
                "   vec4 tc = texture2D(uSampler,vTexCoord);" +
                "   float color = tc.r * 0.3 +tc.g*0.59+tc.b *0.11;" +
                "   gl_FragColor = vec4(color,color,color,1.0);" +
                "}"

    /**
     * 三角形顶点位置
     */
    private val coodData = floatArrayOf(
        // 顶点坐标        纹理坐标
        -1f, 1f, 0.0f, 0f, 0f,   // 左上角
        -1f, -1f, 0.0f, 0f, 1f,  //左下角
        1f, 1.0f, 0.0f, 1f, 0f,   //右上角
        1f, -1f, 0.0f, 1f, 1f  //右下角
    )

    private var translateMatrix = FloatArray(16)
    private var program: Int = 0
    private var positionHandle: Int = -1
    private var texCoordHandle: Int = -1
    private var samplerHandle: Int = -1
    private var uMatrixHandle: Int = -1

    // vbo
    private var vboId = IntArray(1)
    // 纹理id array
    private var textureIds= IntArray(1)

    private lateinit var coordBuffer: FloatBuffer
    private lateinit var byteBuffer: ByteBuffer

    override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
        // 设置背景颜色
        GLES30.glClearColor(1f, 0f, 0f, 1.0f)
        // 清理缓存
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        // 顶点坐标内存申请
        createFloatBuffer()
        // 创建定点着色程序
        val vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
        // 创建片元着色程序
        val fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode_WB_Effect)
        if (vertexShader != 0 && fragmentShader != 0) {
            linkProgram(vertexShader, fragmentShader)
            GLES30.glDeleteShader(vertexShader)
            GLES30.glDeleteShader(fragmentShader)
            // 生成VBO
            GLES30.glGenBuffers(1, vboId, 0)
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
            GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, byteBuffer.capacity(), byteBuffer, GLES30.GL_STATIC_DRAW)
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)

            //
            Matrix.setIdentityM(translateMatrix, 0)

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

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

            uMatrixHandle = GLES30.glGetUniformLocation(program, "uTMatrix")
            samplerHandle = GLES30.glGetUniformLocation(program, "uSampler")

            loadTexture()
        }
    }

    private fun linkProgram(vertexShader: Int, fragmentShader: Int) {
        // 创建空的opengl es 程序
        program = GLES30.glCreateProgram()
        program.let {
            // 将顶点着色器加入程序
            GLES30.glAttachShader(it, vertexShader)
            // 将片元着色器加入程序
            GLES30.glAttachShader(it, fragmentShader)
            // 链接到着色器程序
            GLES30.glLinkProgram(it)
            // 将程序加入到opengl30环境中
            GLES30.glUseProgram(it)
            val info = GLES30.glGetProgramInfoLog(it)
            // 打印链接程序日志
            Log.e("wdf", "info==" + info)
        }
    }

    private fun createFloatBuffer() {
        // 申请物理层空间
        byteBuffer = ByteBuffer.allocateDirect(coodData.size * 4).apply {
            this.order(ByteOrder.nativeOrder())
        }
        // 坐标数据转换
        coordBuffer = byteBuffer.asFloatBuffer()
        coordBuffer.put(coodData, 0, coodData.size)
        coordBuffer.position(0)
    }

    override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
        // 计算并设置窗口大小
        val imgRatio = bitmap.width.toFloat().div(bitmap.height)
        val windowRatio = width.toFloat().div(height)
        GLES30.glViewport(0, 0, width, height)
        // 1. 矩阵数组
        // 2. 结果矩阵起始的偏移量
        // 3. left:x的最小值
        // 4. right:x的最大值
        // 5. bottom:y的最小值
        // 6. top:y的最大值
        // 7. near:z的最小值
        // 8. far:z的最大值
        // 由于是正交矩阵,所以偏移量为0,near 和 far 也不起作用
        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);
        }
    }

    override fun onDrawFrame(p0: GL10?) {
        if (program <= 0) {
            return
        }
        GLES30.glUseProgram(program)
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
        program?.let {
            GLES30.glVertexAttribPointer(positionHandle, 3, GLES30.GL_FLOAT, false, 5 * Float.SIZE_BYTES, 0)
            GLES30.glVertexAttribPointer(texCoordHandle, 2, GLES30.GL_FLOAT, false, 5 * Float.SIZE_BYTES, 3 * Float.SIZE_BYTES)

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

            // 激活纹理单元
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            // 绑定纹理单元
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0])
            // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE0,因此传递0
            GLES30.glUniform1i(samplerHandle, 0)
            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
        }

    }

    private fun loadTexture() {
        GLES30.glGenTextures(1, textureIds, 0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[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)
    }

    /**
     * 创建shader,加载shader程序
     */
    private fun loadShader(type: Int, shaderCode: String): Int {
        val shader = GLES30.glCreateShader(type)
        GLES30.glShaderSource(shader, shaderCode)
        GLES30.glCompileShader(shader)
        return shader
    }
}
class OpenGlEffectActivity : AppCompatActivity() {
    private var glSurfaceView: GLSurfaceView? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_opengl_img_demo)
        glSurfaceView = findViewById(R.id.glSurfaceView_img)
        glSurfaceView?.setEGLContextClientVersion(3)
        glSurfaceView?.setRenderer(GLEffectRender(BitmapFactory.decodeResource(resources, R.drawable.flower)))
        glSurfaceView?.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
    }
}

Blur滤波器

kernel算子,算子一般是3*3的矩阵,算子元素的个数要用奇数,保证中心对齐,

kernel = new Float[]{
1f/16f,2f/16f,1f/16f,
2f/16f,4f/16f,2f/16f,
1f/16f,2f/16f,1f/16f
}

glsl变更,定义宏 :KERNEL_SIZE,新增输入变量uTexOffset和uColorOffset

    private val fragmentShaderCode_Blur_Effect =
        "#define KERNEL_SIZE ${KERNEL_SIZE}\n" +
                "precision mediump float;" +
                "uniform sampler2D uSampler;" +
                "uniform float uKernel[KERNEL_SIZE];" +
                "uniform vec2 uTexOffset[KERNEL_SIZE];" +
                "uniform float uColorOffset;" +
                "varying vec2 vTexCoord;" +
                "void main() { " +
                "   vec4 sum = vec4(0.0); " +
                "   int i = 0; " +
                "   for (i = 0;i < KERNEL_SIZE; i++) { " +
                "        vec4 tc = texture2D(uSampler,vTexCoord+uTexOffset[i]);" +
                "        sum += (tc * uKernel[i]);" +
                "    }" +
                "   sum += uColorOffset;" +
                "   gl_FragColor = sum;" +
                "}"

获取对应的输入变量kernelHandle、uTexOffsetHandle、uColorOffsetHandle

   kernelHandle = GLES30.glGetUniformLocation(program, "uKernel")
            if (kernelHandle > 0) {
                uTexOffsetHandle = GLES30.glGetUniformLocation(program, "uTexOffset")
                uColorOffsetHandle = GLES30.glGetUniformLocation(program, "uColorOffset")
                setKernel(floatArrayOf(
                    1f/16f,2f/16f,1f/16f,
                    2f/16f,4f/16f,2f/16f,
                    1f/16f,2f/16f,1f/16f),0.5f)
                setTexSize(256,256)

            } else {
                kernelHandle = -1
                uTexOffsetHandle = -1
                uColorOffsetHandle = -1
            }

setKernel与setTexSize

    private val mKernel = FloatArray(KERNEL_SIZE)
    private var mColorOffset = 0f
    private fun setKernel(kernel: FloatArray, colorOffset: Float) {
        System.arraycopy(kernel, 0, mKernel, 0, KERNEL_SIZE)
        mColorOffset = colorOffset
    }
    private var mTexOffset = FloatArray(16)
    private fun setTexSize(width: Int, height: Int) {
        val tw = 1f / width
        val th = 1f / height
        mTexOffset = floatArrayOf(
            -tw, -th, 0f, -th, tw, -th,
            -tw, 0f, 0f, 0f, tw, 0f,
            -tw, th, 0f, th, tw, th)
    }

应用

在这里插入图片描述
lut图片资源
具体代码

import android.graphics.Bitmap
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.opengl.GLUtils
import android.opengl.Matrix
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10

/**
 * Day:2024/12/25 20:01
 * @author wangdanfeng02
 */
class GLEffectRender(val bitmap:Bitmap) : GLSurfaceView.Renderer {
    /*
     * 顶点位置程序
     */
    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 fragmentShaderCode =
        "precision mediump float;" +
        "uniform sampler2D uSampler;" +
        "varying vec2 vTexCoord;" +
        "void main() { " +
        "   gl_FragColor = texture2D(uSampler,vTexCoord);" +
        "}"

    private val fragmentShaderCode_WB_Effect =
        "precision mediump float;" +
                "uniform sampler2D uSampler;" +
                "varying vec2 vTexCoord;" +
                "void main() { " +
                "   vec4 tc = texture2D(uSampler,vTexCoord);" +
                "   float color = tc.r * 0.3 +tc.g*0.59+tc.b *0.11;" +
                "   gl_FragColor = vec4(color,color,color,1.0);" +
                "}"
    private val KERNEL_SIZE = 9
    private val fragmentShaderCode_Blur_Effect =
        "#define KERNEL_SIZE ${KERNEL_SIZE}\n" +
                "precision mediump float;" +
                "uniform sampler2D uSampler;" +
                "uniform float uKernel[KERNEL_SIZE];" +
                "uniform vec2 uTexOffset[KERNEL_SIZE];" +
                "uniform float uColorOffset;" +
                "varying vec2 vTexCoord;" +
                "void main() { " +
                "   vec4 sum = vec4(0.0); " +
                "   int i = 0; " +
                "   for (i = 0;i < KERNEL_SIZE; i++) { " +
                "        vec4 tc = texture2D(uSampler,vTexCoord+uTexOffset[i]);" +
                "        sum += (tc * uKernel[i]);" +
                "    }" +
                "   sum += uColorOffset;" +
                "   gl_FragColor = sum;" +
                "}"

    /**
     * 三角形顶点位置
     */
    private val coodData = floatArrayOf(
        // 顶点坐标        纹理坐标
        -1f, 1f, 0.0f, 0f, 0f,   // 左上角
        -1f, -1f, 0.0f, 0f, 1f,  //左下角
        1f, 1.0f, 0.0f, 1f, 0f,   //右上角
        1f, -1f, 0.0f, 1f, 1f  //右下角
    )

    private var translateMatrix = FloatArray(16)
    private var program: Int = 0
    private var positionHandle: Int = -1
    private var texCoordHandle: Int = -1
    private var samplerHandle: Int = -1
    private var uMatrixHandle: Int = -1

    private var kernelHandle: Int = -1
    private var uTexOffsetHandle: Int = -1
    private var uColorOffsetHandle: Int = -1

    // vbo
    private var vboId = IntArray(1)
    // 纹理id array
    private var textureIds= IntArray(1)

    private lateinit var coordBuffer: FloatBuffer
    private lateinit var byteBuffer: ByteBuffer

    override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
        // 设置背景颜色
        GLES30.glClearColor(1f, 0f, 0f, 1.0f)
        // 清理缓存
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        // 顶点坐标内存申请
        createFloatBuffer()
        // 创建定点着色程序
        val vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
        // 创建片元着色程序
        val fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode_Blur_Effect)
        if (vertexShader != 0 && fragmentShader != 0) {
            linkProgram(vertexShader, fragmentShader)
            GLES30.glDeleteShader(vertexShader)
            GLES30.glDeleteShader(fragmentShader)
            // 生成VBO
            GLES30.glGenBuffers(1, vboId, 0)
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
            GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, byteBuffer.capacity(), byteBuffer, GLES30.GL_STATIC_DRAW)
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)

            //
            Matrix.setIdentityM(translateMatrix, 0)

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

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

            uMatrixHandle = GLES30.glGetUniformLocation(program, "uTMatrix")
            samplerHandle = GLES30.glGetUniformLocation(program, "uSampler")



            kernelHandle = GLES30.glGetUniformLocation(program, "uKernel")
            if (kernelHandle > 0) {
                uTexOffsetHandle = GLES30.glGetUniformLocation(program, "uTexOffset")
                uColorOffsetHandle = GLES30.glGetUniformLocation(program, "uColorOffset")
                setKernel(floatArrayOf(
                    1f/16f,2f/16f,1f/16f,
                    2f/16f,4f/16f,2f/16f,
                    1f/16f,2f/16f,1f/16f),0.5f)
                setTexSize(256,256)

            } else {
                kernelHandle = -1
                uTexOffsetHandle = -1
                uColorOffsetHandle = -1
            }

            loadTexture()
        }
    }

    private fun linkProgram(vertexShader: Int, fragmentShader: Int) {
        // 创建空的opengl es 程序
        program = GLES30.glCreateProgram()
        program.let {
            // 将顶点着色器加入程序
            GLES30.glAttachShader(it, vertexShader)
            // 将片元着色器加入程序
            GLES30.glAttachShader(it, fragmentShader)
            // 链接到着色器程序
            GLES30.glLinkProgram(it)
            // 将程序加入到opengl30环境中
            GLES30.glUseProgram(it)
            val info = GLES30.glGetProgramInfoLog(it)
            // 打印链接程序日志
            Log.e("wdf", "info==" + info)
        }
    }

    private fun createFloatBuffer() {
        // 申请物理层空间
        byteBuffer = ByteBuffer.allocateDirect(coodData.size * 4).apply {
            this.order(ByteOrder.nativeOrder())
        }
        // 坐标数据转换
        coordBuffer = byteBuffer.asFloatBuffer()
        coordBuffer.put(coodData, 0, coodData.size)
        coordBuffer.position(0)
    }

    override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
        // 计算并设置窗口大小
        val imgRatio = bitmap.width.toFloat().div(bitmap.height)
        val windowRatio = width.toFloat().div(height)
        GLES30.glViewport(0, 0, width, height)
        // 1. 矩阵数组
        // 2. 结果矩阵起始的偏移量
        // 3. left:x的最小值
        // 4. right:x的最大值
        // 5. bottom:y的最小值
        // 6. top:y的最大值
        // 7. near:z的最小值
        // 8. far:z的最大值
        // 由于是正交矩阵,所以偏移量为0,near 和 far 也不起作用
        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);
        }
    }

    override fun onDrawFrame(p0: GL10?) {
        if (program <= 0) {
            return
        }
        GLES30.glUseProgram(program)
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
        program?.let {
            GLES30.glVertexAttribPointer(positionHandle, 3, GLES30.GL_FLOAT, false, 5 * Float.SIZE_BYTES, 0)
            GLES30.glVertexAttribPointer(texCoordHandle, 2, GLES30.GL_FLOAT, false, 5 * Float.SIZE_BYTES, 3 * Float.SIZE_BYTES)

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

            // 激活纹理单元
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            // 绑定纹理单元
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0])
            // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE0,因此传递0
            GLES30.glUniform1i(samplerHandle, 0)
            if (kernelHandle > 0) {
                GLES30.glUniform1fv(kernelHandle, KERNEL_SIZE, mKernel, 0)
                GLES30.glUniform2fv(uTexOffsetHandle, KERNEL_SIZE, mTexOffset, 0)
                GLES30.glUniform1f(uColorOffsetHandle, mColorOffset)
            }
            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
        }

    }

    private val mKernel = FloatArray(KERNEL_SIZE)
    private var mColorOffset = 0f
    private fun setKernel(kernel: FloatArray, colorOffset: Float) {
        System.arraycopy(kernel, 0, mKernel, 0, KERNEL_SIZE)
        mColorOffset = colorOffset
    }
    private var mTexOffset = FloatArray(16)
    private fun setTexSize(width: Int, height: Int) {
        val tw = 1f / width
        val th = 1f / height
        mTexOffset = floatArrayOf(
            -tw, -th, 0f, -th, tw, -th,
            -tw, 0f, 0f, 0f, tw, 0f,
            -tw, th, 0f, th, tw, th)
    }

    private fun loadTexture() {
        GLES30.glGenTextures(1, textureIds, 0)
        GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[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)
    }

    /**
     * 创建shader,加载shader程序
     */
    private fun loadShader(type: Int, shaderCode: String): Int {
        val shader = GLES30.glCreateShader(type)
        GLES30.glShaderSource(shader, shaderCode)
        GLES30.glCompileShader(shader)
        return shader
    }
}

高斯模糊Blur

待补充~

Lut

LUT介绍

  1. 什么是LUT:Look Up Table ,它是一种颜色查找表
  2. 作用:用颜色表中的色彩替换原来的像素颜色
  3. 原理:以每个像素的RGB值为索引
  4. 在预先定义好的颜色表中查找对应的值。
  5. 用查到的值替换原来像素的颜色

LUT种类:1D LUT,2D LUT ,3D LUT。

1D LUT:它是由256个元素的一维数组,RGB中每个通道值都到这个数组中找到对应的值,最终将三个通道的映射值构成新的像素颜色。
2D LUT:它是由256 x 256 的二维矩阵,查找像素时,以RGB中的(R,G)当做位置去检索,比如RGB(23,26,180),它会在23行,26列找到映射像素。
3D LUT:它是由64 x 64 x 64 的立方体,存储时以512 X 512 存放,查找时,先以B值进行检索,找到对应的面,接着以R为行,G为列定位置映射后像素的颜色。

3中LUT中,1D LUT 和 3DLUT使用最多,1D LUT 每个通道分开计算,因此无法做复杂计算,比如:色彩饱和度,色调的调节等。

3D LUT

在这里插入图片描述
上图按照8*8分成64个
在这里插入图片描述

使用3D LUT步骤

  • 将3D LUT图片加入到项目中

  • 将3D LUT图片加载到内存中

  • 将3D LUT图片交给TextureRender

  • 在TextureRender中,根据LUT创建纹理

  • 将LUT纹理传输到Shader中

  • 编写shader程序

  • 初始化时,创建LUT纹理

  • 将LUT纹理传给Shader

shader程序算法

  • 根据RGB中的B通道,找到相邻的两个颜色块
  • 分别在两个块中根据(R,G)找到对应的颜色
  • 将两个颜色进行混合,形成最终的替换色。
如何找到相邻的两个块
  • 将B值乘以63,得到结果b,求b所在的行和列
  • 如果b的位置不是该行的最后一个,则去b位置处理小块和它的下一个位置处的小块,否则去b位置处理小块和下一行第一个位置处的小块。
三个重要的API
  • floor:向下取整 floor(5.1)=5.0
  • ceil:向上取整 ceil(5.1)=6.0
  • fract: 取小数 fract(5.1)=0.1

glsl程序

通过bluecolor找到相邻2块的位置即:quad1,quad2
对相邻位置的颜色进行计算:newColor1、newColor2

    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;" +
        "}"

加载LUT纹理

 private 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)
    }

激活使用LUT纹理

   // 激活纹理单元
            GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
            // 绑定纹理单元
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, lutTextureIds[0])
            // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE1,因此传递1
            GLES30.glUniform1i(lutSamplerHandle, 1)

应用Demo

在这里插入图片描述


import android.graphics.Bitmap
import android.opengl.GLES30
import android.opengl.GLSurfaceView
import android.opengl.GLUtils
import android.opengl.Matrix
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10

/**
 * Day:2024/12/25 20:01
 * @author wangdanfeng02
 */
class GLLutRender(val bitmap:Bitmap,val lutBitmap:Bitmap) : GLSurfaceView.Renderer {
    /*
     * 顶点位置程序
     */
    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 coodData = floatArrayOf(
        // 顶点坐标        纹理坐标
        -1f, 1f, 0.0f, 0f, 0f,   // 左上角
        -1f, -1f, 0.0f, 0f, 1f,  //左下角
        1f, 1.0f, 0.0f, 1f, 0f,   //右上角
        1f, -1f, 0.0f, 1f, 1f  //右下角
    )

    private var translateMatrix = FloatArray(16)
    private var program: Int = 0
    private var positionHandle: Int = -1
    private var texCoordHandle: Int = -1
    private var samplerHandle: Int = -1
    private var lutSamplerHandle: Int = -1
    private var uMatrixHandle: Int = -1


    // vbo
    private var vboId = IntArray(1)
    // 纹理id array
    private var textureIds= IntArray(1)
    private var lutTextureIds= IntArray(1)

    private lateinit var coordBuffer: FloatBuffer
    private lateinit var byteBuffer: ByteBuffer

    override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {
        // 设置背景颜色
        GLES30.glClearColor(1f, 0f, 0f, 1.0f)
        // 清理缓存
        GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)
        // 顶点坐标内存申请
        createFloatBuffer()
        // 创建定点着色程序
        val vertexShader = loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
        // 创建片元着色程序
        val fragmentShader = loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShader_LUT)
        if (vertexShader != 0 && fragmentShader != 0) {
            linkProgram(vertexShader, fragmentShader)
            GLES30.glDeleteShader(vertexShader)
            GLES30.glDeleteShader(fragmentShader)
            // 生成VBO
            GLES30.glGenBuffers(1, vboId, 0)
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
            GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER, byteBuffer.capacity(), byteBuffer, GLES30.GL_STATIC_DRAW)
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)

            //
            Matrix.setIdentityM(translateMatrix, 0)

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

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

            uMatrixHandle = GLES30.glGetUniformLocation(program, "uTMatrix")
            samplerHandle = GLES30.glGetUniformLocation(program, "uSampler")
            lutSamplerHandle = GLES30.glGetUniformLocation(program, "uLutSampler")


            loadTexture(bitmap,textureIds)
            loadTexture(lutBitmap,lutTextureIds)
        }
    }

    private fun linkProgram(vertexShader: Int, fragmentShader: Int) {
        // 创建空的opengl es 程序
        program = GLES30.glCreateProgram()
        program.let {
            // 将顶点着色器加入程序
            GLES30.glAttachShader(it, vertexShader)
            // 将片元着色器加入程序
            GLES30.glAttachShader(it, fragmentShader)
            // 链接到着色器程序
            GLES30.glLinkProgram(it)
            // 将程序加入到opengl30环境中
            GLES30.glUseProgram(it)
            val info = GLES30.glGetProgramInfoLog(it)
            // 打印链接程序日志
            Log.e("wdf", "info==" + info)
        }
    }

    private fun createFloatBuffer() {
        // 申请物理层空间
        byteBuffer = ByteBuffer.allocateDirect(coodData.size * 4).apply {
            this.order(ByteOrder.nativeOrder())
        }
        // 坐标数据转换
        coordBuffer = byteBuffer.asFloatBuffer()
        coordBuffer.put(coodData, 0, coodData.size)
        coordBuffer.position(0)
    }

    override fun onSurfaceChanged(p0: GL10?, width: Int, height: Int) {
        // 计算并设置窗口大小
        val imgRatio = bitmap.width.toFloat().div(bitmap.height)
        val windowRatio = width.toFloat().div(height)
        GLES30.glViewport(0, 0, width, height)
        // 1. 矩阵数组
        // 2. 结果矩阵起始的偏移量
        // 3. left:x的最小值
        // 4. right:x的最大值
        // 5. bottom:y的最小值
        // 6. top:y的最大值
        // 7. near:z的最小值
        // 8. far:z的最大值
        // 由于是正交矩阵,所以偏移量为0,near 和 far 也不起作用
        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);
        }
    }

    override fun onDrawFrame(p0: GL10?) {
        if (program <= 0) {
            return
        }
        GLES30.glUseProgram(program)
        GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vboId[0])
        program?.let {
            GLES30.glVertexAttribPointer(positionHandle, 3, GLES30.GL_FLOAT, false, 5 * Float.SIZE_BYTES, 0)
            GLES30.glVertexAttribPointer(texCoordHandle, 2, GLES30.GL_FLOAT, false, 5 * Float.SIZE_BYTES, 3 * Float.SIZE_BYTES)

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

            // 激活纹理单元
            GLES30.glActiveTexture(GLES30.GL_TEXTURE0)
            // 绑定纹理单元
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, textureIds[0])
            // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE0,因此传递0
            GLES30.glUniform1i(samplerHandle, 0)

            // 激活纹理单元
            GLES30.glActiveTexture(GLES30.GL_TEXTURE1)
            // 绑定纹理单元
            GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, lutTextureIds[0])
            // 第二个参数传递激活的纹理单元,因为激活的是GLES30.GL_TEXTURE0,因此传递0
            GLES30.glUniform1i(lutSamplerHandle, 1)

            GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, 0, 4)
            GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)
        }

    }

    private 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)
    }

    private fun loadLutTexture(){

    }

    /**
     * 创建shader,加载shader程序
     */
    private fun loadShader(type: Int, shaderCode: String): Int {
        val shader = GLES30.glCreateShader(type)
        GLES30.glShaderSource(shader, shaderCode)
        GLES30.glCompileShader(shader)
        return shader
    }
}

Activity

class OpenGlEffectActivity : AppCompatActivity() {
    private var glSurfaceView: GLSurfaceView? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_opengl_img_demo)
        glSurfaceView = findViewById(R.id.glSurfaceView_img)
        glSurfaceView?.setEGLContextClientVersion(3)
        glSurfaceView?.setRenderer(GLLutRender(
            BitmapFactory.decodeResource(resources, R.drawable.flower),
            BitmapFactory.decodeResource(resources, R.drawable.lut_a)))
        glSurfaceView?.renderMode = GLSurfaceView.RENDERMODE_WHEN_DIRTY
    }
}

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

相关文章:

  • go gin配置air
  • Databend x 沉浸式翻译 | 基于 Databend Cloud 构建高效低成本的业务数据分析体系
  • java多线程学习笔记
  • springboot跨域配置
  • [EAI-023] FAST: Efficient Action Tokenization for Vision-Language-Action Models
  • Django实现数据库的表间三种关系
  • 让Android adb支持互联网调试脱离局域网
  • window中80端口被占用问题
  • Json格式的字符串转换为Json格式
  • 街景全景图切分六面视图(含数据处理教程,可批量处理)
  • unordered_map和unordered_set的使用
  • 读量子霸权17模拟宇宙(下)
  • IPhone14 Pro MAX 设备详情
  • 【论文推荐|深度学习,滑坡检测,多光谱影像,自然灾害,遥感】2022年Landslide4Sense竞赛成果:基于多源卫星影像的先进滑坡检测算法研究(四)
  • git gui 笔记
  • Deepseek的api调用报错乱码问题
  • KNN算法学习实践
  • 过年之无用知识研究:std::is_assignable中的declval<_Dest>() = declval<_Src>()
  • Spring Boot 高级开发指南:全面掌握微服务架构的关键技术
  • Java中的反射机制:深入理解getConstructor(Class<?>... parameterTypes)方法
  • RocketMQ事务消息是如何实现的?
  • python爬虫验证下载的图片是否损坏方法
  • lib.exe正确用法winhv.lib生成方法
  • 题解:P10972 I-Country
  • Swift 中 Codable 和 Hashable 的理解
  • < OS 有关> BaiduPCS-Go 程序的 菜单脚本 Script: BaiduPCS-Go.Menu.sh (bdgo.sh)