You need to enable JavaScript to run this app. PIXI中同baseTexture不同裁剪创建4万精灵帧率过低优化咨询
优惠活动
大模型
产品
解决方案
定价
更多

PIXI中同baseTexture不同裁剪创建4万精灵帧率过低优化咨询

问题描述

我在PIXI中绘制了一个圆形,将其生成为纹理后,基于同一个baseTexture创建40000个设置了不同裁剪区域的精灵。当精灵数量为20000时帧率约为60fps,提升至40000时帧率仅为35fps,请问是否有更优的方案实现使用相同纹理但不同裁剪的需求?

原代码示例

JS逻辑代码

window.onload = () => {

const app = new PIXI.Application({ width: 800, height: 800 });
const stats = new Stats();
stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
stats.domElement.style.position = 'absolute';
stats.domElement.style.right = '50%';
stats.domElement.style.top = '0px';
document.body.appendChild(stats.domElement);
// stats = addStats(document, app);
document.body.appendChild(app.view);

const lineWidth = 40;
const numX = 200;
const numY = 200;

const width = 600;
const height = 600;

const stepX = width / numX;
const stepY = height / numY;

const circleGraphics = new PIXI.Graphics();
circleGraphics.lineStyle(lineWidth, 0xffffff);
circleGraphics.drawCircle(400, 400, 300 - lineWidth * 0.5);
const circleTexture = app.renderer.generateTexture(circleGraphics);

let array = [];
for (let y = 0; y < numY; y++) {
  array[y] = [];
  for (let x = 0; x < numX; x++) {
    const texture = new PIXI.Texture(circleTexture.baseTexture);
    texture.frame = new PIXI.Rectangle(stepX * x, stepY * y, stepX, stepY);
    const sprite = new PIXI.Sprite.from(texture);
    app.stage.addChild(sprite);
    sprite.x = x * stepX + 100;
    sprite.y = y * stepY + 100;
    sprite._x = sprite.x;
    sprite._y = sprite.y;
    array[y][x] = sprite;
  }
}

let c = 0;

app.ticker.add(() => {
  for (let y = 0; y < numY; y++) {
    for (let x = 0; x < numX; x++) {
      const item = array[y][x];
      item.x = item._x + Math.sin(c) * 100 * (x / numX);
      item.y = item._y + Math.cos(c) * 120 * (y / numY);
    }
  }
  c += 0.1;
  stats.update();
});
}

依赖引入代码

<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.1.3/pixi.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r16/Stats.min.js"></script>
优化方案
  • 替换普通容器为PIXI.ParticleContainer:普通Container每个精灵会单独触发一次绘制提交,4万次draw call是帧率下降的核心原因。ParticleContainer针对同纹理的大量精灵做了合批优化,最多只需要1次draw call就能提交所有精灵的渲染,且当前场景仅需要修改精灵位置,完全满足ParticleContainer的使用限制。
  • 预创建裁剪纹理复用:原代码中每个精灵都单独创建Texture和Rectangle实例,4万个重复对象会带来额外的内存和初始化开销,可以提前把所有网格对应的裁剪纹理生成好,不需要重复实例化。
  • 优化遍历逻辑:把存储精灵的二维数组改为一维数组,减少循环中的寻址开销;也可以直接在创建精灵时记录基准坐标和比例系数,不需要每次循环重复计算比值,进一步降低CPU开销。
  • 进阶可选:使用PIXI.Instances实例化渲染:如果需要更高性能,可以直接用GPU实例化渲染,把所有精灵的位置、UV裁剪信息存在顶点缓冲区,一次提交给GPU渲染,CPU开销会降到最低,4万实例可以轻松跑到60fps以上。

优化后代码示例

window.onload = () => {
  const app = new PIXI.Application({ width: 800, height: 800 });
  const stats = new Stats();
  stats.showPanel(0);
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.right = '50%';
  stats.domElement.style.top = '0px';
  document.body.appendChild(stats.domElement);
  document.body.appendChild(app.view);

  const lineWidth = 40;
  const numX = 200;
  const numY = 200;
  const total = numX * numY;
  const width = 600;
  const height = 600;
  const stepX = width / numX;
  const stepY = height / numY;

  // 生成基础纹理
  const circleGraphics = new PIXI.Graphics();
  circleGraphics.lineStyle(lineWidth, 0xffffff);
  circleGraphics.drawCircle(400, 400, 300 - lineWidth * 0.5);
  const circleBaseTexture = app.renderer.generateTexture(circleGraphics).baseTexture;

  // 预生成所有裁剪纹理
  const textures = [];
  for (let y = 0; y < numY; y++) {
    for (let x = 0; x < numX; x++) {
      const tex = new PIXI.Texture(circleBaseTexture, new PIXI.Rectangle(stepX * x, stepY * y, stepX, stepY));
      textures.push(tex);
    }
  }

  // 使用ParticleContainer,仅开启需要的位置属性
  const container = new PIXI.ParticleContainer(total, {
    position: true,
    rotation: false,
    scale: false,
    uvs: false,
    tint: false
  });
  app.stage.addChild(container);

  // 预创建所有精灵,存入一维数组
  const sprites = new Array(total);
  let index = 0;
  for (let y = 0; y < numY; y++) {
    for (let x = 0; x < numX; x++) {
      const sprite = new PIXI.Sprite(textures[index]);
      sprite._baseX = x * stepX + 100;
      sprite._baseY = y * stepY + 100;
      sprite._ratioX = x / numX;
      sprite._ratioY = y / numY;
      sprite.x = sprite._baseX;
      sprite.y = sprite._baseY;
      sprites[index] = sprite;
      container.addChild(sprite);
      index++;
    }
  }

  let c = 0;
  app.ticker.add(() => {
    const sinC = Math.sin(c);
    const cosC = Math.cos(c);
    // 一维数组遍历,性能更高
    for (let i = 0; i < total; i++) {
      const item = sprites[i];
      item.x = item._baseX + sinC * 100 * item._ratioX;
      item.y = item._baseY + cosC * 120 * item._ratioY;
    }
    c += 0.1;
    stats.update();
  });
}

内容的提问来源于stack exchange,提问作者lasse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.15 01:15:00