Холст - заполнить прямоугольник во всех областях, которые полностью прозрачны - PullRequest
5 голосов
/ 27 октября 2011

Я пишу простой 2D игровой движок с использованием HTML5 canvas.Я пришел, чтобы добавить двигатель освещения.Каждый источник света имеет значение радиуса и значение интенсивности (0-1, например, 1 будет очень ярким).Также есть значение рассеянного света, которое используется для освещения всего остального в мире, которое не находится рядом с источником света (0-1, например, 0,1 будет лунным светом).Процесс освещения осуществляется на отдельном холсте над основным холстом:

  1. Для каждого источника света радиальный градиент рисуется в этой позиции с тем же радиусом, что и источник света.Градиент задается двумя ступенями: центр черный с альфа-1-интенсивностью света, а конец / край черный с альфа-значением 1-рассеянного света.Это все работает нормально.
  2. Вот где все идет не так: / Мне нужно заполнить весь холст черным с альфа-значением значения 1-ambient light, и в настоящий момент я делаю это, устанавливая context.globalCompositeOperationк исходному тексту, затем fillRecting весь холст.

Мой код для этого материала:

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 для компостирования, чтобы воспроизвести то, что мне нужно сделать, и ни один из составных режимов не показалсяработа, проверьте это , если это поможет.

Большое спасибо, любой ответ будет отличным:)

Ответы [ 2 ]

14 голосов
/ 27 октября 2011

Один из тривиальных способов - использовать imageData, но это будет мучительно медленно. Это вариант, но не очень хороший для игрового движка.

Другим способом было бы думать об окружающем свете и источнике света, как если бы они были одним путем. Это очень легко сделать:

http://jsfiddle.net/HADky/

Или посмотрите на изображение позади: http://jsfiddle.net/HADky/10/

То, что вы используете здесь, это то, что любое пересечение пути на холсте всегда только объединено и никогда не составляется. Таким образом, вы используете одну градиентную кисть, чтобы нарисовать все это.

Но это становится немного сложнее, если есть более одного источника света. Я не слишком уверен, как это эффективно охватить, особенно если вы планируете пересечение двух источников света.

Что вам, вероятно, следует сделать вместо этого - разработать альфа-канал вместо этого наложения, но в настоящее время я не могу придумать хороший способ заставить его работать. Я еще вернусь к этому, если подумаю о чем-нибудь еще.


РЕДАКТИРОВАТЬ: Эй! Поэтому я немного подумал и нашел хорошее решение.

Что вам нужно сделать, это нарисовать своего рода альфа-канал, где темные пятна отмечают места, где вы хотите, чтобы свет был. Итак, если бы у вас было три источника света, это выглядело бы так:

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/

Дополнительная оптимизация: Вы можете фактически добиться эффекта, не используя вообще никаких путей, просто рисуя точно такие же радиальные градиенты на канавках вместо круговых путей:

http://jsfiddle.net/a2Age/2/ Надеюсь, это поможет!

0 голосов
/ 27 октября 2011

Просто идея, но, поскольку вы получаете противоположный эффект, которого вы добиваетесь от своего градиента, вы пытались обратить вспять градиент?

...