如何在 SpriteBatch Draw XNA (2D) 中将两个精灵相乘

发布于 2024-08-16 14:25:48 字数 265 浏览 5 评论 0原文

我正在 XNA 3.1 中为动作角色扮演游戏编写简单的十六进制引擎。我想照亮英雄和火把附近的地面,就像在暗黑破坏神 II 中点燃它们一样。我认为最好的方法是计算视野,隐藏玩家看不到的任何图块及其内容,并在任何光源顶部绘制特殊的“光”纹理:黑白色的纹理,中心有一个模糊的圆圈。

我想将此纹理与背景相乘(如在混合模式中:相乘),但不幸的是,我在 SpriteBatch 中看不到执行此操作的选项。有人能指出我正确的方向吗?

或者也许还有其他更好的方法来实现暗黑破坏神 II 中的光照模型?

I am writing simple hex engine for action-rpg in XNA 3.1. I want to light ground near hero and torches just as they were lighted in Diablo II. I though the best way to do so was to calculate field-of-view, hide any tiles and their's content that player can't see and draw special "Light" texture on top of any light source: Texture that is black with white, blurred circle in it's center.

I wanted to multiply this texture with background (as in blending mode: multiply), but - unfortunately - I do not see option for doing that in SpriteBatch. Could someone point me in right direction?

Or perhaps there is other - better - way to achive lighting model as in Diablo II?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最好是你 2024-08-23 14:25:48

如果您要将光纹理与场景相乘,您将使该区域变暗,而不是使其变亮。

您可以尝试使用附加混合进行渲染;这看起来不太正确,但是很简单并且可以接受。您必须使用相当低的 Alpha 来绘制光线,以使光纹理不至于使图像的该部分过度饱和。

另一种更复杂的照明方法是将所有灯光纹理(场景中的所有灯光)添加到第二个渲染目标上,然后将此纹理与场景相乘。这应该提供更真实的光照,但性能开销更大并且更复杂。

初始化:

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();

If you were to multiply your light texture with the scene, you will darken the area, not brighten it.

You could try rendering with additive blending; this won't quite look right, but is easy and may be acceptable. You will have to draw your light with a fairly low alpha for the light texture to not just over saturate that part of the image.

Another, more complicated, way of doing lighting is to draw all of your light textures (for all the lights in the scene) additively onto a second render target, and then multiply this texture with your scene. This should give much more realistic lighting, but has a larger performance overhead and is more complex.

Initialisation:

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

Draw:

// 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();
遗心遗梦遗幸福 2024-08-23 14:25:48

据我所知,如果不使用您自己的自定义着色器,就无法做到这一点。

为此,自定义着色器的工作方式如下:

  1. 将场景渲染到纹理
  2. 将灯光渲染到另一个纹理
  3. 作为空白四边形上的后期处理,对两个纹理进行采样,结果是场景纹理 * 灯光纹理。

这将输出一个明亮的场景,但不会产生任何阴影。如果您想要阴影,我建议您遵循来自 Catalin Zima 的这个优秀示例

As far as I know there is no way to do this without using your own custom shaders.

A custom shader for this would work like so:

  1. Render your scene to a texture
  2. Render your lights to another texture
  3. As a post process on a blank quad, sample the two textures and the result is Scene Texture * Light Texture.

This will output a lit scene, but it won't do any shadows. If you want shadows I'd suggest following this excellent sample from Catalin Zima

寄意 2024-08-23 14:25:48

也许使用与 BloomEffect 组件 中相同的技术可能是一个想法。

基本上,该效果的作用是抓取渲染的场景,从场景中最亮的区域计算光晕图像,进行模糊并将两者组合起来。结果是根据颜色突出显示区域。

这里可以使用相同的方法。它会更简单,因为您不必根据背景计算光晕图像,而只需根据角色的位置。

您甚至可以进一步重复使用它来为其他光源(例如火炬、魔法效果等)提供突出显示。

Perhaps using the same technique as in the BloomEffect component could be an idea.

Basically what the effect does is grabbing the rendered scene, calculates a bloom image from the brightest areas in the scene, the blurs and combines the two. The result is highlighting areas depending on color.

The same approach could be used here. It will be simpler since you won't have to calculate the bloom image based on the background, only based on the position of the character.

You could even reuse this further to provide highlighting for other light sources as well, such as torches, magic effects and whatnot.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文