”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 最大化性能:深入探讨 PixiJS 优化

最大化性能:深入探讨 PixiJS 优化

发布于2024-11-08
浏览:158

利用先进的策略和技术将您的 PixiJS 应用程序提升到新的水平

前言

这篇文章介绍了在 CPU / 内存方面可以最好地优化 pixiJS 中多个元素的渲染的不同方法。例如,考虑在没有任何缓存的情况下重新渲染每一帧(在 CPU 使用率方面表现良好)或将渲染的图形缓存在内存中之间的差异。这将根据场景中的图形数量按比例增加内存使用量。

有许多策略可以处理此类优化。特别值得注意的是面向数据的设计,它提出了一组与更传统的常见面向对象编程方式完全不同的方法。

其他主要方法包括:例如,剔除和利用更加结构化的格式 - C# 中的 NativeArrays 和 TypeScript 中的 TypedArrays。这些将允许更好地管理内存缓冲区,这可能会限制缓存未命中,但这也需要大量的工程经验和/或定制。

在这篇文章中,我将重点介绍一种使用 PixiJS 优化 WebGL 环境的工作方法:面向对象的方法,包括最佳实践。这将为您提供一种组织良好的方法来提高 PixiJS 应用程序的速度和效率。

在我的下一篇文章中,我将讨论另一种强大的优化方法:实体组件系统方法。 ECS 方法非常以数据为导向,并且在高性能环境中优化 PixiJS 时提供了全新的外观。继续在 Medium 上阅读本文,我将深入探讨 ECS 方法的本质。

永远记住,为了优化和进一步增强 Pixi 应用程序的性能,总有一些事情可以做得更好。更好并不意味着最优化或最快。最佳解决方案是在优化投入的时间和投资回报之间进行权衡的问题,以确保您能够满足项目期限,但有足够的优化来满足任何潜在用户的需求,而不会过度扩展您的资源。

面向对象的方法

在本节中,我将指导您了解优化 PixiJS 应用程序的最佳方法。

此部分基于官方提示,值得查看!

我们的其余讨论将围绕 Pixi 图形、精灵、网格以及何时使用粒子容器而不是默认的 Pixi 容器。本章应该让您清楚地了解如何在面向对象的上下文中最佳地使用所有内容,以便您的 PixiJS 项目正常运行并以最高的效率进行渲染。

了解 Pixi 图形的内部工作原理

为了有效地使用 Pixi 图形,我们需要了解它们的内部功能。让我们首先展示一个在 Pixi 中创建图形对象的非常基本的示例:

const graphics = new PIXI.Graphics();
graphics.beginFill(0xff0000);
graphics.drawRect(0, 0, 200, 100);
graphics.endFill();

然而,在这个简单的实现中重要的是“幕后”发生的事情。在创建这种图形时,Pixi 创建了一个称为 GraphicsGeometry 对象的东西。该对象的形状和大小取决于您为正在绘制的形状指定的尺寸和属性。然后,最终的 Geometry 对象存储在 Graphics 对象内的 GeometryList 内。

请注意,每次您在 PIXI.Graphics 的帮助下绘制某些内容时,GeometryList 都会更新。有时,您只想清除此列表,但同时保持 Graphics 对象处于活动状态 - 这就是 .clear() 方法发挥作用的地方。了解此过程的工作原理将对您使用 Pixi 时有很大帮助,因为它直接影响 Pixi 如何处理和渲染应用程序中的图形。

Pixi 图形优化技术

让我们通过在 PixiJS 中创建 100 个 Graphics 对象的用例来探索优化策略。

function createGraphics(x, y) {
    const graphic = new PIXI.Graphics();
    graphic.beginFill(0xDE3249);
    graphic.drawCircle(x, y, 10);
    graphic.endFill();
    return graphic;
}

for (let i = 0; i 



在这种情况下,如果所有 100 个 Graphics 对象共享相同的宽度和高度,我们可以通过重用几何体进行优化。

Maximising Performance: A Deep Dive into PixiJS Optimization

传递 GraphicsGeometry 作为参考

为圆创建单个几何图形并重复使用它:

// Create a single geometry for a circle
const circleGeometry = new PIXI.Graphics();
circleGeometry.beginFill(0xDE3249);
circleGeometry.drawCircle(0, 0, 10); // Draw a circle at the origin
circleGeometry.endFill();
// Function to create a graphic using the circle geometry
function createCircle(x, y) {
    const circle = new PIXI.Graphics(circleGeometry.geometry);
    circle.x = x;
    circle.y = y;
    return circle;
}
// Create 100 circles using the same geometry
for (let i = 0; i 



此方法通过引用相同的几何图形而不是为每个对象重复它,显着减少了内存使用量。

Maximising Performance: A Deep Dive into PixiJS Optimization

在一个图形对象中绘制所有内容

对于静态图形或复杂结构,在单个 Graphics 对象中绘制所有元素是另一种优化技术:

const graphics = new PIXI.Graphics();
// Draw 100 circles using the same PIXI.Graphics instance
for (let i = 0; i 



在这种方法中,我们不是创建新的 Graphics 对象,而是将新的几何图形添加到单个 Graphics 实例的 GeometryList 中。此方法对于更复杂的图形结构特别有效。

Maximising Performance: A Deep Dive into PixiJS Optimization


在 PixiJS 中利用 CacheAsBitmap 的强大功能

PixiJS 中最强大的功能之一是 CacheAsBitmap。本质上,它让引擎像精灵一样处理图形。在某些情况下,这可以显着提高性能。

  • 仅当对象不经常更新时才使用 CacheAsBitmap。

  • 大批量的图形可以作为位图缓存在容器中。 pixi 将拍摄快照并将其预渲染为位图,而不是重新渲染 100 个图形。

  • 始终考虑内存使用情况,缓存位图正在使用大量内存。

何时使用 CacheAsBitmap

应该明智地使用cacheAsBitmap。当应用于很少需要更新的对象时,它将是最有效的。例如,如果碰巧有数千个静态或只有很少变化的图形,则将它们缓存为位图可以从根本上减少渲染开销。

PixiJS 可以拍摄这些图形的“快照”并将它们渲染为单个位图,而不是重新渲染 100 个单独的图形。您可以这样实施:

const graphicsContainer = new PIXI.Container();
// Add your graphics to the container
// ...
// Cache the entire container as a bitmap
graphicsContainer.cacheAsBitmap = true;

内存使用注意事项

但是,注意内存使用情况很重要。缓存的位图可能会消耗大量内存。因此,虽然cacheAsBitmap可以大大减少渲染负载,但它会使用更多的内存来进行权衡。应根据应用程序的具体需求和限制仔细考虑这种权衡。

总之,cacheAsBitmap 是 PixiJS 中优化性能的有效工具,特别是对于静态或很少更新的图形。它通过将复杂图形视为单个位图来简化渲染,但必须平衡这一点与内存占用的影响。

为什么 PixiJS 中的精灵通常比图形更有效

当谈到 PixiJS 的内存效率时,精灵通常比图形更具优势。当处理共享相同形状或纹理的多个对象时,这一点尤其明显。让我们回顾一下创建 100 个圆形图形的示例,但这次使用精灵。

从单个纹理创建精灵

首先,我们根据单个圆形图形的几何形状创建纹理:

const circleGraphic = new PIXI.Graphics();
circleGraphic.beginFill(0xDE3249);
circleGraphic.drawCircle(0, 0, 10);
circleGraphic.endFill();
// Generate a texture from the graphic
const circleTexture = app.renderer.generateTexture(circleGraphic);
Next, we use this texture to create sprites:
// Function to create a sprite using the circle texture
function createCircleSprite(x, y) {
    const sprite = new PIXI.Sprite(circleTexture);
    sprite.x = x;
    sprite.y = y;
    return sprite;
}

// Create and add 100 circle sprites to the stage
for (let i = 0; i 



在这种方法中,我们不是重新渲染图形并管理每个对象不断增长的几何列表,而是创建一个纹理并在多个精灵中重用它。这显着减少了渲染负载和内存使用。

局限性和创造性解决方案

此方法的一个限制是您受到所创建的纹理的限制。然而,这就是创造力变得关键的地方。您可以使用 PIXI.Graphics 生成各种形状的纹理并将它们应用到 Sprites。一种特别有效的方法是创建一个基本纹理(例如 1x1 像素位图),并将其重用于所有矩形精灵。通过将精灵大小调整为不同的尺寸,您可以在多个精灵中利用相同的基本纹理,而无需冗余。
例如:

// This creates a 16x16 white texture
const baseTexture = PIXI.Texture.WHITE;

// Use this baseTexture for all rectangular shapes
const sprite= new PIXI.Sprite(baseTexture);
sprite.tint = 0xDE3249; // Set the sprite color
sprite.position.set(x, y);
sprite.width = width;
sprite.height = height;

Maximising Performance: A Deep Dive into PixiJS Optimization

使用此方法,.tint() 允许您在不触发完全重新渲染的情况下为精灵着色,因为色调将作为附加着色器效果直接应用在 GPU 上。

在粒子容器中使用 100k 精灵

为了说明此技术的威力,想象一下运行 100,000 个具有随机色调的单独精灵,每个精灵在每一帧上进行变换,同时保持平滑的 60 FPS。

Maximising Performance: A Deep Dive into PixiJS Optimization

Maximising Performance: A Deep Dive into PixiJS Optimization

为了进一步阅读有关优化 PixiJS 的内容,我强烈推荐 PixiJS 的原始创建者之一撰写的一篇富有洞察力的文章,该文章深入研究了 renderTexture 技术。 

你可以在这里找到它

哇! 如果您已经做到了这一步,我真诚地感谢您在深入研究 PixiJS 优化过程中与我一起坚持。我希望您发现这里分享的见解和技术对您的项目有价值。请继续关注我的下一篇文章,我将更详细地探讨实体组件系统 (ECS) 方法和 NativeArrays 的强大功能。这些方法将使您的 PixiJS 应用程序的性能和效率达到新的高度。感谢您的阅读,我们下一篇再见!

版本声明 本文转载于:https://dev.to/recursivevoid/maximising-performance-a-deep-dive-into-pixijs-optimization-4i0g?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 在JavaScript中如何并发运行异步操作并正确处理错误?
    在JavaScript中如何并发运行异步操作并正确处理错误?
    同意操作execution 在执行asynchronous操作时,相关的代码段落会遇到一个问题,当执行asynchronous操作:此实现在启动下一个操作之前依次等待每个操作的完成。要启用并发执行,需要进行修改的方法。 第一个解决方案试图通过获得每个操作的承诺来解决此问题,然后单独等待它们: co...
    编程 发布于2025-05-11
  • 如何在JavaScript对象中动态设置键?
    如何在JavaScript对象中动态设置键?
    在尝试为JavaScript对象创建动态键时,如何使用此Syntax jsObj['key' i] = 'example' 1;不工作。正确的方法采用方括号: jsobj ['key''i] ='example'1; 在JavaScript中,数组是一...
    编程 发布于2025-05-11
  • 如何在Java的全屏独家模式下处理用户输入?
    如何在Java的全屏独家模式下处理用户输入?
    Handling User Input in Full Screen Exclusive Mode in JavaIntroductionWhen running a Java application in full screen exclusive mode, the usual event ha...
    编程 发布于2025-05-11
  • 如何同步迭代并从PHP中的两个等级阵列打印值?
    如何同步迭代并从PHP中的两个等级阵列打印值?
    同步的迭代和打印值来自相同大小的两个数组使用两个数组相等大小的selectbox时,一个包含country代码的数组,另一个包含乡村代码,另一个包含其相应名称的数组,可能会因不当提供了exply for for for the uncore for the forsion for for ytry...
    编程 发布于2025-05-11
  • 同实例无需转储复制MySQL数据库方法
    同实例无需转储复制MySQL数据库方法
    在同一实例上复制一个MySQL数据库而无需转储在同一mySQL实例上复制数据库,而无需创建InterMediate sqql script。以下方法为传统的转储和IMPORT过程提供了更简单的替代方法。 直接管道数据 MySQL手动概述了一种允许将mysqldump直接输出到MySQL clie...
    编程 发布于2025-05-11
  • 为什么我会收到MySQL错误#1089:错误的前缀密钥?
    为什么我会收到MySQL错误#1089:错误的前缀密钥?
    mySQL错误#1089:错误的前缀键错误descript [#1089-不正确的前缀键在尝试在表中创建一个prefix键时会出现。前缀键旨在索引字符串列的特定前缀长度长度,可以更快地搜索这些前缀。了解prefix keys `这将在整个Movie_ID列上创建标准主键。主密钥对于唯一识别...
    编程 发布于2025-05-11
  • Java中如何使用观察者模式实现自定义事件?
    Java中如何使用观察者模式实现自定义事件?
    在Java 中创建自定义事件的自定义事件在许多编程场景中都是无关紧要的,使组件能够基于特定的触发器相互通信。本文旨在解决以下内容:问题语句我们如何在Java中实现自定义事件以促进基于特定事件的对象之间的交互,定义了管理订阅者的类界面。以下代码片段演示了如何使用观察者模式创建自定义事件: args)...
    编程 发布于2025-05-11
  • Python中何时用"try"而非"if"检测变量值?
    Python中何时用"try"而非"if"检测变量值?
    使用“ try“ vs.” if”来测试python 在python中的变量值,在某些情况下,您可能需要在处理之前检查变量是否具有值。在使用“如果”或“ try”构建体之间决定。“ if” constructs 结果= function() 如果结果: 对于结果: #处理项...
    编程 发布于2025-05-11
  • 如何使用node-mysql在单个查询中执行多个SQL语句?
    如何使用node-mysql在单个查询中执行多个SQL语句?
    Multi-Statement Query Support in Node-MySQLIn Node.js, the question arises when executing multiple SQL statements in a single query using the node-mys...
    编程 发布于2025-05-11
  • 如何正确使用与PDO参数的查询一样?
    如何正确使用与PDO参数的查询一样?
    在pdo 中使用类似QUERIES在PDO中的Queries时,您可能会遇到类似疑问中描述的问题:此查询也可能不会返回结果,即使$ var1和$ var2包含有效的搜索词。错误在于不正确包含%符号。通过将变量包含在$ params数组中的%符号中,您确保将%字符正确替换到查询中。没有此修改,PDO...
    编程 发布于2025-05-11
  • 如何解决AppEngine中“无法猜测文件类型,使用application/octet-stream...”错误?
    如何解决AppEngine中“无法猜测文件类型,使用application/octet-stream...”错误?
    appEngine静态文件mime type override ,静态文件处理程序有时可以覆盖正确的mime类型,在错误消息中导致错误消息:“无法猜测mimeType for for file for file for [File]。 application/application/octet...
    编程 发布于2025-05-11
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php $xml = simplexml_load_file($file); foreach ($xml->Var[0]->attributes() as $attributeName => $attributeValue) { echo $attributeName,...
    编程 发布于2025-05-11
  • 如何使用Depimal.parse()中的指数表示法中的数字?
    如何使用Depimal.parse()中的指数表示法中的数字?
    在尝试使用Decimal.parse(“ 1.2345e-02”中的指数符号表示法表示的字符串时,您可能会遇到错误。这是因为默认解析方法无法识别指数符号。 成功解析这样的字符串,您需要明确指定它代表浮点数。您可以使用numbersTyles.Float样式进行此操作,如下所示:[&& && && ...
    编程 发布于2025-05-11
  • Async Void vs. Async Task在ASP.NET中:为什么Async Void方法有时会抛出异常?
    Async Void vs. Async Task在ASP.NET中:为什么Async Void方法有时会抛出异常?
    在ASP.NET async void void async void void void void void的设计无需返回asynchroncon而无需返回任务对象。他们在执行过程中增加未偿还操作的计数,并在完成后减少。在某些情况下,这种行为可能是有益的,例如未期望或明确预期操作结果的火灾和...
    编程 发布于2025-05-11
  • 如何从2D数组中提取元素?使用另一数组的索引
    如何从2D数组中提取元素?使用另一数组的索引
    Using NumPy Array as Indices for the 2nd Dimension of Another ArrayTo extract specific elements from a 2D array based on indices provided by a second ...
    编程 发布于2025-05-11

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3