乒乓缓冲核心思想
- 不使用乒乓缓冲,如果要每个滤镜作用下的绘制内容,也就是这个滤镜作用下的帧缓冲,需要创建一个
Frame Buffer Object
加上对应的Frame Buffer Object Texture
- 使用乒乓缓冲,只用两个
Frame Buffer Object
加上对应的Frame Buffer Object Texture
,Open GL
渲染管线不允许读取一个帧缓冲的同时,对这个帧缓冲的纹理进行写入,这样会导致读写冲突绘制内容出错,所以用到乒乓缓冲,绑定一个帧缓冲的同时,使用另外一个帧缓冲的纹理,交替使用
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"><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 {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?) {GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f)mDrawData = DrawData().apply {initTexture0(mContext, R.drawable.picture)initShader()initVertexBuffer()initFrameBuffer()initEdgeFilterShader()initPixelFilterShader()initColorFilterShader()initPingFrameBuffer()initPongFrameBuffer()}}override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {GLES30.glViewport(0, 0, width, height)mDrawData?.computeMVPMatrix(width, height)}override fun onDrawFrame(gl: GL10?) {GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)mDrawData?.drawOriginFrameBuffer()mDrawData?.drawPixelFilterBitmap()mDrawData?.drawColorFilterBitmap()mDrawData?.drawEdgeFilterBitmap()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 private var mFBO = IntArray(1)private var mEdgeFBO = IntArray(1)private var mPixelFBO = IntArray(1)private var mColorFBO = IntArray(1)private var mPingFBO = IntArray(1)private var mPongFBO = IntArray(1)private var mVAO = IntArray(1)private var mVBO = IntArray(2)private var mIBO = IntArray(1)private var mTextureID = IntArray(1)private var mFBOTextureID = IntArray(1)private var mEdgeFBOTextureID = IntArray(1)private var mPixelFBOTextureID = IntArray(1)private var mColorFBOTextureID = IntArray(1)private var mPingTextureID = IntArray(1)private var mPongTextureID = IntArray(1)private var mMVPMatrix = FloatArray(16)private val mProjectionMatrix = FloatArray(16)private val mViewMatrix = FloatArray(16)private var mViewPortRatio = 1fprivate var mFrameBufferWidth = 0private var mFrameBufferHeight = 0private val mFrameBufferMVPMatrix = FloatArray(16)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)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 ?= nullfun 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)}fun initVertexBuffer() {GLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)GLES30.glBindVertexArray(mVAO[0])GLES30.glGenBuffers(mVBO.size, mVBO, NO_OFFSET)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertex.size * 4,vertexBuffer,GLES30.GL_STATIC_DRAW)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)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[1])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,textureCoords.size * 4,textureBuffer,GLES30.GL_STATIC_DRAW)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)GLES30.glGenBuffers(mIBO.size, mIBO, NO_OFFSET)GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, mIBO[0])GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,index.size * 2,indexBuffer,GLES30.GL_STATIC_DRAW)GLES30.glBindVertexArray(0)GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0)}fun initFrameBuffer() {GLES30.glGenFramebuffers(mFBO.size, mFBO, NO_OFFSET)GLES30.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 )GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mFBOTextureID[0], 0)if (GLES30.glCheckFramebufferStatus(GLES30.GL_FRAMEBUFFER) != GLES30.GL_FRAMEBUFFER_COMPLETE) {Log.e("yang", "initFrameBuffer: FBO初始化失败")}GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}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])GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)computeFrameBufferMVPMatrix()drawSomething(mProgram, mFrameBufferMVPMatrix)mOriginBitmap = savePixelBufferBitmap()GLES30.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 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, mPingTextureID[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, mPingFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)enableTexture2(mEdgeProgram, mPongTextureID[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, mPingFBO[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, mPongFBO[0])GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)enableTexture4(mColorProgram, mPingTextureID[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)GLES30.glBindVertexArray(mVAO[0])GLES30.glDrawElements(GLES30.GL_TRIANGLES,index.size,GLES30.GL_UNSIGNED_SHORT,NO_OFFSET)GLES30.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; 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; in vec2 vTexCoord;out vec4 fragColor;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);}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() {GLES30.glGenFramebuffers(mEdgeFBO.size, mEdgeFBO, NO_OFFSET)GLES30.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 )GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mEdgeFBOTextureID[0], 0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}fun initPixelFrameBuffer() {GLES30.glGenFramebuffers(mPixelFBO.size, mPixelFBO, NO_OFFSET)GLES30.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 )GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mPixelFBOTextureID[0], 0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}fun initColorFrameBuffer() {GLES30.glGenFramebuffers(mColorFBO.size, mColorFBO, NO_OFFSET)GLES30.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 )GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mColorFBOTextureID[0], 0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}fun initPingFrameBuffer() {GLES30.glGenFramebuffers(mPingFBO.size, mPingFBO, NO_OFFSET)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mPingFBO[0])GLES30.glGenTextures(mPingTextureID.size, mPingTextureID, NO_OFFSET)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mPingTextureID[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 )GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mPingTextureID[0], 0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}fun initPongFrameBuffer() {GLES30.glGenFramebuffers(mPongFBO.size, mPongFBO, NO_OFFSET)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, mPongFBO[0])GLES30.glGenTextures(mPongTextureID.size, mPongTextureID, NO_OFFSET)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mPongTextureID[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 )GLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, GLES30.GL_COLOR_ATTACHMENT0, GLES30.GL_TEXTURE_2D, mPongTextureID[0], 0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)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)val bitmap = Bitmap.createBitmap(mFrameBufferWidth,mFrameBufferHeight,Bitmap.Config.ARGB_8888)pixelBuffer.rewind()bitmap.copyPixelsFromBuffer(pixelBuffer)return bitmap}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 )}Matrix.setLookAtM(mViewMatrix, NO_OFFSET, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f, 0f )Matrix.multiplyMM(mMVPMatrix, NO_OFFSET, mProjectionMatrix, NO_OFFSET, mViewMatrix, NO_OFFSET )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 )}Matrix.setLookAtM(mViewMatrix, NO_OFFSET, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 1f, 0f )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")GLES30.glUniform1i(textureSampleHandle, 0)}fun 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)}fun 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)}fun 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}}
}
效果图
