为什么 WebGL 比 Canvas 更快?

2024-01-26

如果两者都使用硬件加速(GPU)来执行代码,为什么 WebGL 比 Canvas 更快?

我的意思是,我想知道为什么在低级别上,从代码到处理器的链条。

会发生什么? Canvas/WebGL 直接与驱动程序通信,然后与显卡通信?


Canvas 速度较慢,因为它是通用的,因此很难优化到与 WebGL 优化相同的级别。让我们举一个简单的例子,用arc https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc.

Canvas 实际上运行在 GPU 之上,并使用与 WebGL 相同的 API。那么,画圆的时候canvas要做什么呢?使用canvas 2d在JavaScript中绘制圆的最少代码是

ctx.beginPath():
ctx.arc(x, y, radius, startAngle, endAngle);
ctx.fill();

你可以想象内部最简单的实现是

  1. beginPath创建一个缓冲区(gl.bufferData)
  2. arc生成构成圆形的三角形的点并上传gl.bufferData.
  3. fill calls gl.drawArrays or gl.drawElements

但是等一下...知道我们对 GL 如何工作画布的了解无法在步骤 2 中生成点,因为如果我们调用stroke代替fill然后根据我们对 GL 工作原理的了解,我们需要一组不同的点来表示实心圆(填充)和圆的轮廓(描边)。所以,真正发生的事情更像是

  1. beginPath创建或重置一些内部缓冲区
  2. arc生成在内部缓冲区中形成圆圈的点
  3. fill获取该内部缓冲区中的点,为该内部缓冲区中的点生成正确的三角形集到 GL 缓冲区中,然后使用以下命令上传它们gl.bufferData, calls gl.drawArrays or gl.drawElements

如果我们想画 2 个圆圈会怎样?可能会重复相同的步骤。

让我们将其与 WebGL 中的操作进行比较。当然,在 WebGL 中我们必须编写自己的着色器(Canvas 也有它的着色器 https://github.com/google/skia/tree/master/src/gpu/glsl)。我们还必须创建一个缓冲区并用三角形填充它以形成一个圆(请注意,我们已经节省了时间,因为我们跳过了点的中间缓冲区)。然后我们可以调用gl.drawArrays or gl.drawElements画出我们的圆圈。如果我们想画第二个圆呢?我们只需更新制服并致电gl.drawArrays再次跳过所有其他步骤。

const m4 = twgl.m4;
const gl = document.querySelector('canvas').getContext('webgl');
const vs = `
attribute vec4 position;
uniform mat4 u_matrix;

void main() {
  gl_Position = u_matrix * position;
}
`;

const fs = `
precision mediump float;
uniform vec4 u_color;
void main() {
  gl_FragColor = u_color;
}
`;

const program = twgl.createProgram(gl, [vs, fs]);
const positionLoc = gl.getAttribLocation(program, 'position');
const colorLoc = gl.getUniformLocation(program, 'u_color');
const matrixLoc = gl.getUniformLocation(program, 'u_matrix');

const positions = [];
const radius = 50;
const numEdgePoints = 64;
for (let i = 0; i < numEdgePoints; ++i) {
  const angle0 = (i    ) * Math.PI * 2 / numEdgePoints;
  const angle1 = (i + 1) * Math.PI * 2 / numEdgePoints;
  // make a triangle
  positions.push(
    0, 0,
    Math.cos(angle0) * radius,
    Math.sin(angle0) * radius,
    Math.cos(angle1) * radius,
    Math.sin(angle1) * radius,
  );
}

const buf = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);

gl.enableVertexAttribArray(positionLoc);
gl.vertexAttribPointer(positionLoc, 2, gl.FLOAT, false, 0, 0);
                 
gl.useProgram(program);
                 
const projection = m4.ortho(0, gl.canvas.width, 0, gl.canvas.height, -1, 1);

function drawCircle(x, y, color) {
  const mat = m4.translate(projection, [x, y, 0]);
  gl.uniform4fv(colorLoc, color);
  gl.uniformMatrix4fv(matrixLoc, false, mat);

  gl.drawArrays(gl.TRIANGLES, 0, numEdgePoints * 3);
}

drawCircle( 50, 75, [1, 0, 0, 1]);
drawCircle(150, 75, [0, 1, 0, 1]);
drawCircle(250, 75, [0, 0, 1, 1]);
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>
<canvas></canvas>

一些开发人员可能会看到这一点并认为 Canvas 缓存了缓冲区,因此它可以重用第二次绘制调用上的点。这可能是真的,但我有点怀疑。为什么?由于canvas api的通用性。fill,完成所有实际工作的函数不知道点的内部缓冲区中有什么。您可以致电arc, then moveTo, lineTo, then arc再次,然后调用fill。当我们到达时,所有这些点都将位于点的内部缓冲区中fill.

const ctx = document.querySelector('canvas').getContext('2d');
ctx.beginPath();
ctx.moveTo(50, 30);
ctx.lineTo(100, 150);
ctx.arc(150, 75, 30, 0, Math.PI * 2);
ctx.fill();
<canvas></canvas>

换句话说,fill 需要始终查看所有点。另一件事,我怀疑 arc 试图优化尺寸。如果你打电话arc半径为 2 时,它生成的点可能比半径为 2000 时生成的点少。画布可能会缓存这些点,但考虑到命中率可能很小,这似乎不太可能。

无论如何,重点是 WebGL 让您可以在较低级别进行控制,从而跳过画布无法跳过的步骤。它还允许您重用画布无法重用的数据。

事实上,如果我们知道要绘制 10000 个动画圆圈,我们在 WebGL 中甚至还有其他选择。我们可以生成 10000 个圆圈的点,这是一个有效的选项。我们还可以使用实例化。这两种技术都比画布快得多,因为在画布中我们必须调用arc10000 次,以某种方式,它必须为每一帧生成 10000 个圆圈的点,而不是在开始时只生成一次,并且必须调用gl.drawXXX10000 次而不是一次。

当然,canvas 的反面就很简单了。绘制圆圈需要 3 行代码。在 WebGL 中,因为您需要设置和编写着色器,所以可能至少需要 60 行代码。事实上,上面的示例大约有 60 行,不包括编译和链接着色器的代码(约 10 行)。除此之外,画布还支持变换、图案、渐变、蒙版等。我们必须在 WebGL 中添加更多行代码来添加所有选项。因此,Canvas 基本上是用易用性来换取 WebGL 的速度。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 WebGL 比 Canvas 更快? 的相关文章

  • 如何在php中使用preg添加html属性

    我正在寻找在 php 中编写一个脚本来扫描 html 文档并根据它找到的内容向元素添加新标记 更具体地说 我是扫描文档并为每个元素搜索CSS标记 float right left 如果找到它 它会添加align right left 基于它
  • 禁用特定 div 上的 Tab 键

    我有以下结构 div div Some content div div Some content div div 我想 禁用 div2 上的 tab 键 我的意思是按下 tab 键时 div2 的元素不会获得焦点 有没有简单的方法可以使用
  • 如何让div与包含td的高度相匹配?

    我沿着桌子的一排布置了三个 面板 一个比另外两个高 我希望所有三个面板都与最高的一个的高度相匹配 我尝试将 div 的样式设置为 height 100 但是即使包含的 tds 增长 短面板仍然很短 我的 HTML 是由 JSF 生成的 因此
  • 哪些属性有助于运行时 .Net 性能?

    我正在寻找可用于通过向加载器 JIT 编译器或 ngen 提供提示来确保 Net 应用程序获得最佳运行时性能的属性 例如我们有可调试属性 http msdn microsoft com en us library k2wxda47 aspx
  • 从 html 属性中删除单引号和双引号,并且除 href 和 src 之外的所有属性上都没有空格

    我正在尝试从 html 属性中删除单引号和双引号 这些属性是没有空格的单个单词 我写了这个有效的正则表达式 type title data toggle colspan scope role media name rel id class
  • 删除圆形图像周围的边框

    我有一个圆形图像 png 文件 中间是透明的 我需要将图像内的背景设置为纯色 为此 我将背景设为纯色 然后将border radius 50 但这会产生一条丑陋的小白线 有没有办法摆脱这个问题 或者我必须在图像编辑器中手动为图像着色 div
  • HTML5 仅拖放图像

    我想做的是 如果所有拖动的文件都是图像 则将其删除 但如果有其他文件扩展名 则不要删除它们 而仅删除图像 这是我的尝试 HTML div div JavaScript var dropzone document getElementById
  • Arbor Js - 节点 Onclick?

    我在用着arbor js http arborjs org 创建图表 我如何创建一个onclick节点的事件 或者在单击时在某处创建节点链接 Arborjs org 主页的节点在单击时链接到外部页面 我如何复制它 或者使节点在单击时调用 j
  • 如何在 Bootstrap 3 的导航栏中添加带有图标的搜索框?

    我正在使用新的 Twitter Bootstrap 3 并尝试放置一个像这样的搜索框 如下 在顶部导航栏中 在 Bootstrap 2 中 可以这样完成
  • 如何在通过 .ajaxForm() 提交表单之前执行一些操作?

    我正在使用 ajaxForm 框架来发送我的数据 而无需重新加载我的页面 ReplayForm ajaxForm success function data alert Success 现在 我想在提交表单之前检查一些条件 如果条件为假 则
  • 水平滚动的表格上的“粘性”标题......完全不可能?

    经过过去几个小时的研究后 我开始认为这是不可能的 即使在最新的浏览器上也是如此 HTML table具有水平滚动的元素 带有 粘性 thead在顶部 作为垂直滚动的周围网页的一部分 这是我的尝试 a height 100px backgro
  • JSP/Servlet HTTP 404 错误处理

    我想在我的网络应用程序中处理 HTML 404 错误 我可以这样写
  • 如何转义 HTML 字符?在.NET中-->“

    如何在 NET 中转义 HTML 字符 我正在从 json 字符串中获取 html 并在标题中得到 amp quot more text 看起来我需要做两次才能得到 amp quot 成为 quot 那么它就是一个 如何转义 NET 中的所
  • 如何在 select 和 option 标签中添加 JSON 数据?

    我有这个html代码 div class searchfilter div class searchwrapper div div
  • 检索 css3 缩放元素的宽度/高度

    我正在与 offsetWidth 属性的奇怪之处 我认为 作斗争 这是场景 比方说 我有一个span标签 在我的js中 在某个时刻我执行css3转换 对于这个元素 例如 el set styles transform scale scale
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • 如何使整个跨度落入新行?

    这个片段显示了我想要的 http jsfiddle net 945Df 3 http jsfiddle net 945Df 3 div class sup strong a href Rosario Santa Fe Argentina a
  • 适用于移动设备的响应式订单确认电子邮件?

    我从未见过令人惊叹的订单确认 发票电子邮件 即使是最好的 html5 网站也会发送糟糕的订单确认电子邮件 有时是纯文本 我相信这是因为发票通常需要使用表格来显示购买的物品 这在移动设备上实现起来非常困难 我发现了一些让手机上的表格更易于管理
  • 将特定字形与网络字体一起使用

    使用网络字体 我想使用字体功能设置 CSS 中的选项以及跨度类HTML 中 以便使用字体集中的特定替代字形 我需要以正确的语法使用哪些值 GID Unicode 才能定位特定的目标glyph内glyph备择方案 这些功能使用 OpenTyp
  • 如何在数据列表 HTML PHP 中设置选择

    您好我想知道是否有一种方法可以在数据列表中设置选定的值 我想要这样的东西

随机推荐

  • 简单的 jQuery Ajax 调用会泄漏 Internet Explorer 中的内存

    我创建了一个每秒进行一次 Ajax 调用的网页 在 Internet Explorer 7 中 内存泄漏严重 大约 15 分钟内泄漏 20 MB 该程序非常简单 它只是运行一个进行 Ajax 调用的 JavaScript 函数 服务器返回一
  • Windows 上的 Meteor JS,支持 iOS 和 Android

    我想知道 Windows 版本的 Meteor JS 是否支持开发和运行 iOS 和 或 Android 应用程序 我希望它至少支持Android 2016 年 2 月 现在适用于版本 1 3 cordova beta 5 您必须手动安装
  • 存储“派生”值与在提取时计算它们

    当您的值仅取决于一个或多个其他字段 常量 例如零售价和折扣价 时 是否最好也存储这些值或在检索数据时 即时 计算它们 默认不存储冗余信息 第三范式 http en wikipedia org wiki Third normal form通常
  • Perl 中“一维”哈希相对于数组的优势

    我只是想知道在一维数组上使用一维哈希 即只有键 没有值 无论如何我们不关心它们 的效率 我想为此目的使用哈希的主要原因是这样我可以使用存在函数来查看 条目 是否已经存在 哈希值对于不重复密钥也很有用 对吗 对于数组 我需要设置自己的涉及 g
  • 将日期转换为时间戳的问题,Spark 日期从 unix_timestamp 转换为时间戳返回 null

    将日期转换为时间戳时出现问题 Spark 日期从 unix timestamp 转换为时间戳返回 null scala gt import org apache spark sql functions unix timestamp scal
  • 将我的 ASP.NET MVC Web 应用程序发布到 IIS 后,无法上传内容文件夹内的文件

    我有以下操作方法 它将上传的文件添加到文件夹中 if ModelState IsValid string ADusername User Identity Name Substring User Identity Name IndexOf
  • 2 个 3D 点之间的偏航和俯仰

    我有 2 个 3D 点 我需要获取它们之间的偏航和俯仰弧度 我已经尝试了很多公式 但它不起作用 我的坐标系如下 X left right Y forward backward Z up down 有什么帮助吗 请具体说明 我真的很感激整个公
  • syslog-ng 读取文件权限被拒绝

    我需要通过 Syslog ng 发送 tomcat Catalina out 日志内容 但是 但重新启动 Syslog 服务后 出现权限被拒绝错误 那么我如何授予 Syslog ng 的读取权限以从 CATALINA out 读取 tomc
  • java中的排列迭代器

    我想要一个类 它接受一个正整数并生成一个迭代器 让我迭代该正整数下的正数列表的所有可能的排列 例如 模拟器 p paermulator 3 p next gt 0 1 2 p next gt 0 2 1 p next gt 1 0 2 p
  • 如何使用 smtplib 和 Python 保持 SMTP 连接打开?

    我需要检查 SMTP 服务器的超时 但我的套接字刚刚关闭 我究竟做错了什么 这是我的测试 usr bin python import smtplib import time import datetime import socket soc
  • 查找二进制文件的版本

    有谁知道如何找到已传递给我的函数的二进制文件的版本 我从以下代码中得到了这一页 http www linuxquestions org questions programming 9 reading binary file in pytho
  • 在 Zend HeadScript 视图助手中修改堆栈

    我正在尝试攻击这个问题 https stackoverflow com questions 2253170 zend framework last code to execute before layout is rendered从完全不同
  • 在 JUnit 测试类中哪里配置 log4j?

    看看我写的最后一个 JUnit 测试用例 我在类构造函数中调用了 log4j 的 BasicConfigurator configure 方法 这对于仅从 Eclipse 的 作为 JUnit 测试用例运行 命令运行单个类来说效果很好 但我
  • 当用户在文本框中键入值时,在另一个文本框中显示一个文本框值

    当用户在文本框中输入值时 是否有一种方法可以从一个文本框中获取值并使用 jQuery 动态地将其添加到另一个文本框中 如果有这样的事情 有人可以解释一下该方法吗 问候 兰加纳 你的意思是像http jsfiddle net ZLr9N ht
  • 在多层架构中通过层传递业务实体

    目前我正在开发一个利用多层架构的项目 如中所述应用架构指南2 0 http apparch codeplex com 有 5 层 DAL BLL Facade 表示层和公共层 这里我们有一个业务逻辑层 它由业务组件和业务实体 它们是使用 O
  • 切换到另一个分支而不更改工作区文件

    我从 GitHub 克隆了一个 git 存储库 做了一些更改和一些提交 我做了很多 而且都很脏 所以它们不适合拉请求 现在我创建了分支cleanchanges from origin master 所以它很干净 我想将我的更改作为一次提交提
  • Android 将图像保存到 SD 卡

    UPDATE Added
  • LINQ to SQL 的编译查询何时可以提高性能

    我指的是一篇文章 http www albahari com nutshell speedinguplinqtosql aspx其重点是加速 LINQ to SQL 查询 它提到的技术之一是 使用编译查询 并解释了如何使用它 我希望看到编译
  • Sharepoint 中文档库的全局自定义视图

    在Sharepoint文档库中 当我们创建新文档库时 默认视图是 所有文档 这个 我们可以制作一个自定义视图视图 gt 创建视图 位于文档库视图的右上角 我创建了一个新的标准视图并将其命名为 Chromed View 我编辑了它 然后我发现
  • 为什么 WebGL 比 Canvas 更快?

    如果两者都使用硬件加速 GPU 来执行代码 为什么 WebGL 比 Canvas 更快 我的意思是 我想知道为什么在低级别上 从代码到处理器的链条 会发生什么 Canvas WebGL 直接与驱动程序通信 然后与显卡通信 Canvas 速度