8

我想在我正在制作的游戏的背景上放置多个光源,这与一个光源配合得很好,如下所示:

在此处输入图像描述

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

在此处输入图像描述

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

在此处输入图像描述

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

这些图像只是示例,每一帧都会有更多的内容可以使用 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);
4

3 回答 3

5

另一种使用光的方法是使用 globalCompositeOperation 模式 'ligther' 来照亮事物,而只使用 globalAlpha 来使事物变暗。

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

让我们玩蜡烛和灯光吧

所以我是怎么做的:

变暗:
- 选择变暗的颜色(最有可能是黑色,但您可以选择红色或其他颜色来修饰结果)。
- 选择不透明度(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 更改为“lighter”
- 您也可以更改不透明度,以更好地控制照明。
- fillRect 或圆弧你想要变亮的区域。

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

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();
}

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

Rq 2 :您可以使用剪辑来防止屏幕的某些部分变亮(如果有障碍物)。我在示例中添加了蓝色圆圈来显示这一点。

所以你可能想用这些效果直接照亮你的场景,或者在屏幕上绘制图像之前单独创建一个你想要变暗/变亮的光层。

这里有太多的场景来讨论它们(灯光动画与否,剪辑与否,预计算灯光图层与否,......)但就速度而言,对于 Safari 和 iOS safari,使用 rect 的解决方案/arc 绘制 - 使用渐变或实心填充 - 将比绘制图像/画布更快。
在 Chrome 上则完全相反:当几何数增加时,绘制图像比绘制每个几何图形要快。
Firefox 在这方面与 Chrome 非常相似。

于 2013-11-05T17:34:33.470 回答
2
  1. 你的 png 应该有完全透明的角落,而不是中间的透明白色。
  2. 或者你可以画这个,但不能像这里jsfiddle.net/pr9r7/2/那样逐像素绘制

更多示例:jsfiddle.net/pr9r7/3/ http://codepen.io/cwolves/pen/prvnb

于 2013-11-05T11:27:52.703 回答
0

这是我的看法:

A. 在您尝试之前不要担心性能。Canvas 的绘图速度非常快。

B.而不是具有暗角和透明中间的图像。你为什么不试着让它更“IRL”,让整个世界更黑暗,让光源照亮这个区域?突出一个小区域,而不是使除小区域之外的所有内容变暗。

于 2013-11-05T11:33:37.433 回答