WebGL-wind和GPU编程。 FrontTalks 2018演讲

为了在网页上呈现复杂的图形,有一个Web图形库,简称WebGL。 界面设计师Dmitry Vasiliev从布局设计师的角度谈到了GPU编程,介绍了WebGL是什么以及我们如何解决使用此技术可视化大型天气数据的问题。


-我正在Yandex的叶卡捷琳堡办公室开发接口。 我开始参加运动组。 当有曲棍球,足球,奥运会,残奥会和其他酷事世锦赛时,我们正在开发体育特别项目。 我还致力于特殊搜索结果的开发,该搜索结果专门用于新的索契赛道。








幻灯片链接

此外,在一个半盔中,我们重新启动了“错误处理”服务。 然后在Pogoda开始工作,我在那里支持该API的可操作性,其开发,围绕该API编写基础结构以及为受过训练的机器学习公式编写节点绑定程序。



然后工作开始变得更加有趣。 参与了我们天气服务的重新设计。 台式机,独轮车。





整理好标准预测之后,我们决定做出没有人预测的预测。 该预测是整个地区降水量的预测。



有一些特殊的天气雷达可以探测2000公里半径内的降水,它们知道其密度和与之的距离。



使用这些数据并借助机器学习对其进一步运动进行预测,我们在地图上进行了可视化处理。 您可以来回移动。


幻灯片链接

我们查看了人们的评论。 人们喜欢它。 各种各样的模因开始出现,当莫斯科泛滥成灾时,有很酷的画面。

由于每个人都喜欢这种格式,因此我们决定继续进行下面的预测。



显示天气预报的服务已经在那里。 这是几个杰出的。



看着他们,我们意识到我们想做同样的事情-或至少不会更糟。

因此,我们决定可视化根据风速在地图上平滑移动的粒子,并留下某种循环以便可以看到它们,并且可以看到风的轨迹。

由于我们已经很棒,并且使用2D画布绘制了一个带有降水的凉爽地图,因此我们决定对粒子进行相同的处理。



与设计师协商后,我们意识到我们需要在屏幕的约6%处填充颗粒,以达到炫酷效果。

为了使用标准方法绘制这么多的粒子,我们的最小时序为5毫秒。



如果您认为我们仍然需要移动粒子并带来某种美感(例如绘制粒子的尾巴),则可以假设我们将以至少40毫秒的时间掉线以显示流畅的动画,以便每秒至少产生25帧。

问题在于这里每个粒子将被顺序处理。 但是,如果您并行处理它们怎么办?

在一次会议上,“传奇驱逐舰”显示了中央处理器和图形处理器之间的明显区别。 他们推出了一台装有彩弹笔的机器,其任务是用一种颜色画一个笑脸。 在大约10秒钟内,他画了这样一张照片。 ( 链接到视频 -大约。)







然后,这些家伙推出了独木舟,这是一个GPU,还有几口喷绘了蒙娜丽莎(Mona Lisa)。 这就是计算CPU和GPU的速度不同的方式。











为了利用浏览器中的这些功能,发明了WebGL技术。

这是什么 带着这个问题,我爬上了互联网。 我添加了一些带有粒子动画和风的单词,我发现了几篇文章。


幻灯片中的链接: 第一第二

其中之一是Mapbox的工程师Vladimir Agafonkin的演示,他在WebGL上风靡一时,并引用了Chris Wellons的博客,后者谈到了如何在GPU上移动和存储粒子状态。

我们接受并复制。 我们期待这样的结果。 在这里,粒子平滑移动。



我们弄不清楚。



试图找出代码。 不断改进,但结果仍然不尽人意。 我们爬得更深-我们下雨而不是风。



好吧,我们决定自己做。



要使用WebGL,需要有框架。 它们几乎都是针对3D对象的。 我们不需要这些3D功能。 我们只需要绘制一个粒子并移动它。 因此,我们决定用手做所有事情。



当前有两个版本的WebGL技术。 第二个版本很酷,它具有高级的编程语言版本,该版本的程序在图形适配器中运行,可以直接执行计算,而不仅仅是绘制。 但是它的兼容性差。



好吧,我们决定使用久经考验的WebGL 1,它除了Opera Mini之外还具有很好的支持,而没人需要。



WebGL是两部分的东西。 这是JS,用于执行在图形卡上运行的程序的状态。 还有直接在图形卡上运行的组件。

让我们从JS开始。 WebGL只是canvas元素的适当上下文。 而且,在接收到该上下文时,不仅分配了特定对象,还分配了铁资源。 而且,如果我们在浏览器上的WebGL上运行漂亮的东西,然后决定播放Quake,那么很可能这些资源会丢失,上下文可能会丢失,并且整个程序都会中断。



因此,在使用WebGL时,您还必须倾听上下文的丢失并能够恢复它。 因此,我强调了init是。



此外,JS的所有工作归结为收集在GPU上运行的程序,向其发送图形卡,设置一些参数并说“ draw”。



在WebGL中,如果您查看上下文元素本身,则会看到一堆常量。 这些常量引用内存中的地址。 它们在程序执行过程中并不是真正恒定的。 因为如果上下文丢失并再次恢复,则可能会分配另一个地址池,并且这些常量对于当前上下文将有所不同。 因此,JS端WebGL中的几乎所有操作都是通过实用程序执行的。 没有人愿意做查找地址和其他垃圾的例行工作。



我们来看一下在视频卡本身上执行的操作-一个由两组以类C语言GLSL编写的指令组成的程序。 这些指令称为顶点着色器和片段着色器。 从他们的一对创建一个程序。



这些着色器之间有什么区别? 顶点着色器设置应在其上绘制对象的表面。 设置完基元并进行绘制后,将调用落入该范围的片段着色器。





在代码中,它看起来像这样。 着色器有一个部分,用于声明从JS外部设置的变量,并确定其类型和名称。 还有主要部分,该部分执行此迭代所需的代码。

在大多数情况下,期望顶点着色器将gl_Position变量设置为二维空间中的某个坐标。 这是x,y,z和空间的宽度,目前还不需要知道。

片段着色器希望设置特定像素的颜色。

在此示例中,我们从连接的纹理中选择了像素颜色。



要将其传输到JS,只需将着色器的源代码包装在变量中即可。



此外,这些变量将转换为着色器。 这是一个WebGL上下文,我们从源代码创建着色器,并行创建一个程序,然后将几个着色器附加到该程序。 我们得到了一个可行的程序。

在途中,我们验证了着色器的编译是否成功,程序是否已成功构建。 我们说您需要使用此程序,因为可以有多个程序用于不同的渲染值。

设置并说平局。 事实证明是这样。



爬得更深。 在顶点着色器中,所有计算都在-1到1的空间中执行,无论输出点的大小如何。 例如,从-1到1的空间可以占据整个屏幕1920x1080。要在屏幕中央绘制一个三角形,您需要绘制一个覆盖坐标0、0的曲面。



片段着色器在0到1的空间中工作,并且颜色由四个分量显示:R,G,B,Alpha。

以CSS为例,使用百分比时可能会遇到类似的颜色符号。



要绘制内容,您需要说出需要绘制哪些数据。 特别是对于三角形,我们定义了三个顶点的类型化数组,每个顶点由三个分量x,y和足够的分量组成。

对于这种情况,顶点着色器看起来像获取当前的一对点,坐标,以及在屏幕上设置此坐标。 在这里,不进行任何转换,就在屏幕上显示了一个点。



片段着色器可以使用颜色为JS传递的常量着色,也无需进行其他计算。 此外,如果片段着色器中的某些变量是从外部或从先前的着色器传递的,则必须指定准确性。 在这种情况下,中等精度就足够了,几乎总是足够的。



我们传递给JS。 我们将相同的着色器分配给变量,并声明一个将创建这些着色器的函数。 也就是说,创建了一个着色器,将源注入其中,然后进行了编译。



我们制作两个着色器,顶点和片段。



之后,创建一个程序,将已经编译的着色器上载到该程序。 我们绑定程序是因为着色器可以相互交换变量。 并且在此阶段,将检查这些着色器交换的变量类型的对应关系。

我们说使用这个程序。



接下来,我们创建一个要可视化的顶点列表。 WebGL对于某些变量具有有趣的功能。 要更改特定的数据类型,您需要设置全局上下文以编辑array_buffer,然后将某些内容上传到该地址。 没有任何数据明确分配给变量。 一切都是通过包含一些上下文来完成的。

还必须建立从该缓冲区读取的规则。 可以看出,我们指定了一个由六个元素组成的数组,但是程序需要说明每个顶点都由两个组件组成,其类型为float,这是在最后一行完成的。



要设置颜色,程序将搜索u_color变量的地址并设置该变量的值。 我们将颜色设置为红色255,绿色0.8,蓝色0,完全不透明的像素-它变为黄色。 我们说要使用三角形图元执行该程序,在WebGL中,您可以绘制点,线,三角形,复杂形状的三角形等。 并取得三个高峰。



您还可以指定从一开始就应该对要渲染的数组进行计数。


幻灯片链接

如果使示例复杂一点,则可以在光标位置添加颜色依赖性。 同时,每秒帧数穿过屋顶。



要绘制世界各地的粒子,您需要了解世界上每个点的风速。

要放大并以某种方式移动地图,您需要创建与地图的当前位置匹配的容器。

要移动粒子本身,您需要提出一种可以使用GPU更新的数据格式。 制作图形本身并绘制循环。



我们通过纹理处理所有数据。 我们使用22个通道来确定水平和垂直速度,其中零风速对应于颜色范围的中间。 大约是128。 由于速度可以为正也可以为负,因此我们将颜色设置为相对于范围的中间值。

事实证明是这样。



要将其加载到卡上,我们需要对其进行切割。 要将图片连接到地图,我们将使用标准的Yandex.Map图层工具,在其中我们将确定从中切割图块的地址,并将此图层添加到地图中。


幻灯片链接

我们得到一张图片,其中令人不愉快的绿色被编码为风速。



接下来,您需要找到一个将绘制动画本身的位置,而该位置应对应于地图的坐标,其运动和其他动作。

默认情况下,我们可以假设我们将使用“图层”,但是卡片图层会创建一个画布,并从画布中立即捕获它可以捕获的2D上下文。 但是,如果我们尝试从已经具有不同类型的上下文的画布中获取并从GL上下文中获取,则结果将为null。 如果您访问它,程序将崩溃。


幻灯片链接

因此,我们使用Pane,这是布局的容器,并在其中添加了画布,我们已经从中获取了所需的上下文。



为了以某种方式将粒子排列在屏幕上并能够移动它们,使用了粒子在纹理中的位置格式。

如何运作? 创建正方形纹理以进行优化,并且在此已知其侧面的大小。



通过按顺序绘制粒子并知道粒子的序列号和存储粒子的纹理的大小,可以计算出特定像素,在其中编码了实际屏幕上的位置。





在着色器本身中,看起来就像读取渲染的索引,带有粒子当前位置和侧面尺寸的纹理。 接下来,我们确定该粒子的x,y坐标,读取该值并将其解码。 这是什么魔术:rg / 255 + ba?

对于粒子的位置,我们使用20个双通道。 颜色通道的值介于0到255之间,对于1080屏幕,我们不能将粒子放置在屏幕的任何位置一会儿,因为我们最多可以将粒子放置在255个像素中。 因此,在一个通道中,我们存储粒子通过255个像素的次数的知识,在第二通道中,我们存储粒子在之后通过多少次的确切值。

接下来,顶点着色器必须将这些值转换为其工作空间,即从-1到1,然后在显示器上设置此点。



仅查看我们的粒子,只需将其涂成白色即可。 GLSL具有这样的优势,例如,如果我们定义变量的类型并将其传递给常量,则该常量将分布在所有四个组件中。



绘制了这样一个程序,我们看到了一组相同的正方形。 让我们尝试为它们添加美丽。



首先,添加这些平方对当前风速的依赖性。 我们只需读取每个粒子的当前速度和相应的纹理即可。 我们获得矢量的长度,该矢量对应于该点的绝对速度,并将该速度添加到粒子大小中。



此外,为了不绘制正方形,在片段着色器中,我们切掉了不在半径范围内的所有像素,这些像素不包括在内切圆的半径范围内。 也就是说,我们的着色器变成了这样的东西。



我们计算从中心到渲染像素的距离。 如果超过一半的空间,则我们不显示它。



我们得到了更加多样化的图景。

接下来,您需要以某种方式移动这些内容。 由于WebGL 1不知道如何计算,而是直接处理数据,因此我们将利用将程序绘制到特殊组件(帧缓冲区)中的功能。

帧缓冲区可以映射到例如可以更新的纹理。 如果未声明帧缓冲区,则默认情况下在屏幕上进行绘制。

将输出从一种位置纹理切换到另一种位置纹理,我们可以一个一个地更新它们,然后将其用于渲染。





更新位置本身的过程如下所示:读取当前位置,将其添加到当前速度矢量中,然后将其添加,编码为新颜色。



在代码中,看起来就像读取当前位置,解码,读取当前速度,将速度恢复正常,折叠这两个分量并进行彩色编码。



事实证明是这样。 粒子的状态不断变化,并出现某种动画。

如果将此类动画运行5-10分钟,则很明显所有粒子将到达其最终目的地。 它们都滑入漏斗中。 你得到这样的照片。



为了避免这种情况,我们在随机的位置引入一个粒子置换因子。

它取决于当前的风速,当前的粒子位置以及我们从JS传输的随机数-因为WebGL的第一个版本没有随机函数和某种噪声函数。



在此示例中,我们计算粒子的预测位置,随机位置,并根据重置因子选择一个或另一个。


幻灯片中的链接: 第一第二第三第四

要了解上一张幻灯片的内容,您可以阅读这些文章。 第一种方法极大地增进了对WebGL提供的内容,其组成以及如何避免出错的理解。 在Khronos,这是一个从事标准制定的小组,对所有功能进行了描述。



我们任务的最后一点是绘制微粒的痕迹。为此,就像位置更新纹理一样,我们将以两种纹理在屏幕上记录当前位置,并显示当前位置,稍微增加其透明度,覆盖新的粒子位置,然后一次又一次地增加此图像的透明度最重要的是,我们强加了一个新职位。



我们得到了这样的循环动画。





如果将WebGL渲染的整个周期与使用2D画布在屏幕上某些点的显示进行比较,您会发现速度差距很大。要在2D画布上绘制6万4千点,平均需要25毫秒,而WebGL阻止主流0.3毫秒。这相差一百倍。

, WebGL , , .

, , , - break points, - , . WebGL — .



, . , Firefox «», WebGL-, , , . , .



使生活更加轻松的第二个工具是Spector.js浏览器扩展。它还从WebGL上下文中捕获画布,并允许您查看在此画布上执行的所有操作,时序和传递的变量。



在总共一周的工作中,我们从头开始有了一个交钥匙的解决方案我希望我能够分辨出WebGL是什么技术,它包含什么,并给出在产品中使用它的真实示例。仅此而已。

Source: https://habr.com/ru/post/zh-CN433278/


All Articles