在 JavaScript 中渲染灯光太慢了。任何想法?

Rendering lights in JavaScript so slow. Any idea?

我有一个JavaScript写的光渲染方法,可以设置每个物体的亮度。如果我在主循环中使用此过滤器调用渲染对象,它将非常慢(8-10 fps)。 JavaScript 是不是太多了,或者只是一个未优化的解决方案?

如果我在主循环之外调用它就没问题。

这里是 Animator class,其中有我写的那个方法。您应该使用 brightness 开关调用 renderImage 方法。

Animator: { // Animator class 
  renderImage: function(ImageObject,imageX,imageY,filterData = []) {
  /*
    filterData [] =
    0 -> type
    1 -> value for filtering
  */
  switch( filterData[0] )
  {
    case 'none':
      engComponents.ctx.drawImage(ImageObject,imageX,imageY);
      break;

    case 'brightness':
      engComponents.ctx.drawImage(ImageObject,imageX,imageY);
      pixels = engComponents.ctx.getImageData(imageX,imageY,imageX+50,imageY+50);
      data = pixels.data;
      for (var i=0; i<data.length; i+=4) {
        data[i] += filterData[1];
        data[i+1] += filterData[1];
        data[i+2] += filterData[1];
      }
      engComponents.ctx.putImageData(pixels,imageX,imageY);
      break;
  }

感谢您的帮助!

get/putImageData 是相当慢的操作。如果您还需要为每个对象执行此操作,那么期望实时性能更新可能有点过分。完全可以使用编译代码和直接访问显示卡,但是 JavaScript 和 canvas 包括它附带的所有故障保险,我们被迫陷入诡计。

您可以(可能应该)做的一件事是在游戏开始前缓存相关 images/objects 的变体。定义一些关键的亮度值并生成它们的 sprite-sheet。然后使用亮度索引而不是实际亮度,因此您只需将具有预期亮度的单元格绘制到屏幕上,而不是在每一帧期间进行计算。

让我们以这个人为例:

现在生成具有各种亮度值的精灵 sheet - 在 Photoshop 中或在代码中动态执行此操作(后者在下面的演示中显示):

var sctx = document.getElementById("sprite").getContext("2d"),
    ctx = document.getElementById("main").getContext("2d"),
    img = new Image;
    
img.crossOrigin = "";
img.onload = genSprite;
img.src = "http://i.imgur.com/RV2a28T.png";  // 106x120

function genSprite() {

  sctx.canvas.width = 5 * this.width;          // set sprite size: image width x cells
  sctx.canvas.height = this.width;
  sctx.drawImage(this, 0, 0);                  // draw in image to brighten
  
  
  // generate some brightness cells
  var bStep = 25,                              // brightness step per cell
      max = 5,                                 // max sprites
      idata = sctx.getImageData(0, 0, this.width, this.height), // get once only
      data = idata.data,                       
      len = data.length;
  
  for(var i = 0; i < max; i++) {               // iterate to increase values
    for(var p = 0; p < len; p++) {
      data[p++] += bStep;
      data[p++] += bStep;
      data[p++] += bStep;
    }
    sctx.putImageData(idata, this.width * i, 0); // update at cell pos. with new values
  }
  
  // now we have our sprite-sheet, release the Kraken!
  game();
}

/*==========================================================
This part is just to demonstrate you can draw many instances
with varying brightness without suffer from low frame-rate
==========================================================*/

function game() {

  var dudes = [], // holds Dude objects
      max = 70,   // num. of dudes
      i = 0;
  
  // create game dudes
  while(i++ < max) dudes.push(new Dude(sctx.canvas, ctx));
  
  // animate
  (function loop() {
    ctx.clearRect(0, 0, 500, 500);
    var i = 0;
    while(dude = dudes[i++]) dude.update();  // update dude
    requestAnimationFrame(loop)
  })();
  
}

function Dude(sprite, ctx) {
  var b = (4 * Math.random())|0,   // brightness index (fractional is ok)
      db = 0.25,                   // step for brightness, we round it to integer later
      w = ctx.canvas.width,        // cache some values
      h = ctx.canvas.height,
      x = w * 0.5,                 // center of canvas
      y = h * 0.5,
      v = 1 + 4 * Math.random(),   // random velocity
      a = Math.PI*2*Math.random(), // random angle
      dx = Math.cos(a) * v,        // steps based on angle
      dy = Math.sin(a) * v;
  
  // updates position and brightness cell
  this.update = function() {
    
    // position:
    x += dx;
    y += dy;
    if (x < -106 || x > w || y < -120 || y > h) {
      x = w * 0.5;
      y = h * 0.5;
    }
    
    // brightness:
    b += db;
    if (b <= 0 || b >= 4) db = -db;

    // clip the cell from sprite-sheet and draw to main canvas
    // Note the 106*(b|0) - (b|0) will force integer number which we need.
    ctx.drawImage(sprite, 106*(b|0), 0, 102, sprite.height,  x, y, 106, sprite.height);
  };
}
#sprite {border:1px solid #000;margin-bottom:4px;}
<canvas id="sprite"></canvas><br>
<canvas id="main" width=500 height=500></canvas>