WebGL交错缓冲区【Interleaved Buffer】

news2024/9/29 13:19:58

在这里插入图片描述

推荐:用 NSDT设计器 快速搭建可编程3D场景。

昨天我在 WebGL 沙箱项目的评论中收到 Jon 的一个问题:

嗨, 布兰登,以你的演示为起点,我尝试显示一个金字塔,但到目前为止我只能看到它的四个面之一。 如果我使用 gl.LINES 而不是 gl.TRIANGLES,那么我只能看到一个三角形。 我对将纹理坐标混合到 vertArray 中的方式也有点困惑。 您能解释一下这些坐标是如何在着色器中排序的吗?

老实说,我不知道我是否是世界上解释这一点的最佳人选,但我会尝试一下,因为关于此的特定于 WebGL 的信息似乎非常少。 为了简单起见,大多数教程更喜欢将数组分开,但这对于性能来说并不是最佳的。 这些概念的工作方式与 OpenGL 几乎相同,但很高兴看到它们在你正在使用的环境中使用。 它需要对通常不适用于 Javascript 的主题有一些基本的了解,例如计算字节,但是一旦掌握了它的窍门,它就不会太难。

首先要注意的是,WebGL API 不知道任何有关顶点、纹理坐标、法线或其他类似内容的信息。 它所知道的是数字列表。 它知道如何在图形内存中保存数字列表,并且知道如何告诉着色器开始渲染这些数字,但实际上仅此而已。 你是通过着色器代码和 gl.vertexAttribPointer() 告诉系统数字含义的人。

var values = [
    -1.0, -1.0, 0.0, // Vertex 1
    0.0, 1.0, 0.0, // Vertex 2
    1.0, -1.0, 0.0 // Vertex 3
];

你们以前可能都见过,对吧? 这次要注意的是,除了注释和我们构建换行符的方式之外,没有任何东西可以分隔顶点。 该行可以准确地写成这样:

var values = [ -1.0, -1.0, 0.0, 0.0, 1.0, 0.0, 1.0, -1.0, 0.0 ];

当然,现在对于我们人类来说阅读起来要困难得多,但是对于计算机来说却是完全一样的。 无论哪种情况,我们都将数据推送到顶点缓冲区中,如下所示:

var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(values), gl.STATIC_DRAW);

请注意 Float32Array 位,我们稍后会讨论它。 不过,最终结果是 vertBuffer 现在指向显卡上某处的数字数组。 那么计算机如何知道如何解释这些看似随机的值呢? gl.vertexAttribPointer!

在渲染之前,我们必须调用如下代码:

gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.vertexAttribPointer(shader_attrib_position, 3, gl.FLOAT, false, 12, 0);

这告诉系统它需要知道的关于如何读取顶点的一切(更准确地说,如何告诉着色器关于顶点的信息…)。 这里我们要注意 4 点: “3”告诉 WebGL 每个点由多少个值组成。 由于位置是由 x、y 和 z 坐标定义的,因此我们将其指定为“3”。 如果你在 WebGL 代码中进行 2D 渲染,可以轻松地在此处传递 2,并且可以根据情况给它 1 到 4 之间的任何值。 接下来,我们告诉它我们正在传递什么样的数字。 在本例中它们是浮点型,因此我们传入 gl.FLOAT。 还有 gl.BYTE 和 gl.SHORT (以及其他的,但我们不要把事情搞得太复杂)。 这告诉 WebGL 两件非常重要的事情:首先,如何将值暴露给着色器,其次每个值有多大。 现在这很重要,对于那些不熟悉字节打包的人来说,让我们快速回顾一下:

  • gl.BYTE = 1 字节(显然!),可以保存从 -127 到 127 的值
  • gl.SHORT = 2字节,取值范围-32767到32767
  • gl.FLOAT = 4字节,一大堆的值范围。

在大多数情况下,除非你有一些非常具体的需求,否则你将希望坚持使用 gl.FLOAT。 这里使用 Float 也与我们在创建缓冲区时使用 Float32Array 一致。 如果我们要使用其他类型之一,你将需要使用不同的数组类型创建缓冲区。

重要的是,根据上面的 vertexAttribPointer 调用,我们传递了 3 个浮点数。 3 * 4 = 12,所以每个位置占用12个字节。 而且,嘿! 我们在那次通话中也有 12! 这称为Stride(步幅),它告诉系统每个顶点的起点相距多远(以字节为单位)。 如果我们的顶点包含的信息不仅仅是位置,这非常有用。 假设由于某种原因我们的顶点被这样定义:

var values = [
    -1.0, -1.0, 0.0, 999.0, // Vertex 1
    0.0, 1.0, 0.0, 999.0, // Vertex 2
    1.0, -1.0, 0.0, 999.0 // Vertex 3
];

这些 999 可能代表顶点数据的其他部分,但它们与位置没有任何关系。 如果我们继续使用上面相同的 vertexAttribPointer 值,我们的顶点将如下所示:

[-1, -1, 0] [999, 0, 1] [0, 999, 1] [-1, 0, 999]

我们可以轻松地“忽略”第四个值,从而返回到我们想要的顶点位置,如下所示:

gl.vertexAttribPointer(shader_attrib_position, 3, gl.FLOAT, false, 16, 0);

请注意,我们仍然指示 WebGL 读取 3 个浮点,但步幅已更改为 16 以考虑第 4 个不需要的浮点(4 个值 * 每个值 4 个字节 = 16 个字节)。

最后,我们在末尾添加 0,它告诉 WebGL 开始读取数组中的值的深度。 这也是以字节为单位给出的,在本例中为 0,因为我们想从第一个值开始。 如果我们想从第二个数字开始,我们会这样做:

gl.vertexAttribPointer(shader_attrib_position, 3, gl.FLOAT, false, 16, 4);

现在我们的顶点位置将如下所示:

[-1, 0, 999] [1, 0, 999] [-1, 0, 999]

这对于将多个网格打包到单个缓冲区中非常有用,因为我们可以在任何点开始渲染,但它对于交错(Interleaved)数据也很有用。 “交错”意味着将多种类型的数据打包到一个数组中,例如位置和纹理坐标数据。 大多数教程会将它们显示为单独的数组,如下所示:

var pos = [1, 1, 1, 2, 2, 2, 3, 3, 3]; // (x, y, z), (x, y, z)...
var tex = [0, 1, 0, 1, 0, 1]; // (s, t), (s, t)...

但将它们全部作为一个数组通常会更高效、更容易,如下所示:

var verts = [
    1, 1, 1, 0, 1, // (x, y, z), (s, t)...
    2, 2, 2, 0, 1,
    3, 3, 3, 0, 1,
];

var vertBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW);

当我们为此数组设置 vertexPointers 时,我们需要对 vertexAttribPointer 进行两次调用。

gl.bindBuffer(gl.ARRAY_BUFFER, vertBuffer);
gl.vertexAttribPointer(shader_attrib_position, 3, gl.FLOAT, false, 20, 0);
gl.vertexAttribPointer(shader_attrib_texcoord, 2, gl.FLOAT, false, 20, 12);

让我们看看一下这意味着什么:第一个属性(顶点位置)由 3 个浮点组成,从字节 0 开始,间隔 20 个字节。第二个属性(顶点UV)由 2 个浮点组成,间隔 20 个字节,从字节开始 12 个(或 3 个浮点数)。 就是这样! 一个缓冲区中有两种属性类型! 我们可以根据需要(在合理范围内)对尽可能多的属性执行此操作。 包含位置、纹理坐标、法线、副法线和顶点权重的单个缓冲区并不罕见!

我真诚地希望这一切都是有意义的,而不仅仅是一堆图形胡言乱语。 我在这里根本没有讨论着色器方面的内容,但其他教程对此进行了很好的介绍。


原文链接:WebGL交错缓冲区 — BimAnt

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

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

相关文章

零信任:基于Apisix构建认证网关

背景 零信任一直是我们未来主攻的一个方向,全球加速,SD-WAN组网都是一些非常成熟的产品,全球加速是我们所有产品的底座,SD-WAN解决的是多个网络打通的问题,而零信任则主打应用访问。 关于零信任,我们已经…

使用Wireshark 找出 TCP 吞吐瓶颈

Debug 网络质量的时候,我们一般会关注两个因素:延迟和吞吐量(带宽)。延迟比较好验证,Ping 一下或者 mtr[1] 一下就能看出来。这篇文章分享一个 debug 吞吐量的办法。 看重吞吐量的场景一般是所谓的长肥管道(Long Fat …

包装类~~

就是8种基本数据类型对应的引用类型 2:为什么提供包装类 Java为了实现一切皆对象,对8种基本类型提供了对应的引用类型后面的集合和泛型其实也只能支持包装类型,不支持基本数据类型。 自动装箱:基本类型的数据和变量可以直接赋值…

ResNet:深度学习中的重要里程碑

目录 导言: 1. 应用 2. 结构介绍 3. 代码案例 导言: 深度学习的迅速发展在图像识别、语音处理和自然语言处理等领域取得了巨大的突破。然而,深度神经网络在训练过程中遇到了梯度消失和梯度爆炸等问题,限制了模型的性能和训练…

【springboot整合】RabbitMQ

Spring与消息 概述 消息中间件的主要作用:提高系统异步通信、扩展解耦能力消息服务的两个重要概念:消息代理(message broker)和目的地(destination)消息队列的两种形式的目的地:队列&#xff…

CentOS ping命令:name or service not known

1.虚拟机网络连接设置为“NAT模式”,且NAT设置 导航栏“编辑”->“虚拟网络编辑器” ->NAT模式->NAT设置 2.网络配置文件 vi /etc/sysconfig/network-scripts/ifcfg-ens33注:IPADDR和NAT设置里面的IP最后一位要不同 3.DNS设置 vi /etc/reso…

自然语言处理: 第四章Seq2Seq

自然语言处理: 第四章Seq2Seq 理论基础 开始之前,首先提出一个问题,电脑是怎么识别人类的命令的,首先人们通过输入代码(编码) ,带入输入给计算机然后再经过处理(解码)得到最终的命令。所以可以看到这其实是一个编码 解码的过程…

lesson10 Zigbee组播通信原理

目录 Zigbee组播通信原理 实验原理 实验过程 实验设计 发送模块 接收模块 实验现象 组播通信总结 Zigbee组播通信原理 实验原理 1、组播通信:在Zigbee无线网络里,模块可以进行分组来标记。发送的模块如果发送的组号和网络里标记模块的组号相对应…

被偷走的文件

也是一道流量解析题目 既然是文件被盗走,可能跟文件传输协议ftp有关,过滤一下ftp 跟踪流后发现有一个flag.rar文件,是内嵌的吗? 那说明应该也可以利用binwal分出来吧 分离出来的rar文件需要密码,拿去爆破一下&#xff…

03_ES6

ES6(在js中进行操作) 使用var声明变量的弊端 var 声明的变量有预解析,造成逻辑混乱,可以先使⽤,后声明,undefined var 可以重复定义同⼀个变量,第二个会修改变量的值 var ⽤在 for 循环条件中,造成 for 循环的污染的…

【八股】【C++】(三)STL

这里写目录标题 STL定义一、容器概念(1)vector如何避免扩容导致效率低为什么是1.5或2扩容怎么找某vector或者list的倒数第二个元素vector如何释放空间[] 下标检查 (2)deque(3)stack(4&#xff0…

如何通过Python下载GSMap数据集(解决.dat无法打开的问题)?

目录 01 前言 02 GSMap-MVK的存储方式和数据集介绍 03 代码实现 01 前言 这么晚了我还是希望将这篇博客写一下,记录生活。 我所下载的数据集为GSMap-MVK数据集,延迟大概2.5月左右我记得.边下载我就想着先处理着吧。 例如,其中一个文件如…

HOT38-翻转二叉树

leetcode原题链接:翻转二叉树 题目描述 给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。 示例 1: 输入:root [4,2,7,1,3,6,9] 输出:[4,7,2,9,6,3,1]示例 2: 输入:…

华为OD机试真题B卷 Python 实现【小朋友排队】,附详细解题思路

目录 一、题目描述二、输入描述三、输出描述四、解题思路五、Python算法源码六、效果展示1、输入2、输出 一、题目描述 小明今年升学到了小学1年级,来到新班级后,发现其他小朋友身高参差不齐,然后就想基于每个小朋友和自己的身高差&#xff…

postgresql内核分析 spinlock与lwlock原理与实现机制

​专栏内容: postgresql内核源码分析 手写数据库toadb 并发编程 个人主页:我的主页 座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物. 概述 在postgresql 中,有大量的并发同步&#xff0…

浅谈容器技术之Podman

1.Podman容器简介 Podman(Pod Manager)是一个由RedHat公司推出的容器管理工具,它的定位就是 Docker 的替代品,在使用上与Docker 的体验类似。Podman源于CRI-O项目,可以直接访问 OCI 的实现(如 runC&#x…

【Unity实战】复刻实现经典2d平台跳跃游戏《蔚蓝 Celeste》(附工程源码)

文章目录 前言蔚蓝欣赏实现1. 移动2. 跳跃3. 滑动4. 爬墙5. 蹬墙跳6. 移动优化7. 粒子效果8. 角色环境素材9. 编写角色动画控制10. Tilemap绘制地图环境11. 环境粒子特效12. 冲锋残影效果13. 屏幕震动效果14. 涟漪效果 最终效果工程源码参考完结 前言 《蔚蓝》是一款备受好评的…

信号与系统复习笔记——通讯系统

信号与系统复习笔记——通讯系统 复指数与正弦幅度调制 y ( t ) x ( t ) c ( t ) y(t) x(t)c(t) y(t)x(t)c(t) 上式称为调制,其中 x ( t ) x(t) x(t) 称为 调制信号 ,而 c ( t ) c(t) c(t) 称为 载波信号 , y ( t ) y(t) y(t) 称为 已…

Redis高可用(主从复制、哨兵模式和Cluster集群)

文章目录 一、Redis高可用1.持久化2.主从复制3.哨兵4.Cluster集群 二、主从复制1.概念2.作用3.主从复制流程4.配置主从复制 三、哨兵模式1.功能2.作用3.组成4.故障转移机制7.故障模拟8.恢复故障节点 四、Cluster群集1.简介2.作用(1)数据分区(…