我想在我正在制作的游戏的背景上放置一些光源,这对于一个光源很有效,如下所示:
这是通过将.png图像放置在对中心更加透明的所有其他内容上来实现的,如下所示:
适用于一个光源,但我需要另一种方法,我可以添加更多光源并移动光源。
我已经考虑为每个帧逐个像素地绘制类似的“阴影层”,并根据到每个光源的距离计算透明度。但是,这可能会非常缓慢,我确信有更好的解决方案可以解决这个问题。
图片只是示例,每个框架都有更多内容可供使用requestAnimationFrame移动和更新。
是否有轻量且简单的方法来实现这一目标?提前谢谢!
修改
在ViliusL的帮助下,我想出了这个屏蔽解决方案:
// 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);
答案 0 :(得分: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更改为“更轻”
- 你也可以改变不透明度,以便更好地控制闪电
- 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)
更多示例:jsfiddle.net/pr9r7/3/ http://codepen.io/cwolves/pen/prvnb
答案 2 :(得分:0)
这是我的承担:
A. 在您试用之前不要担心性能。 Canvas在绘图时非常快。
B。而不是拥有暗角和透明中间的图像。你为什么不试着让它更“IRL”,让整个世界更加黑暗,让光源照亮区域?突出显示一个小区域,而不是使一切都变暗,除了小区域。