画布上有多个光源

时间:2013-11-05 10:51:32

标签: javascript canvas effects

我想在我正在制作的游戏的背景上放置一些光源,这对于一个光源很有效,如下所示:

enter image description here

这是通过将.png图像放置在对中心更加透明的所有其他内容上来实现的,如下所示:

enter image description here

适用于一个光源,但我需要另一种方法,我可以添加更多光源并移动光源。

enter image description here

我已经考虑为每个帧逐个像素地绘制类似的“阴影层”,并根据到每个光源的距离计算透明度。但是,这可能会非常缓慢,我确信有更好的解决方案可以解决这个问题。

图片只是示例,每个框架都有更多内容可供使用requestAnimationFrame移动和更新。

是否有轻量且简单的方法来实现这一目标?提前谢谢!

修改

在ViliusL的帮助下,我想出了这个屏蔽解决方案:

http://jsfiddle.net/CuC5w/1/

// Create canvas
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 300;
document.body.appendChild(canvas);

// Draw background
var img=document.getElementById("cat");
ctx.drawImage(img,0,0);

// Create shadow canvas
var shadowCanvas = document.createElement('canvas');
var shadowCtx = shadowCanvas.getContext('2d');
shadowCanvas.width = canvas.width;
shadowCanvas.height = canvas.height;
document.body.appendChild(shadowCanvas);

// Make it black
shadowCtx.fillStyle= '#000';
shadowCtx.fillRect(0,0,canvas.width,canvas.height);

// Turn canvas into mask
shadowCtx.globalCompositeOperation = "destination-out";

// RadialGradient as light source #1
gradient = shadowCtx.createRadialGradient(80, 150, 0, 80, 150, 50);
gradient.addColorStop(0, "rgba(255, 255, 255, 1.0)");
gradient.addColorStop(1, "rgba(255, 255, 255, .1)");
shadowCtx.fillStyle = gradient;
shadowCtx.fillRect(0, 0, canvas.width, canvas.height);

// RadialGradient as light source #2
gradient = shadowCtx.createRadialGradient(220, 150, 0, 220, 150, 50);
gradient.addColorStop(0, "rgba(255, 255, 255, 1.0)");
gradient.addColorStop(1, "rgba(255, 255, 255, .1)");
shadowCtx.fillStyle = gradient;
shadowCtx.fillRect(0, 0, canvas.width, canvas.height);

3 个答案:

答案 0 :(得分:5)

另一种玩光的方法是使用globalCompositeOperation模式'ligther'来连接事物,并使用globalAlpha来暗示事物。

首先是一张图片,左边有一个卡通闪电,右边有一个更逼真的闪电,但你更喜欢看小提琴,因为它是动画的:
http://jsfiddle.net/gamealchemist/ABfVj/

let's play with candles and lights

所以我是怎么做的:

变暗:
- 选择变暗的颜色(很可能是黑色,但您可以选择红色或其他颜色来模糊结果)   - 选择不透明度(0.3似乎是一个很好的起始值) - fillRect你要变暗的区域。

function darken(x, y, w, h, darkenColor, amount) {
    ctx.fillStyle = darkenColor;
    ctx.globalAlpha = amount;
    ctx.fillRect(x, y, w, h);
    ctx.globalAlpha = 1;
}

减轻:
- 选择淡淡的颜色。请注意,此颜色的r,g,b将被添加到前一个点的r,g,b:如果使用较高的值,则颜色会被烧焦。
- 将globalCompositeOperation更改为“更轻” - 你也可以改变不透明度,以便更好地控制闪电 - fillRect或arc想要减轻的区域。

如果在较亮模式下绘制多个圆圈,结果会相加,因此您可以选择一个非常低的值并绘制几个圆圈。

function ligthen(x, y, radius, color) {
    ctx.save();
    var rnd = 0.03 * Math.sin(1.1 * Date.now() / 1000);
    radius = radius * (1 + rnd);
    ctx.globalCompositeOperation = 'lighter';
    ctx.fillStyle = '#0B0B00';
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * π);
    ctx.fill();
    ctx.fillStyle = color;
    ctx.beginPath();
    ctx.arc(x, y, radius * 0.90+rnd, 0, 2 * π);
    ctx.fill();
    ctx.beginPath();
    ctx.arc(x, y, radius * 0.4+rnd, 0, 2 * π);
    ctx.fill();
    ctx.restore();
}

请注意,我添加了一个正弦变化,使光线更加生动。

Ligthen:另一种方式:
您还可以在使用“ligther”模式的同时,使用渐变来获得更平滑的效果(第一个更像卡通,除非您画了很多圆圈。)。

function ligthenGradient(x, y, radius) {
    ctx.save();
    ctx.globalCompositeOperation = 'lighter';
    var rnd = 0.05 * Math.sin(1.1 * Date.now() / 1000);
    radius = radius * (1 + rnd);
    var radialGradient = ctx.createRadialGradient(x, y, 0, x, y, radius);
    radialGradient.addColorStop(0.0, '#BB9');
    radialGradient.addColorStop(0.2 + rnd, '#AA8');
    radialGradient.addColorStop(0.7 + rnd, '#330');
    radialGradient.addColorStop(0.90, '#110');
    radialGradient.addColorStop(1, '#000');
    ctx.fillStyle = radialGradient;
    ctx.beginPath();
    ctx.arc(x, y, radius, 0, 2 * π);
    ctx.fill();
    ctx.restore();
}

我还在这里添加了一个罪恶变异 Rq:在每次绘制时创建渐变会产生垃圾:如果使用单个渐变,则存储渐变;如果要为渐变设置动画,则将它们存储在数组中。
如果您在多个位置使用相同的灯光,则构建一个渐变,以(0,0)为中心,并在使用此单个渐变进行绘制之前平移画布。

Rq 2:你可以使用剪裁来防止屏幕的某些部分被照亮(如果有障碍物)。 我在我的例子中添加了蓝色圆圈来表明这一点。

因此,您可能希望直接将场景与这些效果相关联,或者在屏幕上绘制之前单独创建一个您想要变暗/变亮的光层。

有太多的场景要讨论它们(光动画与否,剪裁与否,预先计算光照层,等等),但就速度而言,对于Safari和iOS游猎,使用矩形/弧形绘制的解决方案 - 使用渐变或实心填充 - 将比绘制图像/画布更快 在Chrome上,它将完全相反:绘制图像比在几何计数增加时绘制每个几何图形更快。
Firefox与此非常相似。

答案 1 :(得分:2)

  1. 你的png应该有完整的透明角落,中间不是透明的白色。
  2. 或者你可以画这个,但不是像这里一样的像素jsfiddle.net/pr9r7/2 /
  3. 更多示例:jsfiddle.net/pr9r7/3/ http://codepen.io/cwolves/pen/prvnb

答案 2 :(得分:0)

这是我的承担:

A. 在您试用之前不要担心性能。 Canvas在绘图时非常快。

B。而不是拥有暗角和透明中间的图像。你为什么不试着让它更“IRL”,让整个世界更加黑暗,让光源照亮区域?突出显示一个小区域,而不是使一切都变暗,除了小区域。