Есть много способов добиться этого. Потратьте некоторое время, прежде чем принять окончательное решение. Я кратко суммирую некоторые приемы, которые вы можете использовать, и в конце приведу код.
Жесткое освещение
Если вы хотите создать резкий световой эффект (как в примере с изображением), мне приходят в голову некоторые подходы:
![пример жесткого света](https://i.stack.imgur.com/tvhDd.png)
Быстро и грязно (как вы предложили)
- Используйте черный фон
- Задайте альфа-значения плиток в соответствии с их значением темноты.
Проблема в том, что вы не можете ни сделать тайл ярче, чем он был раньше (блики), ни изменить цвет света. Оба эти аспекта обычно делают освещение в играх хорошим.
Второй набор плитки
- Используйте второй набор (черных/цветных) плиток
- Положите их поверх основных плиток.
- Установите альфа-значение новых плиток в зависимости от того, насколько ярким должен быть новый цвет.
Этот подход имеет тот же эффект, что и первый, с тем преимуществом, что теперь вы можете раскрасить плитку наложения в цвет, отличный от черного, что позволяет использовать как цветные источники света, так и блики.
Пример: ![жесткий свет с черными плитками, пример](https://i.stack.imgur.com/ThMHu.png)
Несмотря на то, что это легко, проблема в том, что это действительно очень неэффективный способ. (Два отрендеренных тайла на тайл, постоянное перекрашивание, множество операций рендеринга и т. д.)
Более эффективные подходы (жесткое и/или мягкое освещение)
Глядя на ваш пример, я представляю, что свет всегда исходит от определенного источника (персонаж, факел и т. д.).
- Для каждого типа света (большой факел, маленький факел, освещение персонажа) вы создаете изображение, которое представляет определенное поведение освещения относительно исходной плитки (световая маска). Может быть, что-то вроде этого для факела (белый — альфа):
![центрированная световая маска](https://i.stack.imgur.com/yZS3G.png)
- Для каждой плитки, которая является источником света, вы визуализируете это изображение в позиции источника как наложение.
- Чтобы добавить немного светлого цвета, вы можете использовать, например. 10% непрозрачного оранжевого цвета вместо полного альфа-канала.
Результаты
![результат жесткого освещения маски изображения](https://i.stack.imgur.com/1kz0c.png)
Добавляем мягкий свет
Мягкий свет теперь не имеет большого значения, просто используйте больше деталей в световой маске по сравнению с тайлами. Используя только 15% альфы в обычно черной области, вы можете добавить эффект плохой видимости, когда плитка не освещена:
![мягкий свет](https://i.stack.imgur.com/rmgRC.png)
Вы даже можете легко получить более сложные формы освещения (конусы и т. д.), просто изменив изображение маски.
Несколько источников света
При комбинировании нескольких источников света такой подход приводит к проблеме: рисование двух пересекающихся друг с другом масок может компенсировать друг друга:
![отмена маски](https://i.stack.imgur.com/RW4Fb.png)
Мы хотим, чтобы они добавляли свои источники света, а не вычитали их. Как избежать проблемы:
- Инвертировать все светлые маски (где альфа-темные области, непрозрачные - светлые)
- Визуализируйте все эти световые маски во временное изображение, которое имеет те же размеры, что и окно просмотра.
- Инвертируйте и визуализируйте новое изображение (как если бы это была единственная световая маска) по всему пейзажу.
Это приведет к чему-то похожему на это: ![result image](https://i.stack.imgur.com/giTfS.png)
Код для метода инвертирования маски
Предполагая, что вы сначала визуализируете все плитки в BufferedImage
, я предоставлю некоторый код руководства, который напоминает последний показанный метод (только поддержка оттенков серого).
Несколько световых масок, например. факел и игрока можно комбинировать так:
public BufferedImage combineMasks(BufferedImage[] images)
{
// create the new image, canvas size is the max. of all image sizes
int w, h;
for (BufferedImage img : images)
{
w = img.getWidth() > w ? img.getWidth() : w;
h = img.getHeight() > h ? img.getHeight() : h;
}
BufferedImage combined = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
// paint all images, preserving the alpha channels
Graphics g = combined.getGraphics();
for (BufferedImage img : images)
g.drawImage(img, 0, 0, null);
return combined;
}
Окончательная маска создается и применяется с помощью этого метода:
public void applyGrayscaleMaskToAlpha(BufferedImage image, BufferedImage mask)
{
int width = image.getWidth();
int height = image.getHeight();
int[] imagePixels = image.getRGB(0, 0, width, height, null, 0, width);
int[] maskPixels = mask.getRGB(0, 0, width, height, null, 0, width);
for (int i = 0; i < imagePixels.length; i++)
{
int color = imagePixels[i] & 0x00ffffff; // Mask preexisting alpha
// get alpha from color int
// be careful, an alpha mask works the other way round, so we have to subtract this from 255
int alpha = (maskPixels[i] >> 24) & 0xff;
imagePixels[i] = color | alpha;
}
image.setRGB(0, 0, width, height, imagePixels, 0, width);
}
Как уже отмечалось, это примитивный пример. Реализация смешивания цветов может потребовать немного больше усилий.
person
bricklore
schedule
09.12.2013