主页 > 软件开发  > 

OpenGLES->GLSurfaceView纹理贴图

OpenGLES->GLSurfaceView纹理贴图
贴图

XML文件 <?xml version="1.0" encoding="utf-8"?> <com.example.myapplication.MyGLSurfaceView xmlns:android="http://schemas.android /apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" /> 自定义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 } } 自定义GLSurfaceView.Renderer代码 class MyGLRenderer(private val mContext : Context) : GLSurfaceView.Renderer { private var mDrawData: DrawData? = null override fun onSurfaceCreated(gl: GL10?, config: EGLConfig?) { // 当 Surface 创建时调用, 进行 OpenGL ES 环境的初始化操作, 设置清屏颜色为青蓝色 (Red=0, Green=0.5, Blue=0.5, Alpha=1) GLES30.glClearColor(0.0f, 0.5f, 0.5f, 1.0f) mDrawData = DrawData().apply { initVertexBuffer() initShader() loadTexture(mContext, R.drawable.bitmap_shader) } } override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) { // 当 Surface 尺寸发生变化时调用,例如设备的屏幕方向发生改变, 设置视口为新的尺寸,视口是指渲染区域的大小 GLES30.glViewport(0, 0, width, height) mDrawData? puteMVPMatrix(width.toFloat(), height.toFloat()) } override fun onDrawFrame(gl: GL10?) { // 每一帧绘制时调用, 清除颜色缓冲区 GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT) mDrawData?.drawSomething() } } GLSurfaceView.Renderer需要的绘制数据 class DrawData { private var mProgram : Int = -1 private var NO_OFFSET = 0 private val VERTEX_POS_DATA_SIZE = 3 private val TEXTURE_POS_DATA_SIZE = 2 // 纹理ID private var mTextureID = IntArray(1) // VBO IDs private var mVertexVBO = 0 private var mTexCoordVBO = 0 // 最终变化矩阵 private val mMVPMatrix = FloatArray(16) // 投影矩阵 private val mProjectionMatrix = FloatArray(16) // 相机矩阵 private val mViewMatrix = FloatArray(16) private var mViewPortRatio = 1f // 1. 准备顶点坐标,分配直接内存 // 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() // 2. 准备纹理坐标,分配直接内存 // 纹理坐标系:原点在左下角,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() // 3. 创建顶点缓冲区对象 fun initVertexBuffer(){ // 初始化顶点坐标缓冲区 vertexBuffer.put(vertex) vertexBuffer.position(NO_OFFSET) // 初始化纹理坐标缓冲区 textureBuffer.put(textureCoords) textureBuffer.position(NO_OFFSET) // 创建两个VBO,一个用于顶点坐标,一个用于纹理坐标 val vbo = IntArray(2) GLES30.glGenBuffers(vbo.size, vbo, NO_OFFSET) // 生成一个缓冲区对象ID,并存储在数组 vbo 中,存放位置为0 // 绑定顶点缓冲区 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[0]) GLES30.glBufferData( GLES30.GL_ARRAY_BUFFER, vertex.size * 4, // 数据总字节数 = 顶点数 * Float占4字节 vertexBuffer, GLES30.GL_STATIC_DRAW ) // 绑定纹理缓冲区 GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vbo[1]) GLES30.glBufferData( GLES30.GL_ARRAY_BUFFER, textureCoords.size * 4, // 数据总字节数 = 顶点数 * Float占4字节 textureBuffer, GLES30.GL_STATIC_DRAW ) mVertexVBO = vbo[0] mTexCoordVBO = vbo[1] } // 4. 初始化着色器程序 fun initShader() { val vertexShaderCode = """#version 300 es in vec4 aPosition; // 顶点坐标 uniform mat4 uMVPMatrix; // 变换矩阵 in vec2 aTexCoord; // 纹理坐标 out vec2 vTexCoord; void main() { // 输出顶点坐标和纹理坐标到片段着色器 gl_Position = uMVPMatrix * aPosition; vTexCoord = aTexCoord; }""".trimIndent() // 顶点着色器代码 val fragmentShaderCode = """#version 300 es precision mediump float; // 定义float 精度为 mediump out vec4 fragColor; // 输出片段颜色 in vec2 vTexCoord; // 接收顶点着色器传递过来的纹理坐标 uniform sampler2D uTexture; // 纹理取样器 void main() { // 使用内置函数texture, 根据纹理坐标和取样器sampler2D计算片段颜色 fragColor = texture(uTexture, 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) } // 5. 加载纹理 fun loadTexture(context: Context, resourceId: Int) { // 生成纹理 GLES30.glGenTextures(mTextureID.size, mTextureID, NO_OFFSET) // 绑定纹理 GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[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, NO_OFFSET, bitmap, NO_OFFSET) // 释放资源 bitmap.recycle() // 解绑纹理 GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, NO_OFFSET) } // 6. 计算变换矩阵 fun computeMVPMatrix(width: Float, height: Float) { // 正交投影矩阵 takeIf { width > height }?.let { mViewPortRatio = width / height Matrix.orthoM( mProjectionMatrix, // 正交投影矩阵 NO_OFFSET, // 偏移量 -mViewPortRatio, // 近平面的坐标系左边界 mViewPortRatio, // 近平面的坐标系右边界 -1f, // 近平面的坐标系的下边界 1f, // 近平面坐标系的上边界 0f, // 近平面距离相机距离 1f // 远平面距离相机距离 ) } ?: run { mViewPortRatio = height / width Matrix.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, // 相机位置x 0f, // 相机位置y 1f, // 相机位置z 0f, // 物体位置x 0f, // 物体位置y 0f, // 物体位置z 0f, // 相机上方向x 1f, // 相机上方向y 0f // 相机上方向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, ) val matrixHandler = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix") GLES30.glUniformMatrix4fv(matrixHandler, 1, false, mMVPMatrix, NO_OFFSET) } // 7. 使用着色器程序绘制图形 fun drawSomething(){ // 激活纹理编号 GLES30.glActiveTexture(GLES30.GL_TEXTURE0) GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[0]) // 激活纹理取样器 val textureSampleHandle = GLES30.glGetUniformLocation(mProgram, "uTexture") GLES30.glUniform1i(textureSampleHandle, NO_OFFSET) // 激活变换矩阵 val matrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix") GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mMVPMatrix, NO_OFFSET) // 输入顶点数据 val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition") GLES30.glEnableVertexAttribArray(positionHandle) GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVertexVBO) GLES30.glVertexAttribPointer(positionHandle, VERTEX_POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, NO_OFFSET) // 绑定纹理数据 val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord") GLES30.glEnableVertexAttribArray(textureHandle) GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mTexCoordVBO) GLES30.glVertexAttribPointer(textureHandle, TEXTURE_POS_DATA_SIZE, GLES30.GL_FLOAT, false, 0, NO_OFFSET) // 绘制纹理 GLES30.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, NO_OFFSET, vertex.size / VERTEX_POS_DATA_SIZE) // 解绑顶点数据 GLES30.glDisableVertexAttribArray(positionHandle) // 解绑纹理数据 GLES30.glDisableVertexAttribArray(textureHandle) } } object LoadShaderUtil{ // 创建着色器对象 fun loadShader(type: Int, source: String): Int { val shader = GLES30.glCreateShader(type) GLES30.glShaderSource(shader, source) GLES30.glCompileShader(shader) return shader } } 效果图

标签:

OpenGLES->GLSurfaceView纹理贴图由讯客互联软件开发栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“OpenGLES->GLSurfaceView纹理贴图