草庐IT

javascript - WebGL:有没有一种有效的方法可以只上传图像/ Canvas 的一部分作为纹理?

coder 2023-08-09 原文

我正在开发一个基于 2D 图层的应用程序,我想使用 WebGL 进行合成。这些层可能会相对于彼此移动,并且每个帧只有每个层的一小部分(矩形)可能会发生变化。但是,该矩形部分的宽度和高度可能会发生不可预测的变化。 我想每层使用一个 Canvas (2D)和一个纹理,并且每个 Canvas 上的每一帧仅重绘已修改的图层部分,然后只需将该小区域上传到 GPU 以将相应部分更新为纹理,在 GPU 为我进行合成之前。但是我还没有找到一种有效的方法来将图像的一部分上传到纹理的一部分。似乎 texSubImage2D() 可以 更新纹理的一部分,但只需要完整的图像/ Canvas ,而且似乎无法指定矩形区域要使用的图像。

我已经想到了几种方法来做到这一点,但每种方法似乎都有明显的开销:

  • 使用 getImageData() + texSubImage2D() 仅将更改的部分上传到 GPU(将 Canvas 像素数据转换为 ImageData 的开销)
  • 使用 texImage2D()
  • 重新上传每一帧的整个图层 Canvas
  • 或创建/调整一个小的 canvas2D 到正确的尺寸以完美适应每个层的修改,然后使用 texSubImage2D() 发送它来更新相关的纹理(内存预留开销)

那么,有没有办法为纹理指定图像/ Canvas 的一部分?像 texImagePart2D()texSubImagePart2D 这样的东西,它们都接受另外四个参数,sourceX, sourceY, sourceWidthsourceHeight 指定要使用的图像/ Canvas 的矩形区域?

最佳答案

很遗憾,没有办法上传 Canvas /图像的一部分。

WebGL 所基于的 OpenGL ES 2.0 没有提供这样做的方法。 OpenGL ES 3.0 确实提供了一种将源的较小矩形上传到纹理或纹理的一部分的方法,因此下一个版本的 WebGL 可能会提供该功能。

现在您可以有一个单独的 Canvas 来帮助上传。首先调整 Canvas 大小以匹配您要上传的部分

canvasForCopying.width = widthToCopy;
canvasForCopying.height= heightToCopy;

然后将要复制的 Canvas 部分复制到复制的 Canvas 上

canvasForCopying2DContext.drawImage(
    srcCanvas, srcX, srcY, widthToCopy, heightToCopy,
    0, 0, widthToCopy, heightToCopy);

然后使用它上传到您想要的纹理。

gl.texSubImage2D(gl.TEXTURE_2D, 0, destX, destY, gl.RGBA, gl.UNSIGNED_BYTE, 
                 canvasForCopying);

getImageData 可能会更慢,因为浏览器必须调用 readPixels获取 图像数据,这会使图形管道停滞。浏览器不必为 drawImage 执行此操作。

至于为什么 texImage2D 有时比 texSubImage2D 快,这取决于驱动程序/GPU,但显然有时 texImage2D 可以使用 DMA 实现而 texSubImage2D 不能。 texImage2D 也可以通过创建新纹理并懒惰地丢弃旧纹理来进行流水线处理,而 texSubImage2D 则不能。

就我个人而言,我不会担心它,但如果你想检查一下,使用 texImage2DtexSubImage2D 上传数万个纹理的时间(不要只是时间一个是图形流水线)。如果您的纹理很大并且您要更新的部分小于纹理的 25%,您可能会发现 texSubImage2D 更快。至少那是我上次检查时发现的。大多数当前的驱动程序至少在以下方面进行了优化:如果您调用 texSubImage2D 并且碰巧要替换全部内容,它们将在内部调用 texImage2D 代码。

更新

你可以做几件事

  1. 对于图像,您可以使用 fetchImageBitmap 将图像的一部分加载到 ImageBitamp 中,然后您可以上传那。获取图像的一部分的示例

    在下面的示例中,我们调用了fetch,然后获取了一个Blob,并使用该blob 制作了一个只有一部分的ImageBitmap图片。结果可以传递给 texImage2D,但为了简洁起见,该示例仅在 2d Canvas 中使用它。

fetch('https://i.imgur.com/TSiyiJv.jpg', {mode: 'cors'})
.then((response) => {
  if (!response.ok) {
    throw response;
  }
  return response.blob();
})
.then((blob) => {
  const x = 451;
  const y = 453;
  const width = 147;
  const height = 156;
  return createImageBitmap(blob, x, y, width, height);
}).then((bitmap) => {
  useit(bitmap);
}).catch(function(e) {
  console.error(e);
});

// -- just to show we got a portion of the image

function useit(bitmap) {
  const ctx = document.createElement("canvas").getContext("2d");
  document.body.appendChild(ctx.canvas);
  ctx.drawImage(bitmap, 0, 0);
}

  1. 在 WebGL2 中有 gl.pixelStorei 设置

    UNPACK_ROW_LENGTH//源的一行有多少像素 UNPACK_SKIP_ROWS//从源头开始跳过多少行 UNPACK_SKIP_PIXELS//从源左边跳过多少个像素

    所以使用这 3 个设置,您可以告诉 webgl2 源更宽,但您想要的部分更小。您将较小的宽度传递给 texImage2D,上面的 3 个设置有助于告诉 WebGL 如何去掉较小的部分以及从哪里开始。

关于javascript - WebGL:有没有一种有效的方法可以只上传图像/ Canvas 的一部分作为纹理?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20930466/

有关javascript - WebGL:有没有一种有效的方法可以只上传图像/ Canvas 的一部分作为纹理?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  5. Ruby 方法() 方法 - 2

    我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

  6. ruby - 难道Lua没有和Ruby的method_missing相媲美的东西吗? - 2

    我好像记得Lua有类似Ruby的method_missing的东西。还是我记错了? 最佳答案 表的metatable的__index和__newindex可以用于与Ruby的method_missing相同的效果。 关于ruby-难道Lua没有和Ruby的method_missing相媲美的东西吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/7732154/

  7. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  8. ruby-on-rails - rails 目前在重启后没有安装 - 2

    我有一个奇怪的问题:我在rvm上安装了ruby​​onrails。一切正常,我可以创建项目。但是在我输入“railsnew”时重新启动后,我有“程序'rails'当前未安装。”。SystemUbuntu12.04ruby-v"1.9.3p194"gemlistactionmailer(3.2.5)actionpack(3.2.5)activemodel(3.2.5)activerecord(3.2.5)activeresource(3.2.5)activesupport(3.2.5)arel(3.0.2)builder(3.0.0)bundler(1.1.4)coffee-rails(

  9. ruby - Highline 询问方法不会使用同一行 - 2

    设置:狂欢ruby1.9.2高线(1.6.13)描述:我已经相当习惯在其他一些项目中使用highline,但已经有几个月没有使用它了。现在,在Ruby1.9.2上全新安装时,它似乎不允许在同一行回答提示。所以以前我会看到类似的东西:require"highline/import"ask"Whatisyourfavoritecolor?"并得到:Whatisyourfavoritecolor?|现在我看到类似的东西:Whatisyourfavoritecolor?|竖线(|)符号是我的终端光标。知道为什么会发生这种变化吗? 最佳答案

  10. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

随机推荐