Как умножить два спрайта в SpriteBatch Draw XNA (2D)

Я пишу простой шестнадцатеричный движок для action-rpg в XNA 3.1. Я хочу зажечь землю рядом с героем и факелы так же, как они были зажжены в Diablo II. Я думаю, что лучший способ сделать это — рассчитать поле зрения, скрыть любые плитки и их содержимое, которые игрок не может видеть, и нарисовать специальную текстуру «Свет» поверх любого источника света: Текстура, которая черная с белым, размытый круг в его центре.

Я хотел умножить эту текстуру на фон (как в режиме наложения: умножить), но, к сожалению, я не вижу возможности сделать это в SpriteBatch. Может ли кто-нибудь указать мне правильное направление?

Или, может быть, есть другой, лучший способ добиться модели освещения, как в Diablo II?


person PiotrK    schedule 30.12.2009    source источник


Ответы (3)


Если вы умножите свою световую текстуру на сцену, вы затемните область, а не сделаете ее ярче.

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

Другой, более сложный способ создания освещения состоит в том, чтобы нарисовать все ваши световые текстуры (для всех источников света в сцене) аддитивно на второй цели рендеринга, а затем умножить эту текстуру на вашу сцену. Это должно дать гораздо более реалистичное освещение, но требует больше производительности и является более сложным.

Инициализация:

RenderTarget2D lightBuffer = new RenderTarget2D(graphicsDevice, screenWidth, screenHeight, 1, SurfaceFormat.Color);
Color ambientLight = new Color(0.3f, 0.3f, 0.3f, 1.0f);

Рисовать:

// set the render target and clear it to the ambient lighting
graphicsDevice.SetRenderTarget(0, lightBuffer);
graphicsDevice.Clear(ambientLight)

// additively draw all of the lights onto this texture. The lights can be coloured etc.
spriteBatch.Begin(SpriteBlendMode.Additive);
foreach (light in lights)
    spriteBatch.Draw(lightFadeOffTexture, light.Area, light.Color);
spriteBatch.End();

// change render target back to the back buffer, so we are back to drawing onto the screen
graphicsDevice.SetRenderTarget(0, null);

// draw the old, non-lit, scene
DrawScene();

// multiply the light buffer texture with the scene
spriteBatch.Begin(SpriteBlendMode.Additive, SpriteSortMode.Immediate, SaveStateMode.None);
graphicsDevice.RenderState.SourceBlend = Blend.Zero;
graphicsDevice.RenderState.DestinationBlend = Blend.SourceColor;
spriteBatch.Draw(lightBuffer.GetTexture(), new Rectangle(0, 0, screenWidth, screenHeight), Color.White);
spriteBatch.End();
person Tom Gillen    schedule 05.01.2010

Насколько я знаю, это невозможно сделать без использования собственных шейдеров.

Пользовательский шейдер для этого будет работать так:

  1. Рендеринг вашей сцены в текстуру
  2. Визуализируйте свои источники света в другую текстуру
  3. В качестве постобработки на пустом четырехугольнике сэмплируйте две текстуры, и в результате получится Текстура сцены * Текстура света.

Это выведет освещенную сцену, но не создаст никаких теней. Если вам нужны тени, я бы посоветовал воспользоваться отличным образцом от Каталин Зима.

person Martin    schedule 31.12.2009

Возможно, стоит использовать ту же технику, что и в компоненте BloomEffect.

По сути, эффект захватывает отрендеренную сцену, вычисляет цветное изображение из самых ярких областей сцены, размытия и объединяет их. В результате получается выделение областей в зависимости от цвета.

Здесь можно использовать тот же подход. Это будет проще, так как вам не придется вычислять цветное изображение на основе фона, а только на основе положения персонажа.

Вы даже можете повторно использовать это, чтобы выделить другие источники света, такие как факелы, магические эффекты и тому подобное.

person Peter Lillevold    schedule 31.12.2009
comment
Это похоже на то, что я предложил, но, вероятно, лучше, потому что вы можете получить образец кода из примера цветения. - person Martin; 01.01.2010