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

Posted

技术标签:

【中文标题】画布 - 在完全透明的所有区域中填充一个矩形【英文标题】:Canvas - Fill a rectangle in all areas that are fully transparent 【发布时间】:2011-12-16 03:05:19 【问题描述】:

我正在使用 html5 画布编写一个简单的 2D 游戏引擎。我来添加一个照明引擎。每个光源都有一个半径值和一个强度值(0-1,例如 1 会非常亮)。还有一个环境光值,用于照亮世界上不在光源附近的所有其他事物(0-1,例如 0.1 将是月光)。光照过程在主画布上方的单独画布上完成:

    对于每个光源,在该位置绘制一个径向渐变,其半径与光源相同。渐变有两个停止点:中心为黑色,alpha 为 1-光强度,末端/边缘为黑色,alpha 为 1-环境光值。一切正常。 这是出错的地方:/ 我需要用黑色和 1 环境光值的 alpha 填充整个画布,目前我通过将 context.globalCompositeOperation 设置为 source-out 然后填充整个画布来做到这一点帆布。

我的代码是:

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 开发中心的 example 上关于堆肥的内容,以复制我需要做的事情,但似乎没有一种复合模式可以工作,check that out 如果有帮助的话。

非常感谢,任何答案都会很棒:)

【问题讨论】:

【参考方案1】:

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

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

http://jsfiddle.net/HADky/

或者后面有图看:http://jsfiddle.net/HADky/10/

您在这里利用的是这样一个事实,即画布上路径的任何交叉点始终只是联合而不是复合。所以你使用一个渐变画笔来绘制整个东西。

但是,如果有多个光源,那就有点棘手了。我不太确定如何以有效的方式覆盖它,特别是如果您计划让两个光源相交。

您可能应该做的是设计一个 alpha 通道而不是这个覆盖的东西,但我目前想不出一个让它工作的好方法。如果我想到其他任何事情,我会重新审视这个。


编辑:嘿!于是我想了想,想出了一个好的解决方案。

您需要做的是绘制一种 Alpha 通道,其中的黑点标记了您希望光线所在的位置。所以如果你有三个光源,它看起来像这样:

然后您想将填充样式设置为您的环境颜色并将 globalCompositeOperation 设置为 xor 和 xor 整个事物。

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

这将为您留下“相反”的图像,但透明部分将正确地成为环境:

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

http://jsfiddle.net/a2Age/

额外优化:您实际上可以在不使用任何路径的情况下实现效果,只需将完全相同的径向渐变绘制到矩形而不是圆形路径上:

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

【讨论】:

谢谢!我已将其放入我的代码中,并且大部分情况下效果都很好,但似乎下降了when the intensity of a light source is < 1,而且当您开始使用increase the ambient light 时,灯光本身似乎消失得比它们应该的要快得多,就像环境光有更多效果比它应该。我猜这些问题与 XOR 模式有关。无论如何,谢谢,绝对是朝着正确方向迈出的一大步 我希望我能多次投票,这个答案让我很开心。一直在尝试让 AGES 模糊画布形状,但无济于事。尝试导出图像等并将它们模糊。您的解决方案正是我所需要的,但搜索之神不知道ctx.createRadialGradient() 与模糊形状/圆形相同。 很好的答案,你是神【参考方案2】:

只是一个想法,但由于您从渐变中获得了相反的效果,您是否尝试过反转渐变?

【讨论】:

以上是关于画布 - 在完全透明的所有区域中填充一个矩形的主要内容,如果未能解决你的问题,请参考以下文章

重新填充画布擦除区域的区域

画布不透明 - 更改默认背景颜色

如何以百分比计算画布的透明区域?

填充 CGContext 的其余部分,保持一个矩形透明

HTML5画布:用颜色填充透明图像并在顶部绘制

带圆角的画布 clearRect