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

news2025/5/16 9:26:09

目录

使用缓冲区对象 

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

创建缓冲区对象(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> element
  var canvas = document.getElementById('webgl');

  // Get the rendering context for WebGL
  var gl = getWebGLContext(canvas);
  if (!gl) {
    console.log('Failed to get the rendering context for WebGL');
    return;
  }

  // Initialize shaders
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    console.log('Failed to intialize shaders.');
    return;
  }

  // Write the positions of vertices to a vertex shader
  var 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 object
  var vertexBuffer = gl.createBuffer()  
  if (!vertexBuffer) {
    console.log('Failed to create the buffer object');
    return -1;
  }

  // Bind the buffer object to target
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  // 将第二个参数 vertices 中的数据写入了绑定到第一个参数 gl.ARRAY_BUFFER 上的缓冲区对象   p73
  gl.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.coloradmin.cn/o/924422.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[管理与领导-46]:IT基层管理者 - 8项核心技能 - 1 - 目标管理

目录 前言&#xff1a; 一、什么是目标管理 二、管理目标的维度 2.1 个人目标维度 2.2 绩效评估的维度 三、目标的分解 四、目标制定的SMART原则 五、目标管理的误解 六、目标管理的注意事项 七、目标无法实现的因素 前言&#xff1a; 管理者存在的价值就是制定目标…

React Diff算法

文章目录 React Diff算法一、它的作用是什么&#xff1f;二、React的Diff算法1.了解一下什么是调和&#xff1f;2.react的diff算法3.React Diff的三大策略4.tree diff&#xff1a;1、如果DOM节点出现了跨层级操作&#xff0c;Diff会怎么办? 5. component diff&#xff1a;6. e…

maven工程的目录结构

https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html maven工程的目录结构&#xff1a; 在maven工程的根目录下面&#xff0c;是pom.xml文件。此外&#xff0c;还有README.txt、LICENSE.txt等文本文件&#xff0c;便于用户能够…

Jenkins 详细安装流程及填坑记录「图文」

目录 一、前言 二、环境准备 三、安装步骤 1、安装jdk 2、安装jenkins 3、配置修改 4、jenkins启动 四、登录jenkins 一、前言 省流&#xff1a;本文仅记录Jenkins详细安装过程&#xff0c;以及安装过程中经常遇到的问题。 二、环境准备 Linux系统&#xff1a;CentOS…

spdlog输出日志等级控制由set_level运行时和#define SPDLOG_ACTIVE_LEVEL编译时共同控制

spdlog输出日志等级控制由set_level运行时和#define SPDLOG_ACTIVE_LEVEL编译时共同控制 common.h中 spdlog.h中 控制log活跃级别是INFO

网络安全工具和资源推荐: 介绍网络安全领域中常用的工具、框架、资源和学习资料

章节1: 前言 随着数字化时代的不断深入&#xff0c;网络安全的重要性愈发凸显。在这个信息爆炸的时代&#xff0c;我们必须保护个人隐私、敏感数据以及网络基础设施免受各种威胁。本文将为您介绍一些网络安全领域中常用的工具、框架、资源和学习资料&#xff0c;帮助您更好地入…

CSS中的vertical-align属性

vertical-align 1.CSS属性 - vertical-align 2.深入理解vertical-align – line boxes This property affects the vertical positioning inside a line box of the boxes generated by an inline-levelelement. 官方文档的翻译&#xff1a;vertical-align会影响 行内块级元素…

VSCode 如何解决 scanf 的输入问题——Code is already running!

文章如何使用 VSCode 软件运行C代码中已经介绍了如何在 VSCode 软件中运行C代码&#xff0c;但最近在使用 scanf 想从键盘输入时&#xff0c;运行代码后显示“Code is already running!”&#xff0c;如下图所示&#xff0c;在输出窗口是无法通过键盘输入的。 解决办法如下&am…

vue若依导出word文件,简单的实现

首先前端导包,注意exportDocx的导包位置要修改成你自己的 import {exportDocx} from /utils/docUtil/docutil.js; import {addDays} from date-fns; import {listGongyi} from "/api/system/detail";然后新建一个测试按钮 <el-col :span"1.5"><…

C++中<iostream> 的cin >> str 和<string>的getline(cin, str) 用来读取用户输入的两种不同方式的不同点

C中<iostream> 的cin >> str 和<string>的getline(cin, str) 用来读取用户输入的两种不同方式的不同点 getline()函数语法如下【https://cplusplus.com/reference/string/string/getline/】&#xff1a; istream& getline (istream& is, string&am…

LLaMA中ROPE位置编码实现源码解析

1、Attention中q&#xff0c;经下式&#xff0c;生成新的q。m为句长length&#xff0c;d为embedding_dim/head θ i 1 1000 0 2 i d \theta_i\frac{1}{10000^\frac{2i}{d}} θi​10000d2i​1​ 2、LLaMA中RoPE源码 import torchdef precompute_freqs_cis(dim: int, end: i…

自动化测试之Selenium

自动化测试Selenium介绍环境搭建如何操作浏览器定位元素css类选择器定位元素xpath定位元素css选择语法xpath选择语法 常用操作添加等待打印信息浏览器更多操作键盘事件鼠标事件特殊场景只选复选框iframe标签下拉框处理弹窗显示上传文件 关闭浏览器切换窗口截图 自动化测试 自动…

ubuntu20.04 直接安装vpp23.06 测试双 VPP Tunnel Ike2

环境信息&#xff1a;VMware Workstation 17 Pro ubuntu20.04 (清华源) ubuntu 源点进去选&#xff1a;ubuntu-22.04.3-desktop-amd64.iso 如果之前装过VPP&#xff0c;用以下命令确定是否卸载干净&#xff1a; dpkg -l | grep vpp dpkg -l | grep DPDK 卸载&#xff1a; …

ChatGPT + Flutter快速开发多端聊天机器人App

下载地址&#xff1a;ChatGPT Flutter快速开发多端聊天机器人App 下载地址&#xff1a;ChatGPT Flutter快速开发多端聊天机器人App

SpringCloud学习笔记(五)_Consul注册中心

本章使用的Consul版本是 1.7.2 项目架构图如下&#xff1a; 搭建服务提供者 1、新建一个maven项目&#xff08;test-springcloud-provider-payment8006&#xff09; 结构如下&#xff1a; 2、引入依赖&#xff0c;编辑pom文件 1 <!-- spring-cloud 整合 consul --> 2…

MyBatis分页与特殊字符处理

文章目录 一、分页1.1 分页插件PageHelper1.2 使用1.2.1 导入pom依赖1.2.2 Mybatis.cfg.xml配置拦截器1.2.3. 配置 Mapper.xml1.2.4 测试 二、特殊字符处理2.1 使用CDATA区段2.2 使用实体引用 一、分页 1.1 分页插件PageHelper PageHelper 是 Mybatis 的一个插件。官网 Page…

SOLIDWORKS提高装配效率的方法:阵列特征驱动阵列

相信SOLIDWORKS用户很喜欢SOLIDWORKS阵列命令&#xff0c;因为阵列可以提高设计效率&#xff0c;减少错误&#xff0c;修改也很方便&#xff0c;但是大家一般在零件里用阵列来阵列特征&#xff0c;在装配体里用阵列来阵列零件&#xff0c;那有没有办法用零件中的阵列特征来驱动…

开源与专有软件:比较与对比

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

173935-46-1/Fmoc-L-Ser(Ac3-L-Fucα)-OH的保存建议

Fmoc-L-Ser((Ac)3-α-Fuc)-OH是一种支链N-连接寡糖的碱性单糖片段&#xff0c;该片段在糖蛋白中发现。该片段由α-L-fucose和N-acetylated α-L-fucose通过乙酰基连接而成&#xff0c;并被包含在一个Fmoc-L-serine分子中。西安凯新生物科技有限公司的这种支链寡糖结构在生物体内…

linux安装JDK及hadoop运行环境搭建

1.linux中安装jdk &#xff08;1&#xff09;下载JDK至opt/install目录下&#xff0c;opt下创建目录soft&#xff0c;并解压至当前目录 tar xvf ./jdk-8u321-linux-x64.tar.gz -C /opt/soft/ &#xff08;2&#xff09;改名 &#xff08;3&#xff09;配置环境变量&#xf…