画布 - 在完全透明的所有区域中填充一个矩形
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】:
只是一个想法,但由于您从渐变中获得了相反的效果,您是否尝试过反转渐变?
【讨论】:
以上是关于画布 - 在完全透明的所有区域中填充一个矩形的主要内容,如果未能解决你的问题,请参考以下文章