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

时间:2011-10-26 22:53:04

标签: javascript html5 canvas composite clipping

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

  1. 对于每个光源,在该位置绘制径向渐变,其半径与光源相同。渐变给出两个停止:中心是黑色,具有1的光强度的α,并且结束/边缘是黑色,具有1个环境光值的α。一切正常。
  2. 这是它出错的地方:/我需要用整个画布填充黑色和1环境光的alpha值,此时我通过将context.globalCompositeOperation设置为source-out然后fillRecting整个帆布。
  3. 我的代码是:

    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复合操作绘制矩形时,它会影响渐变本身的颜色,因为它们是半透明的。

    Desired lighting effect I get this

    有没有办法以不同或更好的方式做到这一点?可能会使用剪辑,或者先将矩形绘制在所有内容上?

    另外,我修改了Mozila开发中心的example关于堆肥以复制我需要做的事情,并且没有任何复合模式似乎有效,check that out如果有帮助的话。

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

2 个答案:

答案 0 :(得分:14)

一个简单的方法是使用imageData,但这会非常缓慢。这是一个选项,但不适合游戏引擎。

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

http://jsfiddle.net/HADky/

或者看一下背后的图片:http://jsfiddle.net/HADky/10/

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

但如果有多个光源,它会比那更棘手。我不太清楚如何以有效的方式覆盖它,特别是如果你计划两个光源交叉。

你可能应该做的是设计一个alpha通道而不是这个重叠的东西,但我现在无法想到让它工作的好方法。如果我想到别的什么,我会重温这个。


编辑:嘿!所以我已经做了一些思考并提出了一个很好的解决方案。

你需要做的是绘制一种alpha通道,其中黑点标记你想要光的位置。所以,如果你有三个光源,它将是这样的:

enter image description here

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

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

除了透明部分是正确的环境外,这将留下“相反”的图像:

enter image description here

以下是代码的工作示例:

http://jsfiddle.net/a2Age/

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

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

答案 1 :(得分:0)

只是一个想法,但是因为你从梯度中获得了相反的效果,你是否尝试过逆转渐变?