当前位置: 首页 > news >正文

OpenGL ES 索引缓冲区(4)

OpenGL ES 索引缓冲区(4)

简述

本节会介绍索引缓冲区,索引缓冲区和顶点缓冲区类似,也是显存上的一段内存,只不过上面的数据用处不同,索引缓冲区故名思义里面的数据是用于索引,主要作用是用于复用顶点缓冲区里的数据。
我们之前说过OpenGL渲染都是渲染三角形,如果我们想渲染一个正方形,就要通过渲染两个三角形,拼接成一个正方形,那么这两个三角形有两个顶点是重合的,如果没有索引缓冲区,两个三角形则需要六个顶点,而实际上一个正方形只有四个顶点,这里有两个顶点时数据冗余。仅仅一个正方形就有这么多冗余,那么一个复杂的游戏场景就会浪费非常多的内存。
下面我们就以渲染一个正方形为例来使用索引缓冲区。

接口使用

索引缓冲区的接口使用方式和顶点缓冲区类似,只不过参数不同,这里使用的GL_ELEMENT_ARRAY_BUFFER表示索引缓冲区。

GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, bufferId);
GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,size,data,type);

glDrawElements是用于渲染索引缓冲区,第一个和其他DrawCall一样,第二个参数是count,表示有多少顶点需要渲染,第三个参数是索引缓冲区参数类型,必须是GLES30.GL_UNSIGNED_SHORT或者GL_UNSIGNED_BYTE,第四个参数是offset。

GLES30.glDrawElements(GLES30.GL_TRIANGLES, count, type, offset)

不使用索引缓冲区渲染正方形

配置顶点数据

顶点数据如下,6个顶点。我我们可以发现第3个和第5个是一样的,第2个和第4个是一样的。

private float[] vertexArray = new float[] {-0.25f, -0.25f, 0.0f,0.25f, -0.25f, 0.0f,-0.25f, 0.25f, 0.0f,0.25f, -0.25f, 0.0f,-0.25f, 0.25f, 0.0f,0.25f, 0.25f, 0.0f,
};

配置着色器

着色器和我们三角形demo的时候基本是一样的,通过一个统一变量来控制颜色。

private final String vertexShaderCode ="attribute vec4 vPosition;" +"void main() {" +"  gl_Position = vPosition;" +"}";private final String fragmentShaderCode ="precision mediump float;" +"uniform vec4 vColor;" +"void main() {" +"  gl_FragColor = vColor;" +"}";

配置顶点缓冲区数据和布局

onSurfaceCreated中填充顶点缓冲区的数据以及加载着色器的逻辑和绘制三角形的时候一样。

public void onSurfaceCreated(GL10 gl, EGLConfig config) {// 清除颜色GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// 创建顶点缓冲区int[] idBuffer = new int[1];GLES30.glGenBuffers(1, idBuffer, 0);vertexBufferId = idBuffer[0];// 顶点缓冲区数据填充FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexArray.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();vertexBuffer.put(vertexArray);vertexBuffer.position(0);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertexArray.length * 4,vertexBuffer,GLES30.GL_STATIC_DRAW);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);// shadershaderProgramId = initShaderProgram(vertexShaderCode, fragmentShaderCode);
}

onDrawFrame方法中其他的基本和绘制三角形的时候一样,参数布局其实也是一样的,变化的只有glDrawArrays中count变成了6个顶点。

public void onDrawFrame(GL10 gl) {// 清除屏幕GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);// 使能着色器程序GLES30.glUseProgram(shaderProgramId);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);int positionLocation = GLES30.glGetAttribLocation(shaderProgramId, "vPosition");GLES30.glEnableVertexAttribArray(positionLocation);// 告诉GPU顶点缓冲区的布局情况,即那些数据的意义是什么。GLES30.glVertexAttribPointer(positionLocation, 3, GLES30.GL_FLOAT, false, 0, 0);// 配置统一变量,用于CPU和GPU通信的int colorLocation = GLES30.glGetUniformLocation(shaderProgramId, "vColor");GLES30.glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, 1.0f);// 调用DrawCall绘制三角形GLES30.glDrawArrays(GLES30.GL_TRIANGLES, 0, 6);// 清除配置GLES30.glDisableVertexAttribArray(positionLocation);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);GLES30.glUseProgram(0);
}

效果

渲染图像看起来不是正方形,而是长方形,是因为OpenGL的坐标比例是按照屏幕显示的比例,我们的手机屏幕是长方形的,所以按照比例也是长方形的。

在这里插入图片描述

使用索引缓冲区

我们前面看到了渲染这个正方形我们使用了6个顶点,而其中有2个顶点是重复的,我们使用索引缓冲区可以复用顶点。

配置顶点数据

顶点数据如下,indexArray为索引缓冲区的数据,必须要short或者byte类型。
这里索引缓冲区就是表示第一个三角形使用的顶点缓冲区中0,1,2号顶点,第二个三角形使用顶点缓冲区中1,2,3号顶点。

private float[] vertexArray = new float[] {-0.25f, -0.25f, 0.0f,0.25f, -0.25f, 0.0f,-0.25f, 0.25f, 0.0f,0.25f, 0.25f, 0.0f,
};private short[] indexArray = new short[] {0,1,2,1,2,3
};

配置顶点缓冲区数据和布局

着色器和之前一样,顶点缓冲区配置修改如下,除了申请顶点缓冲区之外还申请了索引缓冲区。

public void onSurfaceCreated(GL10 gl, EGLConfig config) {// 清除颜色GLES30.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// 申请两个缓冲区,一个作为顶点缓冲区,一个座位索引缓冲区int[] idBuffer = new int[2];GLES30.glGenBuffers(2, idBuffer, 0);vertexBufferId = idBuffer[0];elementBufferId = idBuffer[1];// 顶点缓冲区数据填充FloatBuffer vertexBuffer = ByteBuffer.allocateDirect(vertexArray.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();vertexBuffer.put(vertexArray);vertexBuffer.position(0);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);GLES30.glBufferData(GLES30.GL_ARRAY_BUFFER,vertexArray.length * 4,vertexBuffer,GLES30.GL_STATIC_DRAW);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);// 索引缓冲区数据填充,类型必须为short/byteShortBuffer indexBuffer = ByteBuffer.allocateDirect(indexArray.length * 4).order(ByteOrder.nativeOrder()).asShortBuffer();indexBuffer.put(indexArray);indexBuffer.position(0);GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, elementBufferId);GLES30.glBufferData(GLES30.GL_ELEMENT_ARRAY_BUFFER,indexArray.length * 4,indexBuffer,GLES30.GL_STATIC_DRAW);GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);// shadershaderProgramId = initShaderProgram(vertexShaderCode, fragmentShaderCode);
}

通过GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, elementBufferId)绑定索引缓冲区,然后调用glDrawElements来进行绘制。

public void onDrawFrame(GL10 gl) {// 清除屏幕GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);// 使能着色器程序GLES30.glUseProgram(shaderProgramId);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, vertexBufferId);int positionLocation = GLES30.glGetAttribLocation(shaderProgramId, "vPosition");GLES30.glEnableVertexAttribArray(positionLocation);// 告诉GPU顶点缓冲区的布局情况,即那些数据的意义是什么。GLES30.glVertexAttribPointer(positionLocation, 3, GLES30.GL_FLOAT, false, 0, 0);// 配置统一变量,用于CPU和GPU通信的int colorLocation = GLES30.glGetUniformLocation(shaderProgramId, "vColor");GLES30.glUniform4f(colorLocation, 1.0f, 1.0f, 1.0f, 1.0f);// 绑定索引缓冲区GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, elementBufferId);// 调用DrawCall绘制三角形GLES30.glDrawElements(GLES30.GL_TRIANGLES, 6, GLES30.GL_UNSIGNED_SHORT, 0);// 清除配置GLES30.glDisableVertexAttribArray(positionLocation);GLES30.glBindBuffer(GLES30.GL_ELEMENT_ARRAY_BUFFER, 0);GLES30.glBindBuffer(GLES30.GL_ARRAY_BUFFER, 0);GLES30.glUseProgram(0);
}

效果

和之前一样。
在这里插入图片描述

小结

索引缓冲区里面就是存储了一系列索引,用于复用顶点缓冲区。
有个问题,大家可能会想之前用6个顶点也不过18个数,使用了顶点缓冲区后变为12个,但是索引缓冲区还有6个,好像没有节省多少内存。
但是实际上顶点可能包含了非常多的数据,比如我们之前用它来存颜色,它还可以存纹理等额外数据,实际应用场景一个顶点可能有非常多数据,所以复用可节省的内存是非常可观的。

相关文章:

OpenGL ES 索引缓冲区(4)

OpenGL ES 索引缓冲区(4) 简述 本节会介绍索引缓冲区,索引缓冲区和顶点缓冲区类似,也是显存上的一段内存,只不过上面的数据用处不同,索引缓冲区故名思义里面的数据是用于索引,主要作用是用于复用顶点缓冲区里的数据。…...

01:(寄存器开发)点亮一个LED灯

寄存器开发 1、单片机的简介1.1、什么是单片机1.2、F1系列内核和芯片的系统架构1.3、存储器映像1.4、什么是寄存器 2、寄存器开发模板工程3、使用寄存器点亮一个LED4、代码改进15、代码改进2 本教程使用的是STM32F103C8T6最小系统板,教程来源B站up“嵌入式那些事”。…...

.Net 6.0 Windows平台如何判断当前电脑是否联网

最近在工作中开发需要判断当前电脑是否联网的需求,在网上找了一个调用window API来判断本机是否联网。具体请看下面介绍: 1.方法一(调用winAPI) [DllImport("wininet")] public static extern bool InternetGetConnec…...

微软准备了 Windows 11 24H2 ISO “OOBE/BypassNRO“命令依然可用

Windows 11 24H2 可能在未来几周内开始推出。 微软已经要求 OEM 遵循新的指南准备好 Windows 11 24H2 就绪的驱动程序,并且现在已经开始准备媒体文件 (.ISO)。 OEM ISO 的链接已在微软服务器上发布。 一个标有"X23-81971_26100.1742.240906-0331.ge_release_sv…...

MacOS 终端执行安装 Brew

在配置新的 Mac 环境时,如果你发现终端中无法识别 brew 命令,可以按照以下步骤进行解决。 步骤 1:确保网络稳定 为了避免安装过程中出现中断,建议使用 Wi-Fi 或有线连接,不推荐使用移动网络。 步骤 2:打…...

【设计模式-解释模式】

定义 解释器模式是一种行为设计模式,用于定义一种语言的文法,并提供一个解释器来处理该语言的句子。它通过为每个语法规则定义一个类,使得可以将复杂的表达式逐步解析和求值。这种模式适用于需要解析和执行语法规则的场景。 UML图 组成角色…...

51单片机应用开发(进阶)---数码管+按键+蜂鸣器(电磁炉显示模拟)

实现目标 1、加强数码管、按键的学习,实现数码显示变量数据(四位数的显示); 2、4位数码2个按键无源蜂鸣器实现模拟电磁炉功率调节及显示; 一、内容描述 功能描述:1、开机显示电磁炉功率300,每…...

Emergency Stop (ES)

文章目录 1. 介绍2. Feature List3. 紧急停止信号触发方式3.1 Port触发紧急停止信号3.2 SMU事件触发紧急停止信号3.3 软件触发紧急停止信号 4. 应用场景4.1 Port4.2 MSC 1. 介绍 Emergency Stop (ES)是Ifx System Control Units (SCU)六大模块之一。详细信息可以参考Infineon-…...

[C++][第三方库][gtest]详细讲解

目录 1.介绍2.安装3.使用1.头文件包含2.框架初始化接口3.调用测试样例4.TEST宏5.断言宏6.示例 1.介绍 gtest是一个跨平台的C单元测试框架,由Google公司发布gtest是为了在不同平台上为编写C单元测试而生成的,它提供了丰富的断言、致命和非致命判断、参数…...

【Java数据结构】 链表

【本节目标】 1. ArrayList 的缺陷 2. 链表 3. 链表相关 oj题目 一. ArrayList的缺陷 上节课已经熟悉了ArrayList 的使用&#xff0c;并且进行了简单模拟实现。通过源码知道&#xff0c; ArrayList 底层使用数组来存储元素&#xff1a; public class ArrayList<E>…...

前端——Ajax和jQuery

一、Ajax Ajax即“Asynchronous Javascript And XML”&#xff08;异步 JavaScript 和 XML&#xff09;&#xff0c; 通过 JS 异步的向服务器发送请 求并接收响应数据。 同步访问&#xff1a;当客户端向服务器发送请求时&#xff0c;服务器在处理的过程中&#xff0c;浏览器…...

C++-vector模拟实现

###vector底层相当于是数组&#xff0c;查看源码可以发现&#xff0c;这个类的私有成员变量是三个迭代器&#xff1b;在实现时迭代器就可以当作是vector里面的元素的指针类型&#xff1b; ###vector是一个类模板&#xff0c;实现时也应当按照这样的写法用一个模板去实现&#…...

Activity

69[toc] 1.启停活动页面 1.Activity启动和结束 从当前页面跳到新页面 startActivity(new Intent(this, ActFinishActivity.class));从当前页面返回上一个页面&#xff0c;相当于关闭当前页面 finish();2.Activity生命周期 官方描述生命周期 onCreate&#xff1a;创建活…...

【力扣 | SQL题 | 每日四题】力扣1581, 1811, 1821, 1831

今天的题目就1811这个比较难&#xff0c;其他非常的基础。 1. 力扣1581&#xff1a;进店却未进行过交易的顾客 1.1 题目&#xff1a; 表&#xff1a;Visits ---------------------- | Column Name | Type | ---------------------- | visit_id | int | | customer…...

洛谷【P1955 [NOI2015] 程序自动分析】

反思&#xff1a; 这道题一眼就是并查集 但是数据太大 mle和re都是有可能的我看了题解才知道是离散化数组加并查集离散化再两个月前我觉得好难啊 那道题跟本看不懂 现在觉得还行 离散化思路&#xff1a; 需要一个离散记录数组----ls[N]用来记录下出现的数 步骤&#xff1a; …...

Swift并发笔记

1.同步和异步 说到线程的执行方式&#xff0c;最基本的一组概念是同步和异步。所谓同步&#xff0c;就是在操作执行完成之前&#xff0c;运行操作的这个线程都会被占用&#xff0c;直到函数最终被抛出或返回。Swift5.5之前&#xff0c;func关键字声明的所有的函数都是同步的。…...

React 组件命名规范

在 React 项目中&#xff0c;如果希望保持组件命名的一致性&#xff0c;并防止在引入时出现不同名称的问题&#xff0c;可以遵循以下的组件规范&#xff1a; 1、默认导出组件&#xff1a; 所有特殊要求的组件&#xff08;如页面组件或根组件&#xff09;应该使用 export defau…...

eNSP网络配置指南:IP设置、DNS、Telnet、DHCP与路由表管理

1.eNSP基本操作和路由器IP配置命令 登录设备&#xff1a;通过Console口或通过eNSP的Telnet/SSH客户端登录到设备。进入特权模式&#xff1a;输入system-view进入系统视图。接口配置&#xff1a; 进入接口视图&#xff0c;例如interface GigabitEthernet0/0/0。配置IP地址和子网…...

初步认识产品经理

产品经理 思考问题的维度 1️⃣为什么要抓住核心用户&#xff1f; 所有和产品有关系的群体就是用户&#xff0c;存在共性和差异了解用户的付费点&#xff0c;更好的优化产品是否使用&#xff1a;&#xff08;目标用户-已使用产品&#xff1a;种子用户-尝鲜&#xff1b;核心用…...

web前端面试中拍摄的真实js面试题(真图)

web前端面试中拍摄的真实js面试题&#xff08;真图&#xff09; WechatIMG258.jpeg WechatIMG406.jpeg WechatIMG407.jpeg WechatIMG922.jpeg WechatIMG1063.jpeg © 著作权归作者所有,转载或内容合作请联系作者 喜欢的朋友记得点赞、收藏、关注哦&#xff01;&#xff01;…...

【杂谈】-递归进化:人工智能的自我改进与监管挑战

递归进化&#xff1a;人工智能的自我改进与监管挑战 文章目录 递归进化&#xff1a;人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管&#xff1f;3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表

1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)

文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

laravel8+vue3.0+element-plus搭建方法

创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...

【7色560页】职场可视化逻辑图高级数据分析PPT模版

7种色调职场工作汇报PPT&#xff0c;橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版&#xff1a;职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...

C++_哈希表

本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 一、基础概念 1. 哈希核心思想&#xff1a; 哈希函数的作用&#xff1a;通过此函数建立一个Key与存储位置之间的映射关系。理想目标&#xff1a;实现…...