OpenGL ES ->计算多个帧缓冲对象(Frame Buffer Object)+叠加多个滤镜作用后的Bitmap

XML文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><!-- OpenGL渲染区域 --><com.example.myapplication.MyGLSurfaceViewandroid:id="@+id/gl_surface_view"android:layout_width="match_parent"android:layout_height="match_parent" /><ImageViewandroid:id="@+id/image_view_1"android:layout_width="180dp"android:layout_height="180dp"android:layout_alignParentStart="true"android:layout_alignParentTop="true"android:layout_margin="10dp"android:background="#33000000" /><ImageViewandroid:id="@+id/image_view_2"android:layout_width="180dp"android:layout_height="180dp"android:layout_alignParentEnd="true"android:layout_alignParentTop="true"android:layout_margin="10dp"android:background="#33000000" /><ImageViewandroid:id="@+id/image_view_3"android:layout_width="180dp"android:layout_height="180dp"android:layout_alignParentStart="true"android:layout_alignParentBottom="true"android:layout_margin="10dp"android:background="#33000000" /><ImageViewandroid:id="@+id/image_view_4"android:layout_width="180dp"android:layout_height="180dp"android:layout_alignParentEnd="true"android:layout_alignParentBottom="true"android:layout_margin="10dp"android:background="#33000000" /><Buttonandroid:id="@+id/capture_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:layout_marginBottom="10dp"android:text="滤镜渲染"android:padding="12dp" /></RelativeLayout>

Activity代码

class MainActivity : AppCompatActivity() {private lateinit var glSurfaceView: MyGLSurfaceViewprivate lateinit var imageView1: ImageViewprivate lateinit var imageView2: ImageViewprivate lateinit var imageView3: ImageViewprivate lateinit var imageView4: ImageViewprivate lateinit var captureButton: Button@SuppressLint("MissingInflatedId")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)glSurfaceView = findViewById(R.id.gl_surface_view)imageView1 = findViewById(R.id.image_view_1)imageView2 = findViewById(R.id.image_view_2)imageView3 = findViewById(R.id.image_view_3)imageView4 = findViewById(R.id.image_view_4)captureButton = findViewById(R.id.capture_button)captureButton.setOnTouchListener(object : View.OnTouchListener {override fun onTouch(v: View?, event: MotionEvent?): Boolean {when (event?.action) {MotionEvent.ACTION_DOWN -> {glSurfaceView?.getDrawData()?.getEdgeFilterBitmap()?.let {imageView1.setImageBitmap(it)}glSurfaceView?.getDrawData()?.getPixelFilterBitmap()?.let {imageView2.setImageBitmap(it)}glSurfaceView?.getDrawData()?.getColorFilterBitmap()?.let {imageView3.setImageBitmap(it)}glSurfaceView?.getDrawData()?.getOriginBitmap()?.let {imageView4.setImageBitmap(it)}}MotionEvent.ACTION_UP -> {imageView1.setImageBitmap(null)imageView2.setImageBitmap(null)imageView3.setImageBitmap(null)imageView4.setImageBitmap(null)}}return true}})}
}

自定义GLSurfaceView代码

class MyGLSurfaceView(context: Context, attrs: AttributeSet) : GLSurfaceView(context, attrs) {private var mRenderer = MyGLRenderer(context)init {// 设置 OpenGL ES 3.0 版本setEGLContextClientVersion(3)setRenderer(mRenderer)// 设置渲染模式, 仅在需要重新绘制时才进行渲染,以节省资源renderMode = RENDERMODE_WHEN_DIRTY}fun getDrawData(): DrawData? {return mRenderer?.getDrawData()}
}

自定义GLSurfaceView.Renderer代码

class MyGLRenderer(private val mContext: Context) : GLSurfaceView.Renderer {private var mDrawData: DrawData? = nulloverride fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) {// 当 Surface 创建时调用, 进行 OpenGL ES 环境的初始化操作, 设置清屏颜色为青蓝色 (Red=0, Green=0.5, Blue=0.5, Alpha=1)GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)mDrawData = DrawData().apply {initTexture0(mContext, R.drawable.picture)initShader()initVertexBuffer()initFrameBuffer()initEdgeFilterShader()initEdgeFrameBuffer()initPixelFilterShader()initPixelFrameBuffer()initColorFilterShader()initColorFrameBuffer()}}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)// 绘制到FBO(离屏渲染)mDrawData?.drawOriginFrameBuffer()// 缓存像素着色器mDrawData?.drawPixelFilterBitmap()// 缓存彩色旋涡mDrawData?.drawColorFilterBitmap()// 缓存边缘滤镜mDrawData?.drawEdgeFilterBitmap()// 在GLSurfaceView绘制FBO内容mDrawData?.drawGLSurfaceView()}fun getDrawData(): DrawData? {return mDrawData}
}

GLSurfaceView.Renderer需要的绘制数据

class DrawData {private var NO_OFFSET = 0private val VERTEX_POS_DATA_SIZE = 3private val TEXTURE_POS_DATA_SIZE = 2private var mProgram: Int = -1private var mEdgeProgram : Int = -1 // 边缘检测private var mPixelProgram : Int = -1 // 像素着色private var mColorProgram : Int = -1 // 彩色旋涡// FBO(Frame Buffer Object), 帧缓冲对象,用于存储渲染后的图像private var mFBO = IntArray(1)private var mEdgeFBO = IntArray(1)private var mPixelFBO = IntArray(1)private var mColorFBO = IntArray(1)// VAO(Vertex Array Object), 顶点数组对象, 用于存储VBOprivate var mVAO = IntArray(1)// VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据private var mVBO = IntArray(2)// IBO(Index Buffer Object), 索引缓冲对象,用于存储顶点索引数据private var mIBO = IntArray(1)// 纹理IDprivate var mTextureID = IntArray(1)// FBO中的纹理IDprivate var mFBOTextureID = IntArray(1)private var mEdgeFBOTextureID = IntArray(1)private var mPixelFBOTextureID = IntArray(1)private var mColorFBOTextureID = IntArray(1)// 有上下翻转的变换矩阵private var mMVPMatrix = FloatArray(16)// 投影矩阵private val mProjectionMatrix = FloatArray(16)// 相机矩阵private val mViewMatrix = FloatArray(16)// 视口比例private var mViewPortRatio = 1f// 帧缓冲宽高private var mFrameBufferWidth = 0private var mFrameBufferHeight = 0// 帧缓冲最终变换矩阵private val mFrameBufferMVPMatrix = FloatArray(16)// 准备顶点坐标,分配直接内存// OpenGL ES坐标系:原点在中心,X轴向右为正,Y轴向上为正,Z轴向外为正val vertex = floatArrayOf(-1.0f, 1.0f, 0.0f, // 左上-1.0f, -1.0f, 0.0f, // 左下1.0f, 1.0f, 0.0f, // 右上1.0f, -1.0f, 0.0f, // 右下)val vertexBuffer = ByteBuffer.allocateDirect(vertex.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertex).position(NO_OFFSET)// 准备纹理坐标,分配直接内存// 纹理坐标系:原点在左下角,X轴向右为正,Y轴向上为正val textureCoords = floatArrayOf(0.0f, 1.0f, // 左上0.0f, 0.0f, // 左下1.0f, 1.0f, // 右上1.0f, 0.0f, // 右下)val textureBuffer = ByteBuffer.allocateDirect(textureCoords.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(textureCoords).position(NO_OFFSET)// 索引坐标,分配直接内存val index = shortArrayOf(0, 1, 2, // 第一个三角形1, 3, 2, // 第二个三角形)val indexBuffer = ByteBuffer.allocateDirect(index.size * 2).order(ByteOrder.nativeOrder()).asShortBuffer().put(index).position(NO_OFFSET)private var mOriginBitmap : Bitmap ?= nullprivate var mEdgeFilterBitmap : Bitmap ?= nullprivate var mPixelFilterBitmap : Bitmap ?= nullprivate var mColorFilterBitmap : Bitmap ?= null// 初始化着色器程序fun initShader() {val vertexShaderCode = """#version 300 esuniform mat4 uMVPMatrix; // 变换矩阵in vec4 aPosition; // 顶点坐标in vec2 aTexCoord; // 纹理坐标 out vec2 vTexCoord; void main() {// 输出顶点坐标和纹理坐标到片段着色器gl_Position = uMVPMatrix * aPosition; vTexCoord = aTexCoord;}""".trimIndent()val fragmentShaderCode = """#version 300 esprecision mediump float;uniform sampler2D uTexture_0;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, IBOfun initVertexBuffer() {// 绑定VAOGLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)GLES30.glBindVertexArray(mVAO[0])// 绑定VBOGLES30.glGenBuffers(mVBO.size, mVBO, NO_OFFSET)// 绑定顶点缓冲区数据到VBO[0]GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertex.size * 4,vertexBuffer,GLES30.GL_STATIC_DRAW)// 解析顶点缓冲区数据到VBO[0]val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")GLES30.glEnableVertexAttribArray(positionHandle)GLES30.glVertexAttribPointer(positionHandle,VERTEX_POS_DATA_SIZE,GLES30.GL_FLOAT,false,0,NO_OFFSET)// 解绑顶点缓冲区GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)// 绑定纹理缓冲区数据到VBO[1]GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[1])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,textureCoords.size * 4,textureBuffer,GLES30.GL_STATIC_DRAW)// 解析纹理缓冲区数据到VBO[1]val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")GLES30.glEnableVertexAttribArray(textureHandle)GLES30.glVertexAttribPointer(textureHandle,TEXTURE_POS_DATA_SIZE,GLES30.GL_FLOAT,false,0,NO_OFFSET)// 解绑纹理缓冲区GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)// 绑定IBOGLES30.glGenBuffers(mIBO.size, mIBO, NO_OFFSET)// 绑定索引缓冲区数据到IBO[0]GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, mIBO[0])GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,index.size * 2,indexBuffer,GLES30.GL_STATIC_DRAW)// 解绑VAOGLES30.glBindVertexArray(0)// 解绑IBOGLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0)}// 初始化帧缓冲fun initFrameBuffer() {// 创建FBOGLES30.glGenFramebuffers(mFBO.size, mFBO, NO_OFFSET)// 绑定FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])// 创建空纹理GLES30.glGenTextures(mFBOTextureID.size, mFBOTextureID, NO_OFFSET)// 绑定空纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mFBOTextureID[0])// 设置纹理参数GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_LINEAR) // 纹理缩小时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MAG_FILTER,GLES30.GL_LINEAR) // 纹理放大时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, // 纹理类型NO_OFFSET, // 偏移量GLES30.GL_RGBA, // 颜色通道mFrameBufferWidth, // 纹理宽度mFrameBufferHeight, // 纹理高度NO_OFFSET, // 偏移量GLES30.GL_RGBA, // 颜色通道GLES30.GL_UNSIGNED_BYTE, // 颜色数据类型null // 不传入颜色数据)// 绑定空纹理到FBO,用于绘制到FBOGLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, // FBOGLES30.GL_COLOR_ATTACHMENT0, // 颜色缓冲区GLES30.GL_TEXTURE_2D, // 纹理类型mFBOTextureID[0], // 纹理ID0)if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {Log.e("yang", "initFrameBuffer: FBO初始化失败")}// 解绑FBO纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)// 解绑FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}// FrameBuffer内容绘制(离屏渲染)fun drawOriginFrameBuffer() {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)val viewPort = IntArray(4)try {GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewPort, NO_OFFSET)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)enableTexture0(mProgram, mTextureID[0])// 绑定FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)computeFrameBufferMVPMatrix()drawSomething(mProgram, mFrameBufferMVPMatrix)mOriginBitmap = savePixelBufferBitmap()// 解绑FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)disableTexture0()}finally {GLES30.glViewport(viewPort[0], viewPort[1], viewPort[2], viewPort[3])GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}}// GLSurfaceView内容绘制fun drawGLSurfaceView() {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)try {GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)enableTexture0(mProgram, mEdgeFBOTextureID[0])drawSomething(mProgram, mMVPMatrix)disableTexture0()} finally {GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}}fun drawEdgeFilterBitmap() {takeIf { mEdgeFilterBitmap == null }?.let {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)val viewPort = IntArray(4)try {GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewPort, NO_OFFSET)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)GLES30.glUseProgram(mEdgeProgram)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mEdgeFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)enableTexture2(mEdgeProgram, mColorFBOTextureID[0])// 纹理大小val textureSizeHandle = GLES30.glGetUniformLocation(mEdgeProgram, "uTextureSize")GLES30.glUniform2f(textureSizeHandle, mFrameBufferWidth.toFloat(), mFrameBufferHeight.toFloat())drawSomething(mEdgeProgram, mFrameBufferMVPMatrix)mEdgeFilterBitmap = savePixelBufferBitmap()disableTexture2()GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)} finally {GLES30.glViewport(viewPort[0], viewPort[1], viewPort[2], viewPort[3])GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}}}fun drawPixelFilterBitmap() {takeIf { mPixelFilterBitmap == null }?.let {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)val viewPort = IntArray(4)try {GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewPort, NO_OFFSET)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)GLES30.glUseProgram(mPixelProgram)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mPixelFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)enableTexture3(mPixelProgram, mFBOTextureID[0])// 纹理大小val textureSizeHandle = GLES30.glGetUniformLocation(mPixelProgram, "uTextureSize")GLES30.glUniform2f(textureSizeHandle, mFrameBufferWidth.toFloat(), mFrameBufferWidth.toFloat())// 像素尺寸val pixelSizeHandle = GLES30.glGetUniformLocation(mPixelProgram, "uPixelSize")GLES30.glUniform1f(pixelSizeHandle, 15.0f) // 可调节drawSomething(mPixelProgram, mFrameBufferMVPMatrix)mPixelFilterBitmap = savePixelBufferBitmap()disableTexture3()GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)} finally {GLES30.glViewport(viewPort[0], viewPort[1], viewPort[2], viewPort[3])GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}}}fun drawColorFilterBitmap() {takeIf { mColorFilterBitmap == null }?.let {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)val viewPort = IntArray(4)try {GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewPort, NO_OFFSET)GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)GLES30.glUseProgram(mColorProgram)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mColorFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)enableTexture4(mColorProgram, mPixelFBOTextureID[0])// 流动时间val timeHandle = GLES30.glGetUniformLocation(mColorProgram, "uTime")if (timeHandle != -1) { // 检查是否存在该参数GLES30.glUniform1f(timeHandle, (System.currentTimeMillis() % 10000) / 10000.0f)}// 设置旋涡强度val twistIntensityHandle = GLES30.glGetUniformLocation(mColorProgram, "uTwistIntensity")GLES30.glUniform1f(twistIntensityHandle, 0.15f)drawSomething(mColorProgram, mFrameBufferMVPMatrix)mColorFilterBitmap = savePixelBufferBitmap()disableTexture4()GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)} finally {GLES30.glViewport(viewPort[0], viewPort[1], viewPort[2], viewPort[3])GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}}}// 使用着色器程序绘制图形fun drawSomething(program: Int, mvpMatrix: FloatArray) {// 解析变换矩阵val matrixHandle = GLES30.glGetUniformLocation(program, "uMVPMatrix")GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mvpMatrix, NO_OFFSET)// 绑定VAOGLES30.glBindVertexArray(mVAO[0])// 绘制图形GLES30.glDrawElements(GLES30.GL_TRIANGLES,index.size,GLES30.GL_UNSIGNED_SHORT,NO_OFFSET)// 解绑VAOGLES30.glBindVertexArray(0)}// 边缘检测着色器fun initEdgeFilterShader() {val vertexShaderCode = """#version 300 esuniform mat4 uMVPMatrix;in vec4 aPosition;in vec2 aTexCoord;out vec2 vTexCoord;void main() {gl_Position = uMVPMatrix * aPosition;vTexCoord = aTexCoord;}""".trimIndent()val fragmentShaderCode = """#version 300 esprecision mediump float;uniform sampler2D uTexture_2;uniform vec2 uTextureSize;in vec2 vTexCoord;out vec4 fragColor;void main() {float dx = 1.0 / uTextureSize.x;float dy = 1.0 / uTextureSize.y;// 获取周围像素vec4 center = texture(uTexture_2, vTexCoord);vec4 left = texture(uTexture_2, vTexCoord - vec2(dx, 0.0));vec4 right = texture(uTexture_2, vTexCoord + vec2(dx, 0.0));vec4 top = texture(uTexture_2, vTexCoord - vec2(0.0, dy));vec4 bottom = texture(uTexture_2, 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;// 边缘增强fragColor = vec4(vec3(1.0 - edge * 3.0), 1.0);}""".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 esuniform mat4 uMVPMatrix;in vec4 aPosition;in vec2 aTexCoord;out vec2 vTexCoord;void main() {gl_Position = uMVPMatrix * aPosition;vTexCoord = aTexCoord;}""".trimIndent()val fragmentShaderCode = """#version 300 esprecision mediump float;uniform sampler2D uTexture_3;uniform vec2 uTextureSize;uniform float uPixelSize; // 像素块大小 (10.0-30.0)in vec2 vTexCoord;out vec4 fragColor;void main() {float dx = uPixelSize / uTextureSize.x;float dy = uPixelSize / 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_3, pixelatedCoord);// 减少颜色深度,增强复古效果const float colorLevels = 5.0;pixelColor = floor(pixelColor * colorLevels) / colorLevels;// 添加微小的像素边框vec2 pixelPos = fract(vTexCoord / vec2(dx, dy));float borderFactor = step(0.95, max(pixelPos.x, pixelPos.y));pixelColor.rgb *= mix(1.0, 0.8, borderFactor);fragColor = pixelColor;}""".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 esuniform mat4 uMVPMatrix;in vec4 aPosition;in vec2 aTexCoord;out vec2 vTexCoord;void main() {gl_Position = uMVPMatrix * aPosition;vTexCoord = aTexCoord;}""".trimIndent()val fragmentShaderCode = """#version 300 esprecision mediump float;uniform sampler2D uTexture_4;uniform float uTime; // 动画时间 (如果需要)uniform float uTwistIntensity; // 旋涡强度 (0.1-0.3)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() {// 将纹理坐标转换为中心为原点的坐标vec2 center = vec2(0.5, 0.5);vec2 texCoordFromCenter = vTexCoord - center;// 计算距离和角度float distance = length(texCoordFromCenter);float angle = atan(texCoordFromCenter.y, texCoordFromCenter.x);// 应用旋涡扭曲angle += uTwistIntensity * (1.0 - distance);// 将极坐标转换回纹理坐标vec2 newCoord;newCoord.x = center.x + distance * cos(angle);newCoord.y = center.y + distance * sin(angle);// 采样扭曲后的颜色vec4 color = texture(uTexture_4, newCoord);// 增强颜色对比度和饱和度vec3 hsv = rgb2hsv(color.rgb);hsv.y = hsv.y * 1.4; // 增加饱和度hsv.z = hsv.z * 0.9 + 0.1; // 增加亮度和对比度// 添加彩虹色效果hsv.x = hsv.x + distance * 0.5;fragColor = vec4(hsv2rgb(hsv), color.a);}""".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 initEdgeFrameBuffer() {// 创建FBOGLES30.glGenFramebuffers(mEdgeFBO.size, mEdgeFBO, NO_OFFSET)// 绑定FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mEdgeFBO[0])// 创建空纹理GLES30.glGenTextures(mEdgeFBOTextureID.size, mEdgeFBOTextureID, NO_OFFSET)// 绑定空纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mEdgeFBOTextureID[0])// 设置纹理参数GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_LINEAR) // 纹理缩小时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MAG_FILTER,GLES30.GL_LINEAR) // 纹理放大时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, // 纹理类型NO_OFFSET, // 偏移量GLES30.GL_RGBA, // 颜色通道mFrameBufferWidth, // 纹理宽度mFrameBufferHeight, // 纹理高度NO_OFFSET, // 偏移量GLES30.GL_RGBA, // 颜色通道GLES30.GL_UNSIGNED_BYTE, // 颜色数据类型null // 不传入颜色数据)// 绑定空纹理到FBO,用于绘制到FBOGLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, // FBOGLES30.GL_COLOR_ATTACHMENT0, // 颜色缓冲区GLES30.GL_TEXTURE_2D, // 纹理类型mEdgeFBOTextureID[0], // 纹理ID0)if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {Log.e("yang", "initColdFrameBuffer: FBO初始化失败")}// 解绑FBO纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)// 解绑FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}// 像素滤镜的帧缓冲fun initPixelFrameBuffer() {// 创建FBOGLES30.glGenFramebuffers(mPixelFBO.size, mPixelFBO, NO_OFFSET)// 绑定FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mPixelFBO[0])// 创建空纹理GLES30.glGenTextures(mPixelFBOTextureID.size, mPixelFBOTextureID, NO_OFFSET)// 绑定空纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mPixelFBOTextureID[0])// 设置纹理参数GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_LINEAR) // 纹理缩小时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MAG_FILTER,GLES30.GL_LINEAR) // 纹理放大时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, // 纹理类型NO_OFFSET, // 偏移量GLES30.GL_RGBA, // 颜色通道mFrameBufferWidth, // 纹理宽度mFrameBufferHeight, // 纹理高度NO_OFFSET, // 偏移量GLES30.GL_RGBA, // 颜色通道GLES30.GL_UNSIGNED_BYTE, // 颜色数据类型null // 不传入颜色数据)// 绑定空纹理到FBO,用于绘制到FBOGLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, // FBOGLES30.GL_COLOR_ATTACHMENT0, // 颜色缓冲区GLES30.GL_TEXTURE_2D, // 纹理类型mPixelFBOTextureID[0], // 纹理ID0)if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {Log.e("yang", "initColdFrameBuffer: FBO初始化失败")}// 解绑FBO纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)// 解绑FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}// 彩色旋涡的帧缓冲fun initColorFrameBuffer() {// 创建FBOGLES30.glGenFramebuffers(mColorFBO.size, mColorFBO, NO_OFFSET)// 绑定FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mColorFBO[0])// 创建空纹理GLES30.glGenTextures(mColorFBOTextureID.size, mColorFBOTextureID, NO_OFFSET)// 绑定空纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mColorFBOTextureID[0])// 设置纹理参数GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MIN_FILTER,GLES30.GL_LINEAR) // 纹理缩小时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_MAG_FILTER,GLES30.GL_LINEAR) // 纹理放大时使用线性插值GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_S,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexParameteri(GLES30.GL_TEXTURE_2D,GLES30.GL_TEXTURE_WRAP_T,GLES30.GL_CLAMP_TO_EDGE) // 纹理坐标超出范围时,超出部分使用最边缘像素进行填充GLES30.glTexImage2D(GLES30.GL_TEXTURE_2D, // 纹理类型NO_OFFSET, // 偏移量GLES30.GL_RGBA, // 颜色通道mFrameBufferWidth, // 纹理宽度mFrameBufferHeight, // 纹理高度NO_OFFSET, // 偏移量GLES30.GL_RGBA, // 颜色通道GLES30.GL_UNSIGNED_BYTE, // 颜色数据类型null // 不传入颜色数据)// 绑定空纹理到FBO,用于绘制到FBOGLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, // FBOGLES30.GL_COLOR_ATTACHMENT0, // 颜色缓冲区GLES30.GL_TEXTURE_2D, // 纹理类型mColorFBOTextureID[0], // 纹理ID0)if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {Log.e("yang", "initColdFrameBuffer: FBO初始化失败")}// 解绑FBO纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)// 解绑FBOGLES30.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转换为Bitmapval bitmap = Bitmap.createBitmap(mFrameBufferWidth,mFrameBufferHeight,Bitmap.Config.ARGB_8888)pixelBuffer.rewind()bitmap.copyPixelsFromBuffer(pixelBuffer)return bitmap}// 计算GLSurfaceView变换矩阵fun computeMVPMatrix(width: Int, height: Int) {// 正交投影矩阵takeIf { width > height }?.let {mViewPortRatio = (width * 1f) / heightMatrix.orthoM(mProjectionMatrix, // 正交投影矩阵NO_OFFSET, // 偏移量-mViewPortRatio, // 近平面的坐标系左边界mViewPortRatio, // 近平面的坐标系右边界-1f, // 近平面的坐标系的下边界1f, // 近平面坐标系的上边界0f, // 近平面距离相机距离1f // 远平面距离相机距离)} ?: run {mViewPortRatio = (height * 1f) / widthMatrix.orthoM(mProjectionMatrix, // 正交投影矩阵NO_OFFSET, // 偏移量-1f, // 近平面坐标系左边界1f, // 近平面坐标系右边界-mViewPortRatio, // 近平面坐标系下边界mViewPortRatio, // 近平面坐标系上边界0f, // 近平面距离相机距离1f // 远平面距离相机距离)}// 设置相机矩阵// 相机位置(0f, 0f, 1f)// 物体位置(0f, 0f, 0f)// 相机方向(0f, 1f, 0f)Matrix.setLookAtM(mViewMatrix, // 相机矩阵NO_OFFSET, // 偏移量0f, // 相机位置x0f, // 相机位置y1f, // 相机位置z0f, // 物体位置x0f, // 物体位置y0f, // 物体位置z0f, // 相机上方向x1f, // 相机上方向y0f // 相机上方向z)// 最终变化矩阵Matrix.multiplyMM(mMVPMatrix, // 最终变化矩阵NO_OFFSET, // 偏移量mProjectionMatrix, // 投影矩阵NO_OFFSET, // 投影矩阵偏移量mViewMatrix, // 相机矩阵NO_OFFSET // 相机矩阵偏移量)// 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系// 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变Matrix.scaleM(mMVPMatrix,NO_OFFSET,1f,-1f,1f,)}fun computeFrameBufferMVPMatrix() {// 正交投影矩阵takeIf { mFrameBufferWidth > mFrameBufferHeight }?.let {mViewPortRatio = (mFrameBufferWidth * 1f) / mFrameBufferHeightMatrix.orthoM(mProjectionMatrix, // 正交投影矩阵NO_OFFSET, // 偏移量-mViewPortRatio, // 近平面的坐标系左边界mViewPortRatio, // 近平面的坐标系右边界-1f, // 近平面的坐标系的下边界1f, // 近平面坐标系的上边界0f, // 近平面距离相机距离1f // 远平面距离相机距离)} ?: run {mViewPortRatio = (mFrameBufferHeight * 1f) / mFrameBufferWidthMatrix.orthoM(mProjectionMatrix, // 正交投影矩阵NO_OFFSET, // 偏移量-1f, // 近平面坐标系左边界1f, // 近平面坐标系右边界-mViewPortRatio, // 近平面坐标系下边界mViewPortRatio, // 近平面坐标系上边界0f, // 近平面距离相机距离1f // 远平面距离相机距离)}// 设置相机矩阵// 相机位置(0f, 0f, 1f)// 物体位置(0f, 0f, 0f)// 相机方向(0f, 1f, 0f)Matrix.setLookAtM(mViewMatrix, // 相机矩阵NO_OFFSET, // 偏移量0f, // 相机位置x0f, // 相机位置y1f, // 相机位置z0f, // 物体位置x0f, // 物体位置y0f, // 物体位置z0f, // 相机上方向x1f, // 相机上方向y0f // 相机上方向z)// 最终变化矩阵Matrix.multiplyMM(mFrameBufferMVPMatrix, // 最终变化矩阵NO_OFFSET, // 偏移量mProjectionMatrix, // 投影矩阵NO_OFFSET, // 投影矩阵偏移量mViewMatrix, // 相机矩阵NO_OFFSET // 相机矩阵偏移量)// 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系// 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变
//        Matrix.scaleM(
//            mFrameBufferMVPMatrix,
//            NO_OFFSET,
//            1f,
//            -1f,
//            1f,
//        )}// 加载纹理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]}// 激活纹理编号0fun enableTexture0(program: Int, id: Int) {GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, id)val textureSampleHandle = GLES30.glGetUniformLocation(program, "uTexture_0")GLES30.glUniform1i(textureSampleHandle, 0)}// 激活纹理编号2fun enableTexture2(program: Int, id: Int) {GLES30.glActiveTexture(GLES30.GL_TEXTURE2)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, id)val textureSampleHandle2 = GLES30.glGetUniformLocation(program, "uTexture_2")GLES30.glUniform1i(textureSampleHandle2, 2)}// 激活纹理编号3fun enableTexture3(program: Int, id: Int) {GLES30.glActiveTexture(GLES30.GL_TEXTURE3)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, id)val textureSampleHandle3 = GLES30.glGetUniformLocation(program, "uTexture_3")GLES30.glUniform1i(textureSampleHandle3, 3)}// 激活纹理编号4fun enableTexture4(program: Int, id: Int) {GLES30.glActiveTexture(GLES30.GL_TEXTURE4)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, id)val textureSampleHandle4 = GLES30.glGetUniformLocation(program, "uTexture_4")GLES30.glUniform1i(textureSampleHandle4, 4)}fun disableTexture0() {GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)}fun disableTexture2() {GLES30.glActiveTexture(GLES30.GL_TEXTURE2)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 2)}fun disableTexture3() {GLES30.glActiveTexture(GLES30.GL_TEXTURE3)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 3)}fun disableTexture4(){GLES30.glActiveTexture(GLES30.GL_TEXTURE4)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 4)}fun initTexture0(context: Context, resourceId: Int) {mTextureID[0] = loadTexture(context, resourceId)}fun getEdgeFilterBitmap(): Bitmap? {return mEdgeFilterBitmap}fun getPixelFilterBitmap(): Bitmap? {return mPixelFilterBitmap}fun getColorFilterBitmap(): Bitmap? {return mColorFilterBitmap}fun getOriginBitmap(): Bitmap? {return mOriginBitmap}object LoadShaderUtil {// 创建着色器对象fun loadShader(type: Int, source: String): Int {val shader = GLES30.glCreateShader(type)GLES30.glShaderSource(shader, source)GLES30.glCompileShader(shader)return shader}}
}

关键代码

Renderer.onSurfaceCreated中的初始化

  • 初始化原图纹理
  • 初始化原图着色器程序
  • 初始化顶点坐标和纹理坐标缓冲区
  • 初始化原图帧缓冲
  • 初始化其他三个滤镜的着色器程序和帧缓冲
    • 此时的原图帧缓冲和其他三个滤镜的帧缓冲附着的纹理均为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 = DrawData().apply {initTexture0(mContext, R.drawable.picture)initShader()initVertexBuffer()initFrameBuffer()initEdgeFilterShader()initEdgeFrameBuffer()initPixelFilterShader()initPixelFrameBuffer()initColorFilterShader()initColorFrameBuffer()}
}

Renderer.onDrawFrame中的绘制

  • 先给原图帧缓冲绘制 mDrawData?.drawOriginFrameBuffer(),完成后原图帧缓冲中会有一张原图纹理
    • 先保存当前视口,当前着色器程序,当前作用的帧缓冲
    • 激活纹理编号采样器0,绑定需要使用原图纹理ID
    • 绑定需要作用的帧缓冲ID对象,先清除帧缓冲上的颜色
    • 开始绘制原图纹理
    • 解绑作用帧缓冲的ID对象,解绑使用的原图纹理ID
    • 恢复视口,着色器程序,帧缓冲
  • 再给像素滤镜帧缓冲绘制mDrawData?.drawPixelFilterBitmap(),绘制时绑定的是原图纹理ID,绘制完成后像素滤镜帧缓冲会保留原图+像素滤镜的纹理
  • 再给彩色旋涡滤镜帧缓冲绘制mDrawData?.drawColorFilterBitmap(),绘制时绑定的是像素滤镜帧缓冲纹理ID,绘制完成后彩色旋涡滤镜帧缓冲会保留原图+像素滤镜+彩色旋涡滤镜的纹理
  • 再给边缘滤镜帧缓冲绘制mDrawData?.drawEdgeFilterBitmap(),绘制时绑定的是彩色旋涡滤镜帧缓冲纹理ID,绘制完成后边缘滤镜帧缓冲会保留原图+像素滤镜+彩色旋涡滤镜+边缘滤镜的纹理
  • 再给GLSurfaceView绘制三个滤镜作用的纹理,直接绑定边缘滤镜帧缓冲ID
override fun onDrawFrame(gl: GL10?) {GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)// 绘制到FBO(离屏渲染)mDrawData?.drawOriginFrameBuffer()// 缓存像素着色器mDrawData?.drawPixelFilterBitmap()// 缓存彩色旋涡mDrawData?.drawColorFilterBitmap()// 缓存边缘滤镜mDrawData?.drawEdgeFilterBitmap()// 在GLSurfaceView绘制FBO内容mDrawData?.drawGLSurfaceView()
}// FrameBuffer内容绘制(离屏渲染)
fun drawOriginFrameBuffer() {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)val viewPort = IntArray(4)try {GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewPort, NO_OFFSET)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)enableTexture0(mProgram, mTextureID[0])// 绑定FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)computeFrameBufferMVPMatrix()drawSomething(mProgram, mFrameBufferMVPMatrix)mOriginBitmap = savePixelBufferBitmap()// 解绑FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)disableTexture0()}finally {GLES30.glViewport(viewPort[0], viewPort[1], viewPort[2], viewPort[3])GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}
}fun drawEdgeFilterBitmap() {takeIf { mEdgeFilterBitmap == null }?.let {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)val viewPort = IntArray(4)try {GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewPort, NO_OFFSET)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)GLES30.glUseProgram(mEdgeProgram)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mEdgeFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)enableTexture2(mEdgeProgram, mColorFBOTextureID[0])// 纹理大小val textureSizeHandle = GLES30.glGetUniformLocation(mEdgeProgram, "uTextureSize")GLES30.glUniform2f(textureSizeHandle, mFrameBufferWidth.toFloat(), mFrameBufferHeight.toFloat())drawSomething(mEdgeProgram, mFrameBufferMVPMatrix)mEdgeFilterBitmap = savePixelBufferBitmap()disableTexture2()GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)} finally {GLES30.glViewport(viewPort[0], viewPort[1], viewPort[2], viewPort[3])GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}}
}fun drawPixelFilterBitmap() {takeIf { mPixelFilterBitmap == null }?.let {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)val viewPort = IntArray(4)try {GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewPort, NO_OFFSET)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)GLES30.glUseProgram(mPixelProgram)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mPixelFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)enableTexture3(mPixelProgram, mFBOTextureID[0])// 纹理大小val textureSizeHandle = GLES30.glGetUniformLocation(mPixelProgram, "uTextureSize")GLES30.glUniform2f(textureSizeHandle, mFrameBufferWidth.toFloat(), mFrameBufferWidth.toFloat())// 像素尺寸val pixelSizeHandle = GLES30.glGetUniformLocation(mPixelProgram, "uPixelSize")GLES30.glUniform1f(pixelSizeHandle, 15.0f) // 可调节drawSomething(mPixelProgram, mFrameBufferMVPMatrix)mPixelFilterBitmap = savePixelBufferBitmap()disableTexture3()GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)} finally {GLES30.glViewport(viewPort[0], viewPort[1], viewPort[2], viewPort[3])GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}}
}fun drawColorFilterBitmap() {takeIf { mColorFilterBitmap == null }?.let {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)val viewPort = IntArray(4)try {GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewPort, NO_OFFSET)GLES30.glViewport(0, 0, mFrameBufferWidth, mFrameBufferHeight)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)GLES30.glUseProgram(mColorProgram)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mColorFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)enableTexture4(mColorProgram, mPixelFBOTextureID[0])// 流动时间val timeHandle = GLES30.glGetUniformLocation(mColorProgram, "uTime")if (timeHandle != -1) { // 检查是否存在该参数GLES30.glUniform1f(timeHandle, (System.currentTimeMillis() % 10000) / 10000.0f)}// 设置旋涡强度val twistIntensityHandle = GLES30.glGetUniformLocation(mColorProgram, "uTwistIntensity")GLES30.glUniform1f(twistIntensityHandle, 0.15f)drawSomething(mColorProgram, mFrameBufferMVPMatrix)mColorFilterBitmap = savePixelBufferBitmap()disableTexture4()GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)} finally {GLES30.glViewport(viewPort[0], viewPort[1], viewPort[2], viewPort[3])GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}}
}// GLSurfaceView内容绘制fun drawGLSurfaceView() {val previousProgram = IntArray(1)val previousFrameBuffer = IntArray(1)try {GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, previousProgram, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, previousFrameBuffer, 0)enableTexture0(mProgram, mEdgeFBOTextureID[0])drawSomething(mProgram, mMVPMatrix)disableTexture0()} finally {GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, previousFrameBuffer[0])GLES30.glUseProgram(previousProgram[0])}}

效果图

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/73039.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java线程池深度解析:从使用到调优

适合人群&#xff1a;Java中级开发者 | 并发编程入门者 | 系统调优实践者 目录 一、引言&#xff1a;为什么线程池是Java并发的核心&#xff1f; 二、线程池核心知识点详解 1. 线程池核心参数与原理 2. 线程池的创建与使用 (1) 基础用法示例 (2) 内置线程池的隐患 3. 线…

【工具变量】全国地级市地方ZF债务数据集(2014-2023年)

地方ZF债务是地方财政运作的重要组成部分&#xff0c;主要用于基础设施建设、公共服务及经济发展&#xff0c;是衡量地方财政健康状况的重要指标。近年来&#xff0c;我国地级市的地方ZF债务规模不断变化&#xff0c;涉及一般债务和专项债务等多个方面&#xff0c;对金融市场、…

大模型训练的调参与算力调度技术分析

大模型训练的调参与算力调度 虽然从网络上&#xff0c;还有通过和大模型交流&#xff0c;了解了很多训练和微调的技术。但没有实践&#xff0c;也没有什么机会实践。因为大模型训练门槛还是挺高的&#xff0c;想要有一手资料比较困难。如果需要多机多卡&#xff0c;硬件成本小…

深入理解 lt; 和 gt;:HTML 实体转义的核心指南!!!

&#x1f6e1;️ 深入理解 < 和 >&#xff1a;HTML 实体转义的核心指南 &#x1f6e1;️ 在编程和文档编写中&#xff0c;< 和 > 符号无处不在&#xff0c;但它们也是引发语法错误、安全漏洞和渲染混乱的头号元凶&#xff01;&#x1f525; 本文将聚焦 <&#…

GRS认证的注意事项!GRS认证的定义

GRS认证的注意事项&#xff0c;对于企业而言&#xff0c;是通往可持续发展和环保生产道路上的重要里程碑。在追求这一认证的过程中&#xff0c;企业必须细致入微&#xff0c;确保每一个环节都符合严格的标准与要求。 首先&#xff0c;企业必须全面理解GRS认证的核心原则&#…

位运算--求二进制中1的个数

位运算–求二进制中1的个数 给定一个长度为 n 的数列&#xff0c;请你求出数列中每个数的二进制表示中 1 的个数。 输入格式 第一行包含整数 n。 第二行包含 n 个整数&#xff0c;表示整个数列。 输出格式 共一行&#xff0c;包含 n 个整数&#xff0c;其中的第 i 个数表…

Linux常用指令(3)

大家好,今天我们继续来介绍一下linux常用指令的语法,加深对linux操作系统的了解,话不多说,来看. 1.rmdir指令 功能&#xff1a;删除空目录 基本语法&#xff1a; rmdir 要删除的空目录 ⭐️rmdir删除的是空目录,如果目录下有内容是无法删除 2.mkdir指令 功能&#xff1a;创…

《Linux 网络架构:基于 TCP 协议的多人聊天系统搭建详解》

一、系统概述 本系统是一个基于 TCP 协议的多人聊天系统&#xff0c;由一个服务器和多个客户端组成。客户端可以连接到服务器&#xff0c;向服务器发送消息&#xff0c;服务器接收到消息后将其转发给其他客户端&#xff0c;实现多人之间的实时聊天。系统使用 C 语言编写&#x…

JavaIO流的使用和修饰器模式(直击心灵版)

系列文章目录 JavaIO流的使用和修饰器模式 文章目录 系列文章目录前言一、字节流&#xff1a; 1.FileInputStream(读取文件)2.FileOutputStream(写入文件) 二、字符流&#xff1a; 1..基础字符流:2.处理流&#xff1a;3.对象处理流&#xff1a;4.转换流&#xff1a; 三、修饰器…

【设计模式】SOLID 设计原则概述

SOLID 是面向对象设计中的五大原则&#xff0c;不管什么面向对象的语言&#xff0c; 这个准则都很重要&#xff0c;如果你没听说过&#xff0c;赶紧先学一下。它可以提高代码的可维护性、可扩展性和可读性&#xff0c;使代码更加健壮、易于测试和扩展。SOLID 代表以下五个设计原…

可发1区的超级创新思路:基于注意力机制的DSD-CNN时间序列预测模型(功率预测、交通流量预测、故障检测)

首先声明,该模型为原创!原创!原创! 一、应用场景 该模型主要用于时间序列数据预测问题,包含功率预测、电池寿命预测、电机故障检测等等 二、模型整体介绍(本文以光伏功率预测为例) DSD-CNN(Depthwise-Spacewise Separable CNN)结合通道注意力机制,通过以下创新提升…

wsl2配置xv6全解(包括22.04Jammy)

文章目录 获取xv6源代码Ubuntu20.04 Version安装指令成功测试参考MIT2021年官方文档 24.04 Version安装指令成功测试参考MIT2024年官方文档 Ubuntu 22.04没有官方文档&#xff1f; 配置大体流程1. 卸载原本qemu&#xff08;如果之前安装了&#xff09;2. clone qemu官方源代码&…

招聘面试季--一文顿悟,Java中字节流和字符流的区别及使用场景上的差异

‌一、核心区别‌ ‌特性‌‌字节流‌‌字符流‌‌数据单位‌以字节&#xff08;8-bit&#xff09;为单位处理数据&#xff08;如0xA1&#xff09;以字符&#xff08;16-bit Unicode&#xff09;为单位处理数据&#xff08;如A, 你&#xff09;‌基类‌InputStream / OutputSt…

车载以太网网络测试-16【传输层-UDP】

目录 1 摘要2 车载以太网传输层概述3 车载以太网UDP协议3.1 车载以太网UDP协议的作用3.2 UDP报文帧结构3.3 UDP协议的通信过程3.3.1 通信过程3.3.2 实例示例3.3.3 代码示例 4 总结 1 摘要 车载以太网的第五层是传输层&#xff0c;它在车载网络架构中扮演着至关重要的角色。主要…

深度强化学习中的深度神经网络优化策略:挑战与解决方案

I. 引言 深度强化学习&#xff08;Deep Reinforcement Learning&#xff0c;DRL&#xff09;结合了强化学习&#xff08;Reinforcement Learning&#xff0c;RL&#xff09;和深度学习&#xff08;Deep Learning&#xff09;的优点&#xff0c;使得智能体能够在复杂的环境中学…

无人机点对点技术要点分析!

一、技术架构 1. 网络拓扑 Ad-hoc网络&#xff1a;无人机动态组建自组织网络&#xff0c;节点自主协商路由&#xff0c;无需依赖地面基站。 混合架构&#xff1a;部分场景结合中心节点&#xff08;如指挥站&#xff09;与P2P网络&#xff0c;兼顾集中调度与分布式协同。 2.…

MQ,RabbitMQ,MQ的好处,RabbitMQ的原理和核心组件,工作模式

1.MQ MQ全称 Message Queue&#xff08;消息队列&#xff09;&#xff0c;是在消息的传输过程中 保存消息的容器。它是应用程序和应用程序之间的通信方法 1.1 为什么使用MQ 在项目中&#xff0c;可将一些无需即时返回且耗时的操作提取出来&#xff0c;进行异步处理&#xff0…

django怎么配置404和500

在 Django 中&#xff0c;配置 404 和 500 错误页面需要以下步骤&#xff1a; 1. 创建自定义错误页面模板 首先&#xff0c;创建两个模板文件&#xff0c;分别用于 404 和 500 错误页面。假设你的模板目录是 templates/。 404 页面模板 创建文件 templates/404.html&#x…

各类神经网络学习:(四)RNN 循环神经网络(下集),pytorch 版的 RNN 代码编写

上一篇下一篇RNN&#xff08;中集&#xff09;待编写 代码详解 pytorch 官网主要有两个可调用的模块&#xff0c;分别是 nn.RNNCell 和 nn.RNN &#xff0c;下面会进行详细讲解。 RNN 的同步多对多、多对一、一对多等等结构都是由这两个模块实现的&#xff0c;只需要将对输入…

深度学习篇---深度学习中的范数

文章目录 前言一、向量范数1.L0范数1.1定义1.2计算式1.3特点1.4应用场景1.4.1特征选择1.4.2压缩感知 2.L1范数&#xff08;曼哈顿范数&#xff09;2.1定义2.2计算式2.3特点2.4应用场景2.4.1L1正则化2.4.2鲁棒回归 3.L2范数&#xff08;欧几里得范数&#xff09;3.1定义3.2特点3…