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

乒乓缓冲核心思想

  • 不使用乒乓缓冲,如果要每个滤镜作用下的绘制内容,也就是这个滤镜作用下的帧缓冲,需要创建一个Frame Buffer Object加上对应的Frame Buffer Object Texture
  • 使用乒乓缓冲,只用两个Frame Buffer Object加上对应的Frame Buffer Object TextureOpen 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"><!-- 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()initPixelFilterShader()initColorFilterShader()initPingFrameBuffer()initPongFrameBuffer()}}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)private var mPingFBO = IntArray(1)private var mPongFBO = 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 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 = 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, 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)// 绑定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)// 解绑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)// 解绑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)// 解绑FBO纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)// 解绑FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}fun initPingFrameBuffer() {// 创建FBOGLES30.glGenFramebuffers(mPingFBO.size, mPingFBO, NO_OFFSET)// 绑定FBOGLES30.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 // 不传入颜色数据)// 绑定空纹理到FBO,用于绘制到FBOGLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, // FBOGLES30.GL_COLOR_ATTACHMENT0, // 颜色缓冲区GLES30.GL_TEXTURE_2D, // 纹理类型mPingTextureID[0], // 纹理ID0)// 解绑FBO纹理GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)// 解绑FBOGLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, 0)}fun initPongFrameBuffer() {// 创建FBOGLES30.glGenFramebuffers(mPongFBO.size, mPongFBO, NO_OFFSET)// 绑定FBOGLES30.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 // 不传入颜色数据)// 绑定空纹理到FBO,用于绘制到FBOGLES30.glFramebufferTexture2D(GLES30.GL_FRAMEBUFFER, // FBOGLES30.GL_COLOR_ATTACHMENT0, // 颜色缓冲区GLES30.GL_TEXTURE_2D, // 纹理类型mPongTextureID[0], // 纹理ID0)// 解绑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}}
}

效果图

在这里插入图片描述

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

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

相关文章

Unity导出WebGL,无法加载,data文件无法找到 404(NotFound)

问题&#xff1a;data文件无法找到404Not found 示例是使用IIS托管启动 F12可以看到not found 的报错 解决办法&#xff1a; iis无法识别data文件&#xff0c;在MIME类型中增加data 类型&#xff1a;application/octet-stream 添加之后&#xff0c;会在根目录下生产一个…

C++与OO思想的联系

一、C与OO思想的联系 C&#xff1a;OO思想&#xff08;面向对象--属性和行为&#xff09; 任何事务都可以被看做一个个对象&#xff0c;一个再复杂的模型结构都是由千千万万个对象组成。 OO思想两个要素&#xff1a;属性和行为(方法)。 OO思想的特点&#xff1a; 封装&#x…

单表达式倒计时工具:datetime的极度优雅(DeepSeek)

一个简单表达式&#xff0c;也可以优雅自成工具。 笔记模板由python脚本于2025-03-22 20:25:49创建&#xff0c;本篇笔记适合任意喜欢学习的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&#xff0c;而不仅仅是知识的简单复述。 Pyth…

Kubernetes的Replica Set和ReplicaController有什么区别

ReplicaSet 和 ReplicationController 是 Kubernetes 中用于管理应用程序副本的两种资源&#xff0c;它们有类似的功能&#xff0c;但 ReplicaSet 是 ReplicationController 的增强版本。 以下是它们的主要区别&#xff1a; 1. 功能的演进 ReplicationController 是 Kubernete…

CSS基础知识一览

持续维护 选择器 display 常用属性 浮动 弹性布局

IS-IS原理与配置

一、IS-IS概述 IS-IS&#xff08;Intermediate System to Intermediate System&#xff0c;中间系统到中间系统&#xff09;是ISO&#xff08;International Organization for Standardization&#xff0c;国际标准化组织&#xff09;为它的CLNP&#xff08;ConnectionLessNet…

【前端】Visual Studio Code安装配置教程:下载、汉化、常用组件、基本操作

文章目录 一、Visual Studio Code下载二、汉化三、常用组件1、Auto Rename Tag2、view-in-browser3、Live Server 四、基本操作五、感谢观看&#xff01; 一、Visual Studio Code下载 下载官网&#xff1a;https://code.visualstudio.com/ 进入官网后点击右上角的Download &…

git推送代码相关学习——(一)

推荐去阅读一下廖老师的git相关的教程https://liaoxuefeng.com/books/git/introduction/index.html 这个系列就来学习一下git操作。 第一步&#xff0c;新建项目 去github中新建一个项目&#xff0c;然后依据项目来进行本地的开发工作。 第二步&#xff0c;拉取项目 git c…

CMS网站模板设计与用户定制化实战评测

内容概要 在数字化转型背景下&#xff0c;CMS平台作为企业内容管理的核心载体&#xff0c;其模板架构的灵活性与用户定制能力直接影响运营效率。通过对WordPress、Baklib等主流系统的技术解构发现&#xff0c;模块化设计理念已成为行业基准——WordPress依托超过6万款主题库实…

Maya基本操作

基本操作 按住ALT键&#xff0c;左键旋转视角&#xff0c;中键平移视角&#xff0c;右键放大缩小视角。 按空格键切换4格视图。 导入FBX格式文件后&#xff0c;无贴图显示。 按6键开启。着色纹理显示 坐标轴相关 修改菜单-左键最上面的虚线。固定修改选项窗口。 选中物体…

政安晨【超级AI工作流】—— 使用Dify通过工作流对接ComfyUI实现多工作流协同

政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 希望政安晨的博客能够对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff01; 目录 一、准备工作 Dify跑起来 ollama局域网化配置 Dify配置并验证 启动ComfyUI 二、…

【蓝桥杯】12111暖气冰场(多源BFS 或者 二分)

思路 这题可以用BFS做&#xff0c;也可以用二分来做。 用二分这里只提供一个思路&#xff1a;对时间来二分查找&#xff0c;check函数就是检查在特定的时间 t 0 t_0 t0​内每一个暖气炉的传播距离能否覆盖所有格子。 用BFS做&#xff1a; 由几个点开始向外扩散&#xff0c;知道…

【云上CPU玩转AIGC】——腾讯云高性能应用服务HAI已支持DeepSeek-R1模型预装环境和CPU算力

&#x1f3bc;个人主页&#xff1a;【Y小夜】 &#x1f60e;作者简介&#xff1a;一位双非学校的大三学生&#xff0c;编程爱好者&#xff0c; 专注于基础和实战分享&#xff0c;欢迎私信咨询&#xff01; &#x1f386;入门专栏&#xff1a;&#x1f387;【MySQL&#xff0…

【JavaEE】网络编程socket

1.❤️❤️前言~&#x1f973;&#x1f389;&#x1f389;&#x1f389; Hello, Hello~ 亲爱的朋友们&#x1f44b;&#x1f44b;&#xff0c;这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章&#xff0c;请别吝啬你的点赞❤️❤️和收藏&#x1f4d6;&#x1f4d6;。如果你对我的…

超硬核区块链算法仿真:联盟链PBFT多线程仿真实现 :c语言完全详解版

1 22年年底想用gpt做出一个pbft的算法仿真&#xff0c;到了25年终于可以结合gpt grok perplexcity deepseek等实现了&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 1.1简化版 // 定义 Windows 版本&#xff0c;确保条件变量相关函数可用 #define _WIN32_W…

【并发编程】聊聊forkJoin的原理和最佳实践

对于线程池来说&#xff0c;其实本质就是一个生产者消费者的模式&#xff0c;而通过竞争的方式从队列中获取任务执行。本质上其实就是按照任务级别进行处理&#xff0c;但是对于一些可以分而治之的任务&#xff0c;传统的线程池没有办法分治处理。一是无法对大任务进行拆分&…

【数据预测】基于遗传算法GA的LSTM光伏功率预测 GA-LSTM光伏功率预测【Matlab代码#91】

文章目录 【可更换其他算法&#xff0c;获取资源请见文章第6节&#xff1a;资源获取】1. 遗传算法GA2. 长短期记忆网络LSTM3. 基于GA-LSTM的光伏功率预测4. 部分代码展示5. 运行结果展示6. 资源获取 【可更换其他算法&#xff0c;获取资源请见文章第6节&#xff1a;资源获取】 …

Java 填充 PDF 模版

制作 PDF 模版 安装 OnlyOffice 从 OnlyOffice 官网下载 OnlyOffice Desktop&#xff0c;安装过程很简单&#xff0c;一路下一步即可。用 OnlyOffice 制作 PDF 模版&#xff08;表单&#xff09; 使用 OnlyOffice 表单设计器&#xff0c;制作表单&#xff0c;如下图 注意命名…

使用安装 Kettle 教程 Pentoho 10.2.0.0-222 安装 连接mysql

流程 准备下载安装测试链接常见问题 准备 需要提前安装好 JDK 配置好环境变量 &#xff08;教程看前文&#xff09; 安装好mysql&#xff08;教程看前文&#xff09; 下载好pentaho链接数据库驱动 下载pentaho安装包 https://pentaho.com/wp-content/uploads/2024/04/three-s…

MySQL表的增加、查询、修改、删除的基础操作

MySQL表的增加、查询、修改、删除的基础操作 一、CRUD二、新增数据 insert2.1 单行数据 全列插入2.2 多行数据 指定列插入 三、查询 select3.1 全列查询&#xff08;select *&#xff09;3.2 指定列查询3.3 表达式查询3.4 去重&#xff1a;distinct3.5 带有排序的查询&#x…