Как насчет этого?
Не рисуйте свое освещение, окрашивая свои спрайты плитки. Нарисуйте ваши неосвещенные плитки на цели рендеринга, затем нарисуйте огни плитки на вторую цель рендеринга, представляя каждый из них в виде прямоугольника в оттенках серого, покрывающего область плитки. Для рендеринга финальной сцены используйте шейдер, чтобы объединить две цели рендеринга, затемняя каждый пиксель первого в соответствии со значением второго.
Это будет производить именно то, что у вас есть сейчас. Это не поможет вам, так что давайте немного изменим это.
Измените размеры вашей цели рендеринга карты освещения, чтобы каждая плитка была представлена одним пикселем , а не прямоугольной областью. При компоновке финальной сцены используйте состояние сэмплера с линейной фильтрацией. В противном случае оставьте все остальное таким же.
Предполагая, что вы правильно написали свой шейдер, карта освещения должна быть эффективно «расширена» во время компоновки. Это даст вам хороший эффект градиента бесплатно через сэмплер текстуры графического устройства.
Вы также можете вырезать шейдер и сделать это проще с «затемняющим» BlendState, но мне придется поэкспериментировать с ним, прежде чем я смогу дать вам подробности.
ОБНОВИТЬ
У меня было время сегодня, чтобы на самом деле насмехаться над этим. Ответ выше отражает мою привычку использовать шейдеры в качестве первого ответа на все вопросы, но в этом случае они на самом деле не нужны, и их использование без необходимости усложняет ситуацию.
Как я уже говорил, вы можете достичь точно такого же эффекта, используя пользовательский BlendState. В частности, это пользовательский BlendState:
BlendState Multiply = new BlendState()
{
AlphaSourceBlend = Blend.DestinationAlpha,
AlphaDestinationBlend = Blend.Zero,
AlphaBlendFunction = BlendFunction.Add,
ColorSourceBlend = Blend.DestinationColor,
ColorDestinationBlend = Blend.Zero,
ColorBlendFunction = BlendFunction.Add
};
Уравнение смешивания
result = (source * sourceBlendFactor) blendFunction (dest * destBlendFactor)
Так что с нашим пользовательским BlendState, это становится
result = (lightmapColor * destinationColor) + (0)
Это означает, что исходный цвет чистого белого (1, 1, 1, 1) сохранит целевой цвет, исходный цвет чистого черного (0, 0, 0, 1) затемнит целевой цвет до чистого черного, и любой промежуточный оттенок серого затемнит целевой цвет на среднее значение.
Чтобы применить это на практике, сначала сделайте все, что вам нужно, чтобы создать свою карту освещения:
var lightmap = GetLightmapRenderTarget();
Затем просто нарисуйте вашу неосвещенную сцену прямо в буфер, как обычно:
spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend);
/* draw the world here */
spriteBatch.End();
Затем нарисуйте карту освещения с помощью пользовательского BlendState:
var offsetX = 0; // you'll need to set these values to whatever offset is necessary
var offsetY = 0; // to align the lightmap with the map tiles currently being drawn
var width = lightmapWidthInTiles * tileWidth;
var height = lightmapHeightInTiles * tileHeight;
spriteBatch.Begin(SpriteSortMode.Immediate, Multiply);
spriteBatch.Draw(lightmap, new Rectangle(offsetX, offsetY, width, height), Color.White);
spriteBatch.End();
Это умножит цвет назначения (неосвещенные плитки) на исходный цвет (карту освещения), соответственно затемняя неосвещенные плитки и создавая эффект градиента в результате масштабирования текстуры карты освещения до необходимого размера.
Хорошо, вот один очень простой метод для создания простой и плавной 2D-молнии, рендеринг за три прохода:
В проходе 1 вы рисуете все спрайты и местность.
В проходе 2 вы рисуете вторую группу спрайтов, которые являются источниками света. Они должны выглядеть примерно так:
Инициализируйте цель рендеринга черным и нарисуйте эти спрайты с максимальным или аддитивным смешиванием.
В проходе 3 вы объединяете два предыдущих прохода. Есть несколько разных способов, как они могут быть объединены. Но самый простой и наименее вычурный метод - это смешать их вместе с помощью Multiply. Это будет выглядеть так:
источник
Вторая ссылка, которую вы разместили, выглядит как своего рода туман войны , есть еще пара вопросов на этот счет.
Это выглядит как текстура , где вы «стираете» биты черного наложения (устанавливая альфа этих пикселей в 0), когда игрок движется вперед.
Я бы сказал, что этот человек использовал кисть типа «стереть» там, где исследовал игрок.
источник
Легкие вершины (углы между плитками) вместо плиток. Смешайте освещение каждой плитки на основе четырех вершин.
Выполните тесты прямой видимости для каждой вершины, чтобы определить, насколько она освещена (вы можете либо заблокировать весь свет, либо уменьшить свет, например, подсчитать, сколько пересечений для каждой вершины в сочетании с расстоянием, чтобы рассчитать значение освещения, а не использовать чистый двоичный видимый / невидимый тест).
При рендеринге плитки отправьте значение освещения для каждой вершины в графический процессор. Вы можете легко использовать фрагментный шейдер, чтобы получить интерполированное значение освещения для каждого фрагмента в спрайте элемента мозаичного изображения для плавного освещения каждого пикселя. Прошло достаточно много времени с тех пор, как я прикоснулся к GLSL, и мне было бы неудобно давать реальный пример кода, но в псевдокоде это было бы так просто:
Вершинный шейдер просто должен передать входные значения фрагментному шейдеру, ничего сложного.
Вы получите еще более плавное освещение с большим количеством вершин (скажем, если каждый фрагмент представлен как четыре фрагмента), но это может не стоить усилий, в зависимости от качества, которого вы достигнете. Попробуйте сначала проще.
источник
line-of sight tests to each vertex
? Это будет дорого.