欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 幼教 > Open GL ES ->纹理贴图,顶点坐标和纹理坐标组合到同一个顶点缓冲对象中进行解析

Open GL ES ->纹理贴图,顶点坐标和纹理坐标组合到同一个顶点缓冲对象中进行解析

2025/9/26 23:49:10 来源:https://blog.csdn.net/sunshine_guo/article/details/146922304  浏览:    关键词:Open GL ES ->纹理贴图,顶点坐标和纹理坐标组合到同一个顶点缓冲对象中进行解析

XML文件

<?xml version="1.0" encoding="utf-8"?>
<com.example.myapplication.MyGLSurfaceView2 xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/glSurfaceView"android:layout_width="match_parent"android:layout_height="match_parent" />

Activity代码

class MainActivity6 : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main6)}
}

GLSurfaceView代码

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

GLSurfaceView.Renderer代码

class MyMatrixRenderer2(private val mContext: Context) : GLSurfaceView.Renderer {var mDrawData: MyDrawData2? = 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 = MyDrawData2().apply {initTexture0(mContext, R.drawable.picture)initShaderProgram()initVertexBuffer()}}override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {// 当 Surface 尺寸发生变化时调用,例如设备的屏幕方向发生改变, 设置视口为新的尺寸,视口是指渲染区域的大小GLES30.glViewport(0, 0, width, height)mDrawData?.resetMatrix()mDrawData?.computeMVPMatrix(width, height)}override fun onDrawFrame(gl: GL10?) {GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)mDrawData?.drawCurrentOutput()}
}

GLSurfaceView.Renderer需要的绘制数据

class MyDrawData2 {private var mProgram: Int = -1private var NO_OFFSET = 0private val VERTEX_POS_DATA_SIZE = 3private val TEXTURE_POS_DATA_SIZE = 2private val STRIDE = (VERTEX_POS_DATA_SIZE + TEXTURE_POS_DATA_SIZE) * 4 // 每个顶点的总字节数// VAO(Vertex Array Object), 顶点数组对象, 用于存储VBOprivate var mVAO = IntArray(1)// VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据private var mVBO = IntArray(1) // 只需要一个VBO// IBO(Index Buffer Object), 索引缓冲对象,用于存储顶点索引数据private var mIBO = IntArray(1)// 纹理IDprivate var mTextureID = IntArray(1)// 最终变换矩阵private var mMVPMatrix = FloatArray(16)// 投影矩阵private val mProjectionMatrix = FloatArray(16)// 视图矩阵private val mViewMatrix = FloatArray(16)// 模型矩阵private val mModelMatrix = FloatArray(16)// 视口比例private var mViewPortRatio = 1f// 顶点和纹理坐标合并在一个数组中// 格式:x, y, z, u, v (顶点坐标后跟纹理坐标)val vertexData = floatArrayOf(// 顶点坐标            // 纹理坐标-1.0f, 1.0f, 0.0f,    0.0f, 1.0f, // 左上-1.0f, -1.0f, 0.0f,   0.0f, 0.0f, // 左下1.0f, 1.0f, 0.0f,     1.0f, 1.0f, // 右上1.0f, -1.0f, 0.0f,    1.0f, 0.0f  // 右下)val vertexDataBuffer = ByteBuffer.allocateDirect(vertexData.size * 4).order(ByteOrder.nativeOrder()).asFloatBuffer().put(vertexData).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)// 初始化着色器程序fun initShaderProgram() {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.glDeleteShader(vertexShader)GLES30.glDeleteShader(fragmentShader)}// 创建VAO, VBO, IBOfun initVertexBuffer() {// 绑定VAOGLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)GLES30.glBindVertexArray(mVAO[0])// 绑定VBO - 只需要一个VBO存储所有数据GLES30.glGenBuffers(mVBO.size, mVBO, NO_OFFSET)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertexData.size * 4,vertexDataBuffer,GLES30.GL_STATIC_DRAW)// 设置顶点属性指针 - 顶点坐标val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")GLES30.glEnableVertexAttribArray(positionHandle)GLES30.glVertexAttribPointer(positionHandle,VERTEX_POS_DATA_SIZE,GLES30.GL_FLOAT,false,STRIDE,     // 步长,每个顶点5个float (x,y,z,u,v)NO_OFFSET   // 偏移量,位置数据在前)// 设置顶点属性指针 - 纹理坐标val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")GLES30.glEnableVertexAttribArray(textureHandle)GLES30.glVertexAttribPointer(textureHandle,TEXTURE_POS_DATA_SIZE,GLES30.GL_FLOAT,false,STRIDE,                          // 步长,每个顶点5个float (x,y,z,u,v)VERTEX_POS_DATA_SIZE * 4         // 偏移量,纹理数据在位置数据之后)// 绑定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)// 解绑VBO和IBOGLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 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 resetMatrix() {Matrix.setIdentityM(mModelMatrix, NO_OFFSET)Matrix.setIdentityM(mViewMatrix, NO_OFFSET)Matrix.setIdentityM(mProjectionMatrix, NO_OFFSET)Matrix.setIdentityM(mMVPMatrix, NO_OFFSET)}// 计算GLSurfaceView变换矩阵fun computeMVPMatrix(width: Int, height: Int) {val isLandscape = width > heightmViewPortRatio = if (isLandscape) width.toFloat() / height else height.toFloat() / width// 计算包围图片的球半径val radius = sqrt(1f + mViewPortRatio * mViewPortRatio)val near = 0.1fval far = near + 2 * radiusval distance = near / (near + radius)// 视图矩阵View MatrixMatrix.setLookAtM(mViewMatrix, NO_OFFSET,0f, 0f, near + radius,  // 相机位置0f, 0f, 0f,             // 看向原点0f, 1f, 0f              // 上方向)// 投影矩阵Projection MatrixMatrix.frustumM(mProjectionMatrix, NO_OFFSET,if (isLandscape) (-mViewPortRatio * distance) else (-1f * distance),  // 左边界if (isLandscape) (mViewPortRatio * distance) else (1f * distance),    // 右边界if (isLandscape) (-1f * distance) else (-mViewPortRatio  * distance),  // 下边界if (isLandscape) (1f * distance) else (mViewPortRatio * distance),    // 上边界near, // 近平面far // 远平面)// 最终变换矩阵,第一次变换,模型矩阵 x 视图矩阵 = Model x View, 但是OpenGL ES矩阵乘法是右乘,所以是View x ModelMatrix.multiplyMM(mMVPMatrix,NO_OFFSET,mViewMatrix,NO_OFFSET,mModelMatrix,NO_OFFSET)// 最终变换矩阵,第二次变换,模型矩阵 x 视图矩阵 x 投影矩阵 = Model x View x Projection, 但是OpenGL ES矩阵乘法是右乘,所以是Projection x View x ModelMatrix.multiplyMM(mMVPMatrix,NO_OFFSET,mProjectionMatrix,NO_OFFSET,mMVPMatrix,NO_OFFSET)// 纹理坐标系为(0, 0), (1, 0), (1, 1), (0, 1)的正方形逆时针坐标系,从Bitmap生成纹理,即像素拷贝到纹理坐标系// 变换矩阵需要加上一个y方向的翻转, x方向和z方向不改变Matrix.scaleM(mMVPMatrix,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)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")if (textureSampleHandle != -1) {GLES30.glUniform1i(textureSampleHandle, 0)}}fun disableTexture0() {GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)}fun initTexture0(context: Context, resourceId: Int) {mTextureID[0] = loadTexture(context, resourceId)}// GLSurfaceView实时绘制fun drawCurrentOutput() {val state = saveGLState()try {GLES30.glUseProgram(mProgram)enableTexture0(mProgram, mTextureID[0])drawSomething(mProgram, mMVPMatrix)disableTexture0()} finally {restoreGLState(state)}}// 保存OpenGL状态private fun saveGLState(): GLState {val viewport = IntArray(4)val program = IntArray(1)val framebuffer = IntArray(1)GLES30.glGetIntegerv(GLES30.GL_VIEWPORT, viewport, 0)GLES30.glGetIntegerv(GLES30.GL_CURRENT_PROGRAM, program, 0)GLES30.glGetIntegerv(GLES30.GL_FRAMEBUFFER_BINDING, framebuffer, 0)return GLState(viewport, program[0], framebuffer[0])}// 恢复OpenGL状态private fun restoreGLState(state: GLState) {GLES30.glViewport(state.viewport[0],state.viewport[1],state.viewport[2],state.viewport[3])GLES30.glUseProgram(state.program)GLES30.glBindFramebuffer(GLES30.GL_FRAMEBUFFER, state.framebuffer)}// OpenGL状态数据类data class GLState(val viewport: IntArray,val program: Int,val framebuffer: Int)object LoadShaderUtil {// 创建着色器对象fun loadShader(type: Int, source: String): Int {val shader = GLES30.glCreateShader(type)GLES30.glShaderSource(shader, source)GLES30.glCompileShader(shader)return shader}}
}

解析说明

顶点坐标和纹理坐标的存储

  • 顶点坐标的四个角和纹理坐标的四个角进行对应,顶点由(x, y, z)三个点构成,纹理由(u, v)两个点构成
// 顶点和纹理坐标合并在一个数组中
// 格式:x, y, z, u, v (顶点坐标后跟纹理坐标)
val vertexData = floatArrayOf(// 顶点坐标            // 纹理坐标-1.0f, 1.0f, 0.0f,    0.0f, 1.0f, // 左上-1.0f, -1.0f, 0.0f,   0.0f, 0.0f, // 左下1.0f, 1.0f, 0.0f,     1.0f, 1.0f, // 右上1.0f, -1.0f, 0.0f,    1.0f, 0.0f  // 右下
)

顶点坐标和纹理坐标的解析

    1. 生成并绑定需要存储顶点坐标和纹理坐标的顶点缓冲对象(Vertex Buffer Object)VBO
    • 方法: GLES30.glGenBuffers()GLES30.glBufferData()
    1. 向顶点缓冲对象中写入顶点坐标和纹理坐标数组的数组缓冲GL_ARRAY_BUFFER
    • 方法: GLES30.glBufferData()
    1. 获取顶点坐标和纹理坐标的属性指针并启用
    • 方法:GLES30.glGetAttribLocation()GLES30.glEnableVertexAttribArray()
    1. 按照步长和偏移量解析顶点坐标和纹理坐标,
    • 步长 = (顶点坐标三个点 + 纹理坐标两个点) * Float数据类型字节数为4
    • 偏移量 = 顶点坐标三个点 * Float数据类型字节数为4
    • 方法: GLES30.glVertexAttribPointer()
// 创建VAO, VBO, IBO
fun initVertexBuffer() {// 绑定VAOGLES30.glGenVertexArrays(mVAO.size, mVAO, NO_OFFSET)GLES30.glBindVertexArray(mVAO[0])// 绑定VBO - 只需要一个VBO存储所有数据GLES30.glGenBuffers(mVBO.size, mVBO, NO_OFFSET)GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertexData.size * 4,vertexDataBuffer,GLES30.GL_STATIC_DRAW)// 设置顶点属性指针 - 顶点坐标val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")GLES30.glEnableVertexAttribArray(positionHandle)GLES30.glVertexAttribPointer(positionHandle,VERTEX_POS_DATA_SIZE,GLES30.GL_FLOAT,false,STRIDE,     // 步长,每个顶点5个float (x,y,z,u,v)NO_OFFSET   // 偏移量,位置数据在前)// 设置顶点属性指针 - 纹理坐标val textureHandle = GLES30.glGetAttribLocation(mProgram, "aTexCoord")GLES30.glEnableVertexAttribArray(textureHandle)GLES30.glVertexAttribPointer(textureHandle,TEXTURE_POS_DATA_SIZE,GLES30.GL_FLOAT,false,STRIDE,                          // 步长,每个顶点5个float (x,y,z,u,v)VERTEX_POS_DATA_SIZE * 4         // 偏移量,纹理数据在位置数据之后)// 绑定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)// 解绑VBO和IBOGLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0)
}

效果图

在这里插入图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词