1
\$\begingroup\$

I am implementing lighting in a tile engine, and I need a way to darken tiles, based on their light level.

For the ground tiles, I can clear the context to black, then set the globalAlpha and use drawImage normally. But what about foreground tiles, such as trees? I don't want them to be translucent, just darker. Is there any way to filter the output of drawImage?

\$\endgroup\$
1

2 Answers 2

1
\$\begingroup\$

I figured it out. My solution was to use a "light mask" - an image with a progression of tiles of varying translucency. The darkening is achieved by simply rendering from the light mask over the finished scene.

http://jsfiddle.net/kX58L/

\$\endgroup\$
1
  • \$\begingroup\$ Note that answers that reference transient external resources should recap the information in them to avoid the inevitable dangling links that render the answer near-useless. \$\endgroup\$ Commented Nov 8, 2014 at 18:21
0
\$\begingroup\$

When you want to have full control over the color values of an image, you can draw it to an off-screen canvas like this (NOTE: This code assumes the image is already loaded!)

    var offscreenCanvas = document.createElement('canvas');
    offscreenCanvas.width = image.width;
    offscreenCanvas.height = image.height;
    var context = offscreenCanvas.getContext('2d');
    context.drawImage(image, 0, 0);

You can then use context.getImageData to get an array of raw RGBA values. You can then manipulate these color values in any way you desire and write them back with context.putImageData.

// get raw pixel values
var imageData = context.getImageData(0, 0, image.width, image.height);
var pixels = imageData.data;
// modify each pixel
for(var i = 0; i < pixels.length; i += 4) {
   // red is pixels[i];
   // green is pixels[i + 1];
   // blue is pixels[i + 2];
   // alpha is pixels[i + 3];
   // all values are integers between 0 and 255
   // do with them whatever you like. Here we are reducing the color volume to 75%
   // without affecting the alpha channel
   pixels[i] = pixels[i] * 0.75;
   pixels[i+1] = pixels[i+1] * 0.75;
   pixels[i+2] = pixels[i+2] * 0.75;
}
// write modified pixels back to canvas
context.putImageData(imageData, 0, 0);

When you want to draw the modified image to the main canvas, you can use the offscreen canvas as a source parameter just like any normal image.

\$\endgroup\$
4
  • \$\begingroup\$ That's a good and simple technique, but I fear it would be too slow to be done on a per-tile basis. \$\endgroup\$
    – alekop
    Commented May 12, 2014 at 18:22
  • \$\begingroup\$ @alekop Keep in mind that you only need to do it once on initialization per tileset and light level because you can keep the offscreen canvas with the modified image around and draw it again and again. It's a simple memory/cpu tradeoff which is likely much faster than your method which requires two drawing-passes per frame. \$\endgroup\$
    – Philipp
    Commented May 12, 2014 at 19:29
  • \$\begingroup\$ Yeah, it would be faster, but the trouble is, I have 16 light levels, and I am considering going up because the difference from one tile to the next is still rather noticable. That would mean increasing the memory consumption by 16x! I guess I could give this technique a try, it just feels like it wouldn't be fast enough. \$\endgroup\$
    – alekop
    Commented May 12, 2014 at 23:39
  • \$\begingroup\$ @alekop When you are planning to write an advanced lighting engine, especially when you want mobile light sources, you might want to look into WebGL and implement it as a pixel shader. \$\endgroup\$
    – Philipp
    Commented May 13, 2014 at 10:46

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .