【问题标题】:Rendering lights in JavaScript so slow. Any idea?在 JavaScript 中渲染灯光太慢了。任何想法?
【发布时间】:2015-03-15 12:03:34
【问题描述】:

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

如果我在主循环之外调用它就可以了。

这是Animator 类,它有我写的那个方法。您应该使用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;
  }

感谢您的帮助!

【问题讨论】:

    标签: javascript canvas rendering frame-rate light


    【解决方案1】:

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

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

    让我们以这个人为例:

    现在生成具有各种亮度值的精灵表 - 在 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>

    【讨论】:

    • 非常感谢您的帮助!所以要澄清。加载精灵,制作变体,缓存每个变体的imageData,直接用imageData渲染?
    • @LászlóLukács 没问题。几乎,除了最后一步,只需将新的精灵画布用作 drawImage() 的图像(之后无需使用 getImageData 中的 ImageData)。
    • 我做到了,但我还有一个问题。当我第一次在 Firefox 中打开代码时,它还可以。但是当我按 ctrl+f5 时,它不会显示变亮的图片。 Internet Explorer 只显示一些图片,Chrome 不显示任何内容,除非我在链接上按 Enter 键。我正在使用一个名为 pxLoader 的预加载精灵的插件,但没有帮助。你会推荐什么?
    • @LászlóLukács 很好,看看我的 YAIL 加载器是否可以帮助加载图像(尝试 f.ex. LT/light 版本):-)
    • 好吧,我看,你怎么看,这是卸载精灵的结果吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-10
    • 2014-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多