乒乓缓冲核心思想
- 不使用乒乓缓冲,如果要每个滤镜作用下的绘制内容,也就是这个滤镜作用下的帧缓冲,需要创建一个
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.MyGLSurfaceView
android:id="@+id/gl_surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ImageView
android: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" />
<ImageView
android: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" />
<ImageView
android: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" />
<ImageView
android: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" />
<Button
android: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: MyGLSurfaceView
private lateinit var imageView1: ImageView
private lateinit var imageView2: ImageView
private lateinit var imageView3: ImageView
private lateinit var imageView4: ImageView
private 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? = null
override 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 = 0
private val VERTEX_POS_DATA_SIZE = 3
private val TEXTURE_POS_DATA_SIZE = 2
private var mProgram: Int = -1
private var mEdgeProgram : Int = -1
private var mPixelProgram : Int = -1
private var mColorProgram : Int = -1
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 = 1f
private var mFrameBufferWidth = 0
private var mFrameBufferHeight = 0
private 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 ?= null
private var mEdgeFilterBitmap : Bitmap ?= null
private var mPixelFilterBitmap : Bitmap ?= null
private var mColorFilterBitmap : Bitmap ?= null
fun initShader() {
val vertexShaderCode = """#version 300 es
uniform mat4 uMVPMatrix;
in vec4 aPosition;
in vec2 aTexCoord;
out vec2 vTexCoord;
void main() {
gl_Position = uMVPMatrix * aPosition;
vTexCoord = aTexCoord;
}""".trimIndent()
val fragmentShaderCode = """#version 300 es
precision mediump float;
uniform sampler2D uTexture_0;
in vec2 vTexCoord;
out vec4 fragColor;
void main() {
fragColor = texture(uTexture_0, vTexCoord);
}""".trimIndent()
val vertexShader = LoadShaderUtil.loadShader(GLES30.GL_VERTEX_SHADER, vertexShaderCode)
val fragmentShader =
LoadShaderUtil.loadShader(GLES30.GL_FRAGMENT_SHADER, fragmentShaderCode)
mProgram = GLES30.glCreateProgram()
GLES30.glAttachShader(mProgram, vertexShader)
GLES30.glAttachShader(mProgram, fragmentShader)
GLES30.glLinkProgram(mProgram)
GLES30.glUseProgram(mProgram)
GLES30.glDeleteShader(vertexShader)
GLES30.glDeleteShader(fragmentShader)
}
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 es
uniform mat4 uMVPMatrix;
in vec4 aPosition;
in vec2 aTexCoord;
out vec2 vTexCoord;
void main() {
gl_Position = uMVPMatrix * aPosition;
vTexCoord = aTexCoord;
}""".trimIndent()
val fragmentShaderCode = """#version 300 es
precision mediump float;
uniform sampler2D uTexture_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 es
uniform mat4 uMVPMatrix;
in vec4 aPosition;
in vec2 aTexCoord;
out vec2 vTexCoord;
void main() {
gl_Position = uMVPMatrix * aPosition;
vTexCoord = aTexCoord;
}""".trimIndent()
val fragmentShaderCode = """#version 300 es
precision mediump float;
uniform sampler2D uTexture_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 es
uniform mat4 uMVPMatrix;
in vec4 aPosition;
in vec2 aTexCoord;
out vec2 vTexCoord;
void main() {
gl_Position = uMVPMatrix * aPosition;
vTexCoord = aTexCoord;
}""".trimIndent()
val fragmentShaderCode = """#version 300 es
precision mediump float;
uniform sampler2D uTexture_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) / height
Matrix.orthoM(
mProjectionMatrix,
NO_OFFSET,
-mViewPortRatio,
mViewPortRatio,
-1f,
1f,
0f,
1f
)
} ?: run {
mViewPortRatio = (height * 1f) / width
Matrix.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) / mFrameBufferHeight
Matrix.orthoM(
mProjectionMatrix,
NO_OFFSET,
-mViewPortRatio,
mViewPortRatio,
-1f,
1f,
0f,
1f
)
} ?: run {
mViewPortRatio = (mFrameBufferHeight * 1f) / mFrameBufferWidth
Matrix.orthoM(
mProjectionMatrix,
NO_OFFSET,
-1f,
1f,
-mViewPortRatio,
mViewPortRatio,
0f,
1f
)
}
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
}
}
}
效果图
