OpenGL ES -> GLSurfaceView纹理贴图VBO(Vertex Buffer Object)方法实现

贴图

在这里插入图片描述

XML文件

<?xml version="1.0" encoding="utf-8"?>
<com.example.myapplication.MyGLSurfaceViewxmlns:android="http://schemas.android.com/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? = 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 {initShader()initVertexBuffer()initTexture0(mContext, R.drawable.bitmap_shader)}}override fun onSurfaceChanged(gl: GL10?, width: Int, height: Int) {// 当 Surface 尺寸发生变化时调用,例如设备的屏幕方向发生改变, 设置视口为新的尺寸,视口是指渲染区域的大小GLES30.glViewport(0, 0, width, height)mDrawData?.computeMVPMatrix(width.toFloat(), height.toFloat())}override fun onDrawFrame(gl: GL10?) {// 每一帧绘制时调用, 清除颜色缓冲区GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT)mDrawData?.enableTexture()mDrawData?.drawSomething()mDrawData?.disableTexture()}
}

GLSurfaceView.Renderer需要的绘制数据

class DrawData {private var mProgram: Int = -1private var NO_OFFSET = 0private val VERTEX_POS_DATA_SIZE = 3private val TEXTURE_POS_DATA_SIZE = 2// VBO(Vertex Buffer Object), 顶点缓冲对象,用于存储顶点数据和纹理数据private var mVBO = IntArray(2)// 最终变化矩阵private val mMVPMatrix = FloatArray(16)// 投影矩阵private val mProjectionMatrix = FloatArray(16)// 相机矩阵private val mViewMatrix = FloatArray(16)private var mViewPortRatio = 1f// 纹理IDvar mTextureID = IntArray(2)// 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().put(vertex).position(0)// 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().put(textureCoords).position(NO_OFFSET)// 3. 初始化着色器程序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;uniform sampler2D uTexture_1;in vec2 vTexCoord;out vec4 fragColor;void main() {fragColor = texture(uTexture_0, vTexCoord) + texture(uTexture_1, 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)}// 4. 创建顶点缓冲区对象fun initVertexBuffer() {// 绑定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)// 解绑顶点缓冲区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)// 解绑纹理缓冲区GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)}// 5. 计算变换矩阵fun computeMVPMatrix(width: Float, height: Float) {// 正交投影矩阵takeIf { width > height }?.let {mViewPortRatio = width / heightMatrix.orthoM(mProjectionMatrix, // 正交投影矩阵NO_OFFSET, // 偏移量-mViewPortRatio, // 近平面的坐标系左边界mViewPortRatio, // 近平面的坐标系右边界-1f, // 近平面的坐标系的下边界1f, // 近平面坐标系的上边界0f, // 近平面距离相机距离1f // 远平面距离相机距离)} ?: run {mViewPortRatio = height / 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,)}// 6. 使用着色器程序绘制图形fun drawSomething() {// 解析变换矩阵val matrixHandle = GLES30.glGetUniformLocation(mProgram, "uMVPMatrix")GLES30.glUniformMatrix4fv(matrixHandle, 1, false, mMVPMatrix, NO_OFFSET)// 解析顶点缓冲区数据到VBO[0]GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[0])val positionHandle = GLES30.glGetAttribLocation(mProgram, "aPosition")GLES30.glEnableVertexAttribArray(positionHandle)GLES30.glVertexAttribPointer(positionHandle,VERTEX_POS_DATA_SIZE,GLES30.GL_FLOAT,false,0,NO_OFFSET)// 解析纹理缓冲区数据到VBO[1]GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, mVBO[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.glDrawArrays(GLES30.GL_TRIANGLE_STRIP, NO_OFFSET, vertex.size / VERTEX_POS_DATA_SIZE)// 禁用顶点缓冲区数据GLES30.glDisableVertexAttribArray(positionHandle)// 禁用纹理缓冲区数据GLES30.glDisableVertexAttribArray(textureHandle)// 解绑VBOGLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0)}fun enableTexture() {// 激活纹理编号0GLES30.glActiveTexture(GLES30.GL_TEXTURE0)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[0])val textureSampleHandle = GLES30.glGetUniformLocation(mProgram, "uTexture_0")GLES30.glUniform1i(textureSampleHandle, 0)// 激活纹理编号1GLES30.glActiveTexture(GLES30.GL_TEXTURE1)GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, mTextureID[1])val textureSampleHandle1 = GLES30.glGetUniformLocation(mProgram, "uTexture_1")GLES30.glUniform1i(textureSampleHandle1, 1)}fun disableTexture() {GLES30.glBindTexture(GLES30.GL_TEXTURE_2D, 0)}fun initTexture0(context: Context, resourceId: Int){mTextureID[0] = loadTexture(context, resourceId)}// 加载纹理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)return textureId[0]}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/29368.html

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

相关文章

IDEA中Git版本回退终极指南:Reset与Revert双方案详解

目录 前言一、版本回退前置知识二、Reset方案&#xff1a;整体改写历史1、IDEA图形化操作&#xff08;推荐&#xff09;1.1、查看提交历史1.2、选择目标版本1.3、选择回退模式1.3.1、Soft&#xff08;推荐&#xff09;1.3.2、Mixed1.3.3、Hard&#xff08;慎用&#xff09;1.3.…

面试题02.02.返回倒数第k个节点

实现一种算法&#xff0c;找出单向链表中倒数第 k 个节点。返回该节点的值。 注意&#xff1a;本题相对原题稍作改动 示例&#xff1a; 输入&#xff1a; 1->2->3->4->5 和 k 2 输出&#xff1a; 4 说明&#xff1a; 给定的 k 保证是有效的。 题解&#xff…

【经验分享】Ubuntu20.04编译RK3568 AI模型报错问题(已解决)

【经验分享】Ubuntu20.04编译RK3568 AI模型报错问题&#xff08;已解决&#xff09; 前言问题现象问题分析解决方案总结 前言 这里使用的是Rockchip提供的rknn_model_zoo&#xff0c;https://github.com/airockchip/rknn_model_zoo/tree/main 此解决方案适用于Rockchip芯片在U…

Python的那些事第四十一篇:简化数据库交互的利器Django ORM

Django ORM:简化数据库交互的利器 摘要 随着互联网技术的飞速发展,Web开发越来越受到重视。Django作为一款流行的Python Web框架,以其高效、安全、可扩展等特点受到了广大开发者的喜爱。其中,Django ORM(对象关系映射)是Django框架的核心组件之一,它为开发者提供了一种…

Swagger UI界面的使用

访问地址 一般格式&#xff1a;http://xxxx:端口号/上下文路径/swagger-ui/index.html 首先保证当前项目已经集成Swagger的功能 上下文路径&#xff1a;指的配置文件中的&#xff1a;server.servlet.context-path的值 刚进入界面&#xff0c;找到自己的服务接口&#xff0c;开…

WPS工具栏添加Mathtype加载项

问题描述&#xff1a; 分别安装好WPS和MathType之后&#xff0c;WPS工具栏没直接显示MathType工具&#xff0c;或者是前期使用正常&#xff0c;由于WPS更新之后MathType工具消失&#xff0c;如下图 解决办法 将文件“MathType Commands 2016.dotm”和“MathPage.wll”从Matht…

部署RabbitMQ集群详细教程

部署RabbitMQ集群详细教程 下面是一份在 Ubuntu 环境下部署 RabbitMQ 集群的详细步骤说明&#xff0c;涉及主机名设置、Erlang & RabbitMQ 安装、管理插件启用、集群通信 Cookie 配置、节点加入集群、镜像队列策略设置以及集群验证等。为了演示方便&#xff0c;以下示例假…

三维数据可视化与表面重建:Marching Cubes算法的原理与应用

1. 引言 随着现代医学影像技术的飞速发展&#xff0c;三维数据的可视化与重建已成为医学研究、临床诊断和手术规划的重要工具。在众多三维重建算法中&#xff0c;Marching Cubes算法因其高效、稳定的特性成为从离散数据场中提取等值面的经典方法。本报告将深入探讨Marching Cu…

IDEA 2024.1.7 Java EE 无框架配置servlet

1、创建一个目录&#xff08;文件夹&#xff09;lib来放置我们的库 2、将tomcat目录下的lib文件夹中的servlet-api.jar文件复制到刚创建的lib文件夹下。 3、把刚才复制到lib下的servlet-api.jar添加为库 4、在src下新建一个package&#xff1a;com.demo&#xff0c;然后创…

【文生图】windows 部署stable-diffusion-webui

windows 部署stable-diffusion-webui AUTOMATIC1111 stable-diffusion-webui Detailed feature showcase with images: 带图片的详细功能展示: Original txt2img and img2img modes 原始的 txt2img 和 img2img 模式 One click install and run script (but you still must i…

【TCP/IP协议栈】【传输层】端口号、套接字、多路复用/分解、网络字节序

参考资料&#xff1a; 前言&#xff1a; 总结&#xff1a; 【计算机网络】套接字&#xff08;应用层和传输层之间的接口&#xff09; 套接字是一个通用的通信接口抽象不仅限于TCP/IP协议族作为应用层和传输层之间的桥梁支持多种通信方式和协议族 套接字定义 在 TCP 或者 UDP…

【AI大模型】DeepSeek + Kimi 高效制作PPT实战详解

目录 一、前言 二、传统 PPT 制作问题 2.1 传统方式制作 PPT 2.2 AI 大模型辅助制作 PPT 2.3 适用场景对比分析 2.4 最佳实践与推荐 三、DeepSeek Kimi 高效制作PPT操作实践 3.1 Kimi 简介 3.2 DeepSeek Kimi 制作PPT优势 3.2.1 DeepSeek 优势 3.2.2 Kimi 制作PPT优…

【哇! C++】类和对象(三) - 构造函数和析构函数

目录 一、构造函数 1.1 构造函数的引入 1.2 构造函数的定义和语法 1.2.1 无参构造函数&#xff1a; 1.2.2 带参构造函数 1.3 构造函数的特性 1.4 默认构造函数 二、析构函数 2.1 析构函数的概念 2.2 特性 如果一个类中什么成员都没有&#xff0c;简称为空类。 空类中…

基于RapidOCR与DeepSeek的智能表格转换技术实践

基于RapidOCR与DeepSeek的智能表格转换技术实践 一、技术背景与需求场景 在金融分析、数据报表处理等领域&#xff0c;存在大量图片格式的表格数据需要结构化处理。本文介绍基于开源RapidOCR表格识别与DeepSeek大模型的智能转换方案&#xff0c;实现以下典型场景&#xff1a; …

字节跳动AI原生编程工具Trae和百度“三大开发神器”AgentBuilder、AppBuilder、ModelBuilder的区别是?

字节跳动AI编程工具Trae与百度"三大开发神器"&#xff08;AgentBuilder、AppBuilder、ModelBuilder&#xff09;在定位、功能架构和技术路线上存在显著差异&#xff0c;具体区别如下&#xff1a; 一、核心定位差异 Trae&#xff1a;AI原生集成开发环境&#xff08;AI…

docker:Dockerfile案例之自定义centos7镜像

1 案例需求 自定义centos7镜像。要求&#xff1a; 默认登录路径为 /usr可以使用vim 2 实施步骤 编写dockerfile脚本 vim centos_dockerfile 内容如下&#xff1a; #定义父镜像 FROM centos:7#定义作者信息 MAINTAINER handsome <handsomehandsome.com># 设置阿里云…

【音视频】ffplay简单过滤器

一、ffplay简单过滤器 视频旋转&#xff1a;借助transpose滤镜 ffplay -i 1.mp4 -vf transpose1这里选择不同的数字是不同的方向&#xff1a; 视频翻转&#xff1a;借助hflip/vflip实现水平和垂直翻转&#xff1a; 水平翻转 ffplay 1.mp4 -vf hflip垂直翻转 ffplay 1.mp4 …

使用Kingfisher加载网络图片时使用indicatorType产生布局混乱

SheetInfoCell代码中&#xff0c;执行iconView.show(info.picurl) 时&#xff0c;若采用kf.indicatorType .activity&#xff0c;则会产生ui页面混乱&#xff0c;如果不使用这个加载动画&#xff0c;则不会产生。 与此同时&#xff0c;对于发现页面&#xff0c;是否使用加载动…

【慕课网wiki项目学习笔记01】Spring Boot 项目搭建

2-2 新建SpringBoot项目 一、创建SpringBoot项目 &#xff08;1&#xff09;在SpringBoot官网创建 &#xff08;2.1&#xff09;在 IDEA 中创建 Group&#xff1a;公司名 Artifact&#xff1a;项目名 创建成功后开始下载Maven依赖&#xff08;选择右下角的Import Changes&…

突破极限:高性能ROCK 220A-M 工业级无人机电调深度测评 —— 无人机动力系统的核心守护者

引言 在无人机技术高速发展的今天&#xff0c;动力系统的稳定性与效率成为决定任务成败的关键。作为工业级电调领域的标杆产品&#xff0c;ROCK 220A-M 凭借其卓越的性能与多重安全设计&#xff0c;为专业级无人机应用提供了可靠的动力解决方案。本文将从技术架构、防护…