画布 - 在所有完全透明的区域中填充矩形

6
我正在使用HTML5画布写一个简单的2D游戏引擎。我现在需要添加一个照明引擎。每个光源都有半径值和亮度值(0-1,例如1表示非常亮)。还有一个环境光值,用于照亮世界中没有靠近光源的其他物体(0-1,例如0.1表示月光)。照明过程在主画布上面完成:
  1. 对于每个光源,会在该位置绘制一个具有与光源相同半径的径向渐变。该渐变会给定两个停止点:中心为黑色,透明度为1-光强度,末端/边缘为黑色,透明度为1-环境光值。这一步很顺利。
  2. 这是出错的地方:/ 我需要用透明度为1-环境光值的黑色填充整个画布,目前我通过将context.globalCompositeOperation设置为source-out然后fillRect整个画布来实现。
这部分代码如下:
var amb = 'rgba(0,0,0,' + (1-f.ambientLight) + ')';
for(i in f.entities) {
    var e = f.entities[i], p = f.toScreenPoint(e.position.x, e.position.y), radius = e.light.radius;

    if(radius > 0) {
        var g = cxLighting.createRadialGradient(p.x, p.y, 0, p.x, p.y, radius);
        g.addColorStop(0, 'rgba(0,0,0,' + (1-e.light.intensity) + ')');
        g.addColorStop(1, amb);

        cxLighting.fillStyle = g;
        cxLighting.beginPath();
        cxLighting.arc(p.x, p.y, radius, 0, Math.PI*2, true);
        cxLighting.closePath();
        cxLighting.fill();
    }
}
//Ambient light
cxLighting.globalCompositeOperation = 'source-out';
cxLighting.fillStyle = amb;
cxLighting.fillRect(0, 0, f.width, f.height);
cxLighting.globalCompositeOperation = 'source-over';

然而,与我想要从引擎中获得的内容(左侧)不同,我得到了一种相反的渐变效果(右侧)。我认为这是因为当我使用“source-out”混合模式绘制矩形时,它会影响渐变本身的颜色,因为它们是半透明的。

期望的光照效果 我得到的 有没有更好或不同的方法?也许使用裁剪,或者首先在所有东西上绘制矩形?
另外,我修改了Mozila Dev Centre的示例来复制我需要做的事情,但没有一个混合模式似乎起作用,请查看是否有帮助。
非常感谢,任何答案都很棒 :)
2个回答

15

一种简单的方法是使用imageData,但这会非常缓慢。虽然这是一种选择,但对于游戏引擎来说并不是一个好方法。

另一种方法是将环境光和光源视为一条路径。这样做会非常容易:

http://jsfiddle.net/HADky/

或者使用带有背景图片的方式查看: http://jsfiddle.net/HADky/10/

你在这里利用的是canvas上路径的任何交集始终只是联合而不是复合。所以你正在使用单个渐变画刷来绘制整个图像。

但是,如果有多个光源,情况会变得有些棘手。我不太确定如何以有效的方式解决这个问题,特别是如果您计划让两个光源相交。

相反,您应该考虑设计一个alpha通道,而不是这个覆盖物,但我目前想不出一个好的方法使其正常工作。如果我想到了其他内容,我会重新审视这个问题。


编辑: 嘿!我思考了一下,想出了一个好的解决方案。

你需要绘制一种类似于阿尔法通道的东西,其中黑斑点标记着你想要光亮的地方。所以如果你有三个光源,它会看起来像这样:

enter image description here

然后您需要将填充样式设置为环境颜色,并将globalCompositeOperation设置为xor,然后对整个内容进行异或操作。

ctx.fillStyle = amb;
ctx.globalCompositeOperation = 'xor';
ctx.fillRect(0,0,500,500);

那将会给你留下一个“相反”的图像,除了透明部分会正确地反射环境:

enter image description here

这是代码的一个可工作示例:

http://jsfiddle.net/a2Age/

额外的优化:实际上,您可以通过在矩形上绘制完全相同的径向渐变而不是圆形路径来实现相同的效果:

http://jsfiddle.net/a2Age/2/ 希望对您有所帮助!


1
谢谢!我已经把它放到我的代码中,大部分情况下都很好用。但是当光源的强度小于1时似乎会出现问题(http://jsfiddle.net/ve2jy/),并且当你开始增加环境光时(http://jsfiddle.net/vnurX/),灯光本身似乎比应该更快地消失了,就像环境光的影响比它应该的更大一样。我猜这些问题与异或模式有关。无论如何,感谢您,这绝对是朝着正确方向迈出的一大步。 - jt78
我希望我可以多次点赞,这个答案让我的一天都变得美好了。我已经尝试了很长时间来模糊画布形状,但一直没有成功。我尝试过导出图像等方法来进行模糊处理。你的解决方案正是我所需要的,但搜索引擎并不知道 ctx.createRadialGradient() 就是模糊一个形状/圆形的方法。 - Christopher

0

只是一个想法,但既然你从渐变中得到了与你想要的相反的效果,你尝试过反转渐变吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接