WebGL 缓冲区对象介绍,创建并使用缓冲区,使用缓冲区对象向顶点着色器传入多个顶点数据的所有步骤

目录

使用缓冲区对象 

使用缓冲区对象向顶点着色器传入多个顶点的数据,需要遵循以下五个步骤。

创建缓冲区对象(gl.createBuffer()) 

gl.createBuffer()的函数规范

gl.deleteBuffer ()

绑定缓冲区(gl.bindBuffer()) 

gl.bindBuffer()的函数规范

向缓冲区对象中写入数据(gl.bufferData())

gl.bufferData()的规范

类型化数组 

将缓冲区对象分配给attribute变量(gl.vertexAttribPointer())

 gl.vertexAttribPointer()的规范如下

开启attribute变量(gl.enableVertexAttribArray())

​编辑 gl.disableVertexAttribArray()

gl.drawArrays()的第2个和第3个参数


WebGL提供了一种很方便的机制,即缓冲区对象(buffer object),它可以一次性地向着色器传入多个顶点的数据。缓冲区对象是WebGL系统中的一块内存区域,我们可以一次性地向缓冲区对象中填充大量的顶点数据,然后将这些数据保存在其中,供顶点着色器使用。

在进一步解释缓冲区对象前,让我们先浏览一下下面的示例程序

  

var VSHADER_SOURCE ='attribute vec4 a_Position;\n' +'void main() {\n' +'  gl_Position = a_Position;\n' +'  gl_PointSize = 10.0;\n' +'}\n';var FSHADER_SOURCE ='void main() {\n' +'  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +'}\n';function main() {// Retrieve <canvas> elementvar canvas = document.getElementById('webgl');// Get the rendering context for WebGLvar gl = getWebGLContext(canvas);if (!gl) {console.log('Failed to get the rendering context for WebGL');return;}// Initialize shadersif (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('Failed to intialize shaders.');return;}// Write the positions of vertices to a vertex shadervar n = initVertexBuffers(gl);if (n < 0) {console.log('Failed to set the positions of the vertices');return;}// Specify the color for clearing <canvas>gl.clearColor(0, 0, 0, 1);// Clear <canvas>gl.clear(gl.COLOR_BUFFER_BIT);// 一次绘制了三个顶点,顶点着色器执行了3次通过存储在缓冲区中的顶点坐标数据被依次传给attribute变量,注意,每次执行顶点着色器,a_Position 的z和w分量值都会自动被设为0.0或1.0,因为a_Position需要4个分量(vec4),而你只提供了两个gl.drawArrays(gl.POINTS, 0, n);
}function initVertexBuffers(gl) {var vertices = new Float32Array([0.0, 0.5,   -0.5, -0.5,   0.5, -0.5]);var n = 3; // The number of vertices// Create a buffer objectvar vertexBuffer = gl.createBuffer()  if (!vertexBuffer) {console.log('Failed to create the buffer object');return -1;}// Bind the buffer object to targetgl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);// 将第二个参数 vertices 中的数据写入了绑定到第一个参数 gl.ARRAY_BUFFER 上的缓冲区对象   p73gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);var a_Position = gl.getAttribLocation(gl.program, 'a_Position');if (a_Position < 0) {console.log('Failed to get the storage location of a_Position');return -1;}// 将绑定到 gl.ARRAY_BUFFER 的缓冲区对象分配给由 第一个参数 a_position 指定的 attribute变量gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);// 激活开启 attribute 变量,使缓冲区对 attitude 变量的分配生效,为了使顶点着色器能够访问缓冲区内的数据gl.enableVertexAttribArray(a_Position);return n;
}

函数initVertexBuffers()的任务是创建顶点缓冲区对象,并将多个顶点的数据保存在缓冲区中,然后将缓冲区传给顶点着色器。

函数的返回值是待绘制顶点的数量,保存在变量n中。注意,如果函数内发生错误,返回的是负值。

示例程序仅调用了一次gl.drawArrays()函数就完成了绘图操作,这与 WebGL 变量uniform、gl.getUniformLocation、gl.uniform4f及其同族函数相关_山楂树の的博客-CSDN博客 不同。调用gl.drawArrays()时,传入的第3个参数是n,而不是1(上面链接传入了1)。 

因为我们在initvertexBuffer()函数中利用缓冲区对象向顶点着色器传输了多个(3个)顶点的数据,所以还需要通过第3个参数告诉gl.drawArrays()函数需要绘制多少个顶点。WebGL系统并不知道缓冲区中有多少个顶点的数据(即使它知道也不能确定是否要全部画出),所以我们应该显式地告诉它要绘制多少个顶点。

使用缓冲区对象 

前面说过,缓冲区对象是WebGL系统中的一块存储区,你可以在缓冲区对象中保存想要绘制的所有顶点的数据,如下图所示。先创建一个缓冲区,然后向其中写入顶点数据,你就能一次性地向顶点着色器中传入多个顶点的attribute变量的数据。

在示例程序中,向缓冲区对象写入的数据(顶点坐标)是一种特殊的JavaScript数组(Float32Array),如下所示。

使用缓冲区对象向顶点着色器传入多个顶点的数据,需要遵循以下五个步骤。

1.创建缓冲区对象(gl.createBuffer())。

2.绑定缓冲区对象(gl.bindBuffer())。

3.将数据写入缓冲区对象(gl.bufferData())。

4.将缓冲区对象分配给一个attribute变量(gl.vertexAttribPointer())。

5.开启attribute变量(gl.enableVertexAttribArray())。

解析了上述五个步骤

创建缓冲区对象(gl.createBuffer()) 

显然,在使用缓冲区对象之前,你必须创建它。这是第1步:

使用WebGL时,你需要调用gl.createBuffer()方法来创建缓冲区对象。下图示意了该方法执行前后WebGL系统的中间状态,上面一张图是执行前的状态,下面一张图是执行后的状态。执行该方法的结果就是,WebGL系统中多了一个新创建出来的缓冲区对象。

gl.createBuffer()的函数规范

gl.deleteBuffer ()

相应地,gl.deleteBuffer(buffer)函数可用来删除被gl.createBuffer()创建出来的缓冲区对象。 

绑定缓冲区(gl.bindBuffer()) 

创建缓冲区之后的第2个步骤就是将缓冲区对象绑定到WebGL系统中已经存在的“目标”(target)上。这个“目标”表示缓冲区对象的用途(在这里,就是向顶点着色器提供传给attribute变量的数据),这样WebGL才能够正确处理其中的内容。

gl.bindBuffer()的函数规范

在示例程序中,我们将缓冲区对象绑定到了gl.ARRAY_BUFFER目标上,缓冲区对象中存储着的关于顶点的数据(顶点的位置坐标)。在执行完毕后,WebGL系统内部状态发生了改变,如下图所示

 接下来,我们就可以向缓冲区对象中写入数据了。

向缓冲区对象中写入数据(gl.bufferData())

第3步是,开辟空间并向缓冲区中写入数据。我们使用gl.bufferData()方法来完成这一步:

该方法的效果是,将第2个参数vertices中的数据写入了绑定到第1个参数gl.ARRAY_BUFFER上的缓冲区对象。我们不能直接向缓冲区写入数据,而只能向“目标”写入数据,所以要向缓冲区写数据,必须先绑定。该方法执行之后,WebGL系统的内部状态如下图所示。 

 从上图中可以看到,定义在JavaScript程序中的数据被写入了绑定在gl.ARRAY_BUFFER上的缓冲区对象。下面是对gl.bufferData()的规范。

gl.bufferData()的规范

 现在我们来看看gl.bufferData()方法向缓冲区中传入了什么数据。该方法使用了一个特殊的数组vertices将数据传给顶点着色器。我们使用new运算符,并以<第一个顶点的x坐标和y坐标><第二个顶点的x坐标和y坐标>,等等的形式创建这个数组:

如你所见,我们使用了Float32Array对象,而不是JavaScript中更常见的Array对象。这是因为,JavaScript中通用的数组Array是一种通用的类型,既可以在里面存储数字也可以存储字符串,而并没有对“大量元素都是同一种类型”这种情况(比如vertices)进行优化。为了解决这个问题,WebGL引入了类型化数组,Float32Array就是其中之一。

类型化数组 

为了绘制三维图形,WebGL通常需要同时处理大量相同类型的数据,例如顶点的坐标和颜色数据。为了优化性能,WebGL为每种基本数据类型引入了一种特殊的数组(类型化数组)。浏览器事先知道数组中的数据类型,所以处理起来也更加有效率。

例子中的Float32Array就是一种类型化数组,通常用来存储顶点的坐标或颜色数据。WebGL中的很多操作都要用到类型化数组,比如gl.bufferData()中的第2个参数

与JavaScript中的Array数组相似,类型化数组也有一系列方法和属性(包括一个常量属性),如下所示。注意,与普通的Array数组不同,类型化数组不支持push()和pop()方法。

和普通的数组一样,类型化数组可以通过new运算符调用构造函数并传入数据而被创造出来。比如,为了创建Float32Array类型的顶点数据,你可以向构造函数中传入普通数组[0.0,0.5,-0.5,-0.5,0.5,-0.5],这个数组表示一些顶点的数据。注意,创建类型化数组的唯一方法就是使用new运算符,不能使用[]运算符(那样创建的就是普通数组)。 

此外,你也可以通过指定数组元素的个数来创建一个空的类型化数组,例如:

到目前为止,你就完成了建立和使用缓冲区的前三个步骤(即在WebGL系统中创建缓冲区,绑定缓冲区对象到目标,向缓冲区对象中写入数据)。我们来看看在接下来的两步中,WebGL是如何真正使用缓冲区来进行绘图的。 

将缓冲区对象分配给attribute变量(gl.vertexAttribPointer())

你可以使用gl.vertexAttrib[1234]f系列函数为attribute变量分配值。但是,这些方法一次只能向attribute变量分配(传输)一个值。而现在,你需要将整个数组中的所有值——这里是顶点数据——一次性地分配给一个attribute变量。

gl.vertexAttribPointer()方法解决了这个问题,它可以将整个缓冲区对象(实际上是缓冲区对象的引用或指针)分配给attribute变量。示例程序将缓冲区对象分配给attribute变量a_Position。

 gl.vertexAttribPointer()的规范如下

 执行完第4步后,我们就将整个缓冲区对象分配给了attribute变量,为WebGL绘图进行的准备工作(即向location处的attribute变量传入缓冲区)就差最后一步了:进行最后的“开启”,使这次分配真正生效,如图所示:

 第5步,也是最后一步,就是开启(激活)attribute变量,使缓冲区对attribute变量的分配生效。

开启attribute变量(gl.enableVertexAttribArray())

为了使顶点着色器能够访问缓冲区内的数据,我们需要使用gl.enableVertex AttribArray()方法来开启attribute变量。

注意,虽然函数的名称似乎表示该函数是用来处理“顶点数组”的,但实际上它处理的对象是缓冲区。这是由于历史原因(从OpenGL中继承)造成的。

当你执行gl.enableVertexAttribArray()并传入一个已经分配好缓冲区的attribue变量后,我们就开启了该变量,也就是说,缓冲区对象和attribute变量之间的连接就真正建立起来了,如下图所示。

 gl.disableVertexAttribArray()

同样,你可以使用gl.disableVertexAttribArray()来关闭分配。 

终于,万事俱备了!现在,你只需要让顶点着色器运行起来,它会自动将缓冲区中的顶点画出来。之前你使用gl.drawArrays()方法绘制了一个点,现在你要画多个点,所用的仍然是gl.drawArrays()方法,但是用的是方法中的第2个和第3个参数。 

注意,开启attribute变量后,你就不能再用gl.vertexAttrib[1234]f()向它传数据了,除非你显式地关闭该attribute变量。实际上,你无法(也不应该)同时使用这两个函数。

gl.drawArrays()的第2个和第3个参数

在对gl.drawArrays()作进一步详细说明之前,我们再看一下这个方法的规范。下表是规范的参数部分。

这个示例程序如下调用这个方法:

WebGL 变量uniform、gl.getUniformLocation、gl.uniform4f及其同族函数相关_山楂树の的博客-CSDN博客此章由于我们仍然在绘制单个的点,第1个参数mode仍然是gl.POINTS;设置第2个参数为0,表示从缓冲区中的第1个坐标开始画起;设置第3个参数为3,表示我们准备绘制3个点。

当程序运行到该代码时,实际上顶点着色器执行了count(3)次,我们通过存储在缓冲区中的顶点坐标数据被依次传给attribute变量,如下图所示:

注意,每次执行顶点着色器,a_Position的z和w分量值都会自动被设为0.0或1.0,因为a_Position需要4个分量(vec4),而你只提供了两个。

gl.vertexAttribPointer()的第2个参数size被设为2。之前说过,这个参数表示缓冲区中每个顶点有几个分量值,在缓冲区中你只提供x坐标和y坐标,所以你将它设为2。

在绘出所有点后,颜色缓冲区中的内容(3个红点,如图3.2所示)就会自动显示在浏览器上,其过程如图底部所示。 

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

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

相关文章

C# winform加载yolov8模型测试(附例程)

第一步&#xff1a;在NuGet中下载Yolov8.Net 第二步&#xff1a;引用 using Yolov8Net; 第三步&#xff1a;加载模型 private IPredictor yolov8 YoloV8Predictor.Create("D:\\0MyWork\\Learn\\vs2022\\yolov_onnx\\best.onnx", mylabel); 第四步&#xff1a;图…

【OpenCV • c++】图像对比度调整 | 图像亮度调整

&#x1f680; 个人简介&#xff1a;CSDN「博客新星」TOP 10 &#xff0c; C/C 领域新星创作者&#x1f49f; 作 者&#xff1a;锡兰_CC ❣️&#x1f4dd; 专 栏&#xff1a;【OpenCV • c】计算机视觉&#x1f308; 若有帮助&#xff0c;还请关注➕点赞➕收藏&#xff…

window系统中如何判断是物理机还是虚拟机及VMPROTECT无法检测云主机

为什么要判断物理机&#xff0c;因为授权不能对虚拟机安装后的软件进行授权。虚拟机可以复制可以克隆&#xff0c;无法作为一个不可复制ID来使用。 总结了如何判断物理机&#xff1a; 1. 用systeminfo的系统型号。&#xff08;注&#xff0c;有资料是看处理器和bios。但是我这…

一步一步实验,讲解python中模块和包的使用

背景 为什么要提出这个问题&#xff1f; 在一个项目中&#xff0c;每一个python文件打开后&#xff0c;都会看到依赖了其他的一些包、模块等&#xff1b;概念混乱&#xff0c;魔改目标不清晰 为什么要修改&#xff1f; 如果需要将某开源包进行自定义处理&#xff0c;不再使…

Python 包管理(pip、conda)基本使用指南

Python 包管理 概述 介绍 Python 有丰富的开源的第三方库和包&#xff0c;可以帮助完成各种任务&#xff0c;扩展 Python 的功能&#xff0c;例如 NumPy 用于科学计算&#xff0c;Pandas 用于数据处理&#xff0c;Matplotlib 用于绘图等。在开始编写 Pytlhon 程序之前&#…

数据隐私与安全在大数据时代的挑战与应对

文章目录 数据隐私的挑战数据安全的挑战应对策略和方法1. 合规和监管2. 加密技术3. 匿名化和脱敏4. 安全意识培训5. 隐私保护技术 结论 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&…

【算法与数据结构】513、LeetCode找树左下角的值

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;这道题用层序遍历来做比较简单&#xff0c;最底层最左边节点就是层序遍历当中最底层元素容器的第一个值…

vue 简单实验 自定义组件 独立模块

1.概要 2.代码 2.1 const Counter {data() {return {counter: 0}},template:<div>Counter: {{ counter }}</div> }export default Counter 2.2 import Counter from ./t2.jsconst app Vue.createApp({components: {component-a: Counter} })app.mount(#count…

浅析 GlusterFS 与 JuiceFS 的架构异同

在进行分布式文件存储解决方案的选型时&#xff0c;GlusterFS 无疑是一个不可忽视的考虑对象。作为一款开源的软件定义分布式存储解决方案&#xff0c;GlusterFS 能够在单个集群中支持高达 PiB 级别的数据存储。自从首次发布以来&#xff0c;已经有超过十年的发展历程。目前&am…

不使用ip和port如何进行网络通讯(raw socket应用例子)

主要应用方向是上位机和嵌软(如stm32单片机)通讯&#xff0c;不在单片机中嵌入web server&#xff0c;即mac层通讯。 一、下面先了解网络数据包组成。 常见数据包的包头长度: EtherHeader Length: 14 BytesTCP Header Length : 20 BytesUDP Header Length : 8 BytesIP Heade…

Spring@Scheduled定时任务接入XXL-JOB的一种方案(基于SC Gateway)

背景 目前在职的公司&#xff0c;维护着Spring Cloud分布式微服务项目有25个。其中有10个左右微服务都写有定时任务逻辑&#xff0c;采用Spring Scheduled这种方式。 Spring Scheduled定时任务的缺点&#xff1a; 不支持集群&#xff1a;为避免重复执行&#xff0c;需引入分…

【VMware】CentOS 设置静态IP(Windows 宿主机)

文章目录 1. 更改网络适配器设置2. 配置虚拟网络编辑器3. 修改 CentOS 网络配置文件4. ping 测试结果 宿主机&#xff1a;Win11 22H2 虚拟机&#xff1a;CentOS-Stream-9-20230612.0 (Minimal) 1. 更改网络适配器设置 Win R&#xff1a;control 打开控制面板 依次点击&#x…

【应用层】网络基础 -- HTTPS协议

HTTPS 协议原理加密为什么要加密常见的加密方式对称加密非对称加密 数据摘要&&数据指纹 HTTPS 的工作过程探究方案1-只使用对称加密方案2-只使用非对称加密方案3-双方都使用非对称加密方案4-非对称加密对称加密中间人攻击-针对上面的场景 CA认证理解数据签名方案5-非对…

15-模型 - 一对多 多对多

一对多&#xff1a; 1. 在多的表里定义外键 db.ForeignKey(主键) 2. 增加字段 db.relationship 建立联系 ("关联表类名","反向引用名") from ext import db# 一 class User(db.Model):id db.Column(db.Integer, primary_keyTrue, autoincrementTrue)us…

Dart PowerTCP Emulation for .NET Crack

Dart PowerTCP Emulation for .NET Crack .NET CF上的PowerTCP Emulation为手持设备提供了高级的Internet通信组件。这些功能允许同步操作&#xff0c;这样可以消耗更少的资源&#xff0c;提供更大的灵活性&#xff0c;并生成易于维护的软件。带有.NET的PowerTCP仿真包括VT52、…

gpt-3.5-turbo微调图形界面;Hugging Face完成2.35亿美元融资

&#x1f989; AI新闻 &#x1f680; 人工智能初创公司Hugging Face完成2.35亿美元融资&#xff0c;估值达到45亿美元 摘要&#xff1a;总部位于纽约的人工智能初创公司Hugging Face完成了一轮2.35亿美元的融资&#xff0c;估值达到45亿美元。本轮融资的投资者包括谷歌、亚马…

几个nlp的小任务(多选问答)

@TOC 安装库 多选问答介绍 定义参数、导入加载函数 缓存数据集 随机选择一些数据展示 进行数据预处理部分(tokenizer) 调用t

Android全面屏下,默认不会全屏显示,屏幕底部会留黑问题

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂&#xff0c;风趣幽默"&#xff0c;感觉非常有意思,忍不住分享一下给大家。 &#x1f449;点击跳转到教程 公司以前的老项目&#xff0c;便出现了这种情况&#xff0c;网上搜索了各种资料&#xf…

Web Components详解-Custom Elements

目录 引言 演变过程 概述 使用方式 创建标签 定义标签 使用标签 获取标签 异步定义标签 升级标签 完整案例 结语 相关代码 参考文章 引言 随着项目体量的增大&#xff0c;组件化和模块化的优势也愈发明显了&#xff0c;构建可重复使用、独立、可互操作的组件变得…

【Java基础】Java注解与反射

文章目录 ⭐️写在前面的话⭐️1、什么是注解&#xff1f;注解的分类常用的Java注解 2、元注解TargetRetentionDocumentedInherited 3、自定义注解Override注解的基本格式 4、什么是反射&#xff1f;什么时候需要用到反射&#xff1f;反射的应用场合 5、反射的原理6、反射机制的…