Как насчет этого?
Не рисуйте свое освещение, окрашивая свои спрайты плитки. Нарисуйте ваши неосвещенные плитки на цели рендеринга, затем нарисуйте огни плитки на вторую цель рендеринга, представляя каждый из них в виде прямоугольника в оттенках серого, покрывающего область плитки. Для рендеринга финальной сцены используйте шейдер, чтобы объединить две цели рендеринга, затемняя каждый пиксель первого в соответствии со значением второго.
Это будет производить именно то, что у вас есть сейчас. Это не поможет вам, так что давайте немного изменим это.
Измените размеры вашей цели рендеринга карты освещения, чтобы каждая плитка была представлена одним пикселем , а не прямоугольной областью. При компоновке финальной сцены используйте состояние сэмплера с линейной фильтрацией. В противном случае оставьте все остальное таким же.
Предполагая, что вы правильно написали свой шейдер, карта освещения должна быть эффективно «расширена» во время компоновки. Это даст вам хороший эффект градиента бесплатно через сэмплер текстуры графического устройства.
Вы также можете вырезать шейдер и сделать это проще с «затемняющим» 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();
Это умножит цвет назначения (неосвещенные плитки) на исходный цвет (карту освещения), соответственно затемняя неосвещенные плитки и создавая эффект градиента в результате масштабирования текстуры карты освещения до необходимого размера.