调试 Three.js 中的低 FPS

2024-04-26

我正在处理 Three.js WebGL 场景,当我缩小时,我注意到 60 FPS,以便所有观察结果(约 20,000 个三角形)都在视图中,但当我放大时,FPS 非常低,因此只有一个小三角形的子集在视野中。

我想弄清楚是什么导致了这种差异。我的直觉是,事实恰恰相反:我假设当用户放大近端和远端剪切平面时,会从场景中删除许多三角形,从而提高 FPS。我想弄清楚为什么这种直觉在这个场景中是错误的。

如何识别在 Three.js 程序中使用的完整调用堆栈?理想情况下,我想确定所有函数/方法调用以及执行该函数所需的时间,以便我可以尝试找出我正在处理的着色器的哪一部分在用户放大时会破坏 FPS。


GPU 有几个消耗计算能力的基本地方。这应该是很明显的。一种是每个顶点运行一次顶点着色器。另一种是每个像素/片段运行一次片段着色器。

像素几乎总是比顶点多很多。单个 1920x1080 屏幕有近 200 万像素,但可以用 3 顶点三角形或 4 或 6 顶点四边形(2 个三角形)覆盖。这意味着为了覆盖整个屏幕,顶点着色器运行了 3 到 6 次,但片段着色器运行了 200 万次!

向片段着色器发送过多的工作称为“填充绑定”。您最大化了填充率(用像素填充三角形),这就是您所看到的。在最糟糕的情况下,在我的 2014 年 MacBook Pro 上,我可能只能在 6 个左右的屏幕上绘制像素,然后才能达到每秒 60 帧更新屏幕的填充率限制。

对此有多种解决方案。

第一个是 z 缓冲区。 GPU 将首先测试深度缓冲区,看看是否需要运行片段着色器。如果深度测试失败,GPU 不需要运行片段着色器。因此,如果您对不透明对象进行排序和绘制,首先是最近的对象,最后是最远的对象,那么远处的大多数对象在渲染其三角形的像素时将无法通过深度测试。请注意,只有当您的片段着色器不写入时,这才有可能gl_FragDepth并且不使用discard关键词。

这是一种“避免透支”的方法。过度绘制是指多次绘制的任何像素。如果您在远处绘制一个立方体,然后在近距离绘制一个球体,使其覆盖立方体,那么对于为立方体渲染的每个像素,它都会被球体像素“透支”。那是浪费时间。

如果您的片段着色器确实很复杂,因此运行速度很慢,某些 3D 引擎将绘制“Z 缓冲区预通道”。他们将使用最简单的顶点和片段着色器绘制所有不透明几何体。顶点着色器只需要位置。片段着色器仅发出一个常量值。他们甚至会关闭对颜色缓冲区的绘制gl.colorMask(false, false, false, false)或者如果硬件支持的话,可能会制作一个仅深度的帧缓冲区。然后他们用它来填充深度缓冲区。完成后,他们使用昂贵的着色器再次渲染所有内容,并将深度测试设置为LEQUAL(或任何适合他们的引擎的东西)。这样每个像素只会被渲染一次。当然它不是免费的,它仍然需要 GPU 时间来尝试光栅化三角形并测试每个像素,但如果着色器很昂贵,它仍然比过度绘制更快。

另一种方法是尝试找出哪些对象将被较近的对象遮挡,甚至不将它们提交给 GPU。有很多方法可以做到这一点 https://www.gamasutra.com/view/feature/131801/occlusion_culling_algorithms.php,通常涉及边界球和/或边界框。一些潜在可见集 https://en.wikipedia.org/wiki/Potentially_visible_set技术还可以帮助进行遮挡剔除。您甚至可以使用 GPU 来计算其中的一些内容遮挡查询 http://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch29.html虽然这仅在 WebGL2 中可用

查看是否已填充的最简单方法是使画布变小,例如 2x1 像素(或者将浏览器窗口的大小设置得非常小)。如果您的应用程序开始快速运行,则很可能已满。如果它仍然运行缓慢,则可能是几何限制(顶点着色器做了太多工作)或者是 CPU 限制(无论您在 CPU 上执行的任何工作都花费了太长时间,无论是调用 WebGL 命令还是计算动画或碰撞)或物理或其他)。

在你的情况下,你可能会被填充,因为你看到当所有三角形都很小时,它运行得很快(因为绘制的像素很少),而当你放大并且大量三角形覆盖屏幕时,它运行得很慢(因为太正在绘制许多像素)。

不存在真正“简单”的解决方案。我真的只取决于你想做什么。显然你正在使用 Three.js,我知道它可以对透明对象进行排序。我不知道它是否适合不透明的物体。我认为列出的其他技术超出了 Three.js 的范围,更多取决于您的应用程序将事物带入和带出场景或将其可见性设置为 false 等...

Note: 这是一个简单的演示,展示您的 GPU 可以处理多少过度绘制 http://greggman.github.io/doodles/overdraw.html。它只是绘制一堆全屏四边形。默认情况下,在无法再达到 60 fps 之前,它可能无法绘制那么多,尤其是在全屏尺寸下。打开从前到后排序,它将能够绘制更多内容,并且仍能达到 60fps。

另请注意,启用混合比禁用混合要慢。这应该很清楚,因为在不混合的情况下,GPU 只是写入像素。通过混合,GPU 必须首先读取目标像素,以便它可以进行混合,因此速度较慢。

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

调试 Three.js 中的低 FPS 的相关文章

  • 如何从 cdn THREE.js 加载 GLTFLoader

    我在弄清楚如何让 GLTFLoader 在 THREE js 中工作时遇到一些问题 我不明白如何使用 CDN 站点来托管文件 我尝试过使用网络上示例的链接 但这并没有完成我的工作 我在另一篇文章中读到 GLTFLoader 文件必须与我正在
  • Three.js 通过 Gui 阻止 Raycast

    我想通过光线投射选择对象 但每次我想在 Three js GUI 上选择某些内容时 Mousdown 事件都会被触发 我怎么说 如果 Gui 在对象前面 则不触发 之类的话 document addEventListener mousedo
  • 使用 OrbitControls 时锁定 x 轴旋转 - Three.js

    使用 OrbitControls 时锁定 x 轴旋转是否可行 目前我有一个挂在绳子上的圣诞星的对象模型 我希望它仅水平旋转 对于您的 OrbitControls 实例集 controls minPolarAngle Math PI 2 co
  • 如何制作可点击的 CSS3DObject

    我正在使用三个 JS CSS3DRenderer 尝试使 CSS3DObject 在单击时更新其position z 这是我的代码 var element document createElement div element style w
  • THREE.js 使用 DOMElements 的 SphereGeometry 全景热点

    我使用以下命令创建了一个简单的 WebGL 3D 全景应用程序SphereGeometry PerspectiveCamera and a CanvasTexture 现在 我希望通过在场景的某些部分添加 热点 来使场景栩栩如生Sphere
  • 向该对象的每一面添加不同的颜色

    我为我的应用程序重新创建了一个包模型 并将其作为 obj 导出到 ThreeJs 中 我为模型几何中发现的每个面分配了不同的颜色 如下所示 var geometry new THREE Geometry fromBufferGeometry
  • requestAnimationFrame 仅被调用一次

    我正在尝试在 Ionic 2 应用程序中使用 ThreeJS 实现非常基本的动画 基本上是尝试旋转一个立方体 但立方体没有旋转 因为 requestAnimationFrame 仅在渲染循环内执行一次 I m able to see onl
  • 为什么音频可视化直播无法在移动设备/Safari 上运行?

    我正在尝试基于 Three js 示例制作音频直播可视化工具 https trijs org examples q visua webaudio visualizer https threejs org examples q visua w
  • 如何将 3D 模型从 Cinema4D 导出到 Three.js?

    如果我有一个网格建模4D影院 我怎样才能将其导出three js http github com mrdoob three js3D JS 引擎 另外 导出材料也很方便colors for 多边形选择 为此 我刚刚为 Cinema4D 编写
  • 将几何图形转换为 BufferGeometry

    据我了解 Geometry 存储顶点和面的 javascript 对象结构 而 BufferGeometry 仅通过 Float32Arrays 等存储原始 gl 数据 有没有什么方法可以将标准 Geometry 转换为 BufferGeo
  • Three.js - 在自定义几何体上平滑兰伯特材质着色的问题

    我在 Three js 中创建了一个自定义几何体 现在 我想创建一个使用平滑阴影兰伯特材质的网格 使用循环 我创建了顶点数组 然后创建了面 然后我调用了 geometry computeCentroids geometry computeF
  • Three.jsmaterialLoader不加载嵌入的纹理图像

    我使用 Material toJSON 提供的方法导出 Three js 材质 结果如下 metadata version 4 5 type Material generator Material toJSON uuid 8E6F9A32
  • Material.alphaTest 是什么意思?

    在过去的几天里 我一直在非相交对象的透明度方面遇到重大问题 我遇到了设置的建议alphaTest将材料的值调整为0 5 从而解决了问题 太好了 但我想更好地理解它的含义以及它如何如此优雅地解决问题 有人可以建议吗 从一个简单的实验来看 这似
  • 获取网格顶点的最佳方法 Three.js

    我是 Three js 的新手 所以也许我不会以最佳方式解决这个问题 我创建的几何图形如下 const geo new THREE PlaneBufferGeometry 10 0 然后我对其进行旋转 geo applyMatrix new
  • 如何在 Three.js 中从三角面获取多边形?

    我在网上查了一下是否有人遇到同样的问题 我正在使用 Three js 我有一个 3DObject 其中可能包含孔 面是三角形的 假设我想从上面看到它 我的目标是获得一个代表顶面周长的多边形 这对我来说意味着不再有三角面 而只有 1 个多边形
  • 如何在 Three.js 中将 geoJSON 绘制为网格而不是线条,并填充颜色?

    我正在使用 Three js 制作一个地球仪 并将添加一些数据层 所有图层都将从 geoJSON 创建 我已将其设置为使地球仪 第一个数据文件 包含国家 地区 显示为线条 这使用三GeoJSON https github com jdomi
  • 一次性渲染阴影

    考虑到阴影投射的成本 我想知道对于动态定位的静态对象 例如 程序城市 是否有一个功能或可能 实验性的方法可以在 Three js 中仅渲染一次阴影贴图 甚至在 webgl 中 因此 结果可以在静态对象的下一帧中免费使用 仅当物体移动时才会进
  • Three.js 光线投射器可以与组相交吗?

    我想知道我的光线投射器是否正在查看我已加载的 OBJ 由于从 Cinema4D 导出的方式 我相信 OBJ 是一个具有 3 个子级的 THREE Group 而不是 THREE Object 我可以更改我的 raycaster 代码行来查找
  • 在 TypeScript 中使用三个 Js + OrbitControl

    我无法得到this http www example com 在 TypeScript 中使用上述组合的示例 I have and 在我的html中和打字稿文件
  • Three.js - 将 WebGL 和 CSS3D 与 iFrame 混合

    我准备了一个混合的工作页面WebGL and CSS3D 在SO的一点帮助下here https stackoverflow com questions 24681170 three js properly blending css3d a

随机推荐