如何在 DirectX 中以恒定 alpha 混合两个精灵的颜色?

发布于 2024-08-02 21:49:12 字数 2039 浏览 5 评论 0原文

本质上,我想要做的(在 DirectX 中)是获取两个部分透明的图像并将它们混合在一起。这对于默认混合效果很好,因为它们都显示为重叠等。但是,问题是两者相交的地方不透明度显着增加。随着更多精灵重叠,这会导致越来越多的问题。我想做的是保持混合相同,除了为所有这些混合的精灵保持全局不透明度,无论它们如何重叠。

似乎会有一个渲染设置(所有这些精灵在它们的精灵批次中都是单独的,这使该部分变得简单),但如果是这样,我不知道。现在我有点在黑暗中拍摄,我尝试了很多不同的方法,但没有一个看起来完全正确。我知道我可能需要 D3DBLENDOP 的某种变体,但我只是不知道我真正需要什么样的设置(我已经尝试了很多东西,但现阶段都是猜测)。

这是标准混合实际发生的情况的屏幕截图(我能得到的最好的截图): http:// /arcengames.com/share/FFActual.png 这是一个屏幕截图,其中包含我希望混合结果的模型(将力场添加到 Photoshop 中的同一层,然后给出共享的 alpha 值) : http://arcengames.com/share/FFMockup.png

这就是我的做法在 Photoshop 中: 1. 拍摄两张图像,并去除所有透明度(完全透明的像素除外)。 2. 将它们组合成一层,混合颜色但根本没有部分 Alpha。 3. 现在将该层的全局透明度设置为(例如)40%。

结果是颜色看起来有点混合在一起,但重叠部分的不透明度没有增加。

更新:好的,非常感谢下面的 Goz,他建议使用 Z 缓冲区。那行得通!总的来说,混合是完美的,正是我想要的。唯一剩下的问题是什么?使用这种新方法,最后渲染的力场图像的边缘周围会出现巨大的伪影。请参阅:http://www.arcengames.com/share/FFZBuffer.png

更新:下面是 C# (SlimDX) 中的最终解决方案,

  1. 每帧将 ZBuffer 清除为黑色、透明或白色一次都具有相同的效果(这是在调用 BeginScene 之前)

Direct3DWrapper.ClearDevice( SlimDX.Direct3D9.ClearFlags.ZBuffer , 颜色.透明, 0 );

  1. 所有其他精灵均在 Z=1 处绘制,并禁用 ZBuffer:

device.SetRenderState( RenderState.ZEnable, ZBufferType.DontUseZBuffer );

  1. 力场精灵在 Z=2 处绘制,启用 ZBuffer 并启用 ZWrite 且 ZFunc 为 Less:

device.SetRenderState( RenderState.ZEnable, ZBufferType.UseZBuffer ); device.SetRenderState( RenderState.ZWriteEnable, true ); device.SetRenderState( RenderState.ZFunc, Compare.Less );

  1. 此时还设置了以下标志,以防止我遇到的黑色边框伪影:

device.SetRenderState( RenderState.AlphaTestEnable, true ); device.SetRenderState( RenderState.AlphaFunc, Compare.GreaterEqual ); device.SetRenderState( RenderState.AlphaRef, 55 );

请注意,AlphaRef 为 55,因为我使用的特定源图像中设置了 alpha 级别。如果我的源图像具有更高的 alpha 值,那么 AlphaRef 也需要更高。

Essentially, what I want to do (in DirectX) is to take two partially-transparent images and blend them together. This works fine with default blending, insofar as they both show up as overlapping, etc. However, the problem is that the opacity goes up markedly where the two intersect. This causes increasing problems as more sprites overlap. What I'd like to do is keep the blending the same, except keep a global opacity for all these sprites being blended, regardless of how they overlap.

Seems like there would be a render setting for this (all of these sprites are alone in their sprite batch, which keeps that part easy), but if so I don't know it. Right now I'm kind of shooting in the dark, and I've tried a lot of different things and none of them have looked right at all. I know I probably need some sort of variant of D3DBLENDOP, but I just don't know what sort of settings there I really need (I have tried many things, but it is all guessing at this stage).

Here is a screenshot of what is actually happening with standard blending (the best I can get it): http://arcengames.com/share/FFActual.png Here is a screenshot with a mockup of how I would want the blending to turn out (the forcefields were added to the same layer in Photoshop, then given a shared alpha value): http://arcengames.com/share/FFMockup.png

This is how I did it in Photoshop:
1. Take the two images, and remove all transparency (completely transparent pixels excepted).
2. Combine them into one layer, which blends the color but which has no partial alpha at all.
3. Now set the global transparency for that layer to (say) 40%.

The result is something that looks kind of blended together color-wise, but which has no increase in opaqueness on the overlapped sections.

UPDATE: Okay, thanks very much to Goz below, who suggested using the Z-Buffer. That works! The blending, by and large, is perfect and just what I would want. The only remaining problem? Using that new method, there is a huge artifact around the edge of the force field image that is rendered last. See this: http://www.arcengames.com/share/FFZBuffer.png

UPDATE: Below is the final solution in C# (SlimDX)

  1. Clearing the ZBuffer to black, transparent, or white once per frame all has the same effect (this is right before BeginScene is called)

Direct3DWrapper.ClearDevice( SlimDX.Direct3D9.ClearFlags.ZBuffer, Color.Transparent, 0 );

  1. All other sprites are drawn at Z=1, with the ZBuffer disabled for them:

device.SetRenderState( RenderState.ZEnable, ZBufferType.DontUseZBuffer );

  1. The force field sprites are drawn at Z=2, with the ZBuffer enabled and ZWrite enabled and ZFunc as Less:

device.SetRenderState( RenderState.ZEnable, ZBufferType.UseZBuffer );
device.SetRenderState( RenderState.ZWriteEnable, true );
device.SetRenderState( RenderState.ZFunc, Compare.Less );

  1. The following flags are also set at this time, to prevent the black border artifact I encountered:

device.SetRenderState( RenderState.AlphaTestEnable, true );
device.SetRenderState( RenderState.AlphaFunc, Compare.GreaterEqual );
device.SetRenderState( RenderState.AlphaRef, 55 );

Note that AlphaRef is at 55 because of the alpha levels set in the specific source image I was using. If my source image had a higher alpha value, then the AlphaRef would also need to be higher.

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

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

发布评论

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

评论(3

小耗子 2024-08-09 21:49:12

我能说的最多的是,力场是一个完整的物体。为什么不按照从前到后的顺序最后渲染它们,并启用 Z 缓冲。这会给你你想要的效果。

即它不混合设置这根本就是你的问题。

编辑:那么你可以使用渲染到纹理吗?如果这样你就可以轻松地完成你在 Photoshop 下所做的事情。将它们全部渲染到纹理中,然后将纹理混合回屏幕上。

Edit2:怎么样

ALPHATESTENABLE  = TRUE;
ALPHAFUNC = LESS

ALPHABLENDENABLE = TRUE;
SRCBLEND = SRCALPHA;
DESTBLEND = INVSRCALPHA;

SEPERATEALPHABLENDENABLE = TRUE;
SRCBLENDALPHA = ONE;
DESTBLENDALPHA = ZERO;

您需要确保每帧帧缓冲区中的 alpha 值都被清除为 0xff。然后进行标准的 alpha 混合。同时将 alpha 值直接传递到后缓冲区。不过,这就是 alpha 测试的用武之地。您可以根据后台缓冲区中的值来测试最终的 alpha 值。如果它小于后缓冲区中的像素,则该像素尚未混合并将被放入帧缓冲区中。如果它等于(或更大),那么它已经被混合并且 alpha 值将被丢弃。

也就是说……使用 Z 缓冲区会消耗大量 RAM,但总体速度会更快,因为它能够在管道中更早地丢弃像素。由于所有屏蔽只需要写入给定的 Z 平面,您甚至不需要经历我之前建议的地狱。如果它接收到的 Z 值小于已有的值,那么它将渲染它,如果它大于或等于它,那么它将丢弃它,幸运的是在执行混合计算之前。

也就是说......您也可以通过使用模板缓冲区来做到这一点,无论如何,这都需要 Z 缓冲区。

无论如何......希望其中一种方法能有所帮助。

Edit3:你是否在边缘周围使用某种形式的羽化来渲染力场?最有可能的是,边缘是由以下事实引起的:alpha 稍微减弱,然后“轻微 alpha”像素被写入 z 缓冲区,因此任何后续绘制都不会覆盖它们。

尝试以下设置

ALPHATESTENABLE = TRUE
ALPHAFUNC = GREATEREQUAL // if this doesn't work try less .. i may be being a retard
ALPHAREF = 255

要微调边缘周围的羽化,请调整 alpharef,但我怀疑您需要将其保留为上述内容。

Best I can tell is that the forcefields are a whole object. Why not render them last, in front to back order, and with Z-buffering enabled. That will give you the effect you are after.

ie its not blending settings thats your problem at all.

Edit: Can you use render-to-texture then? IF so you could easily do what you did under photoshop. Render them all together into the texture and then blend the texture back over the screen.

Edit2: How about

ALPHATESTENABLE  = TRUE;
ALPHAFUNC = LESS

ALPHABLENDENABLE = TRUE;
SRCBLEND = SRCALPHA;
DESTBLEND = INVSRCALPHA;

SEPERATEALPHABLENDENABLE = TRUE;
SRCBLENDALPHA = ONE;
DESTBLENDALPHA = ZERO;

You need to make sure the alpha is cleared to 0xff in the frame buffer each frame. You then do the standard alpha blend. while passing the alpha value straight through to the backbuffer. This is, though, where the alpha test comes in. You test the final alpha value against the one in the back buffer. If it is less than whats in the backbuffer then that pixel has not been blended yet and will be put into the frame buffer. If it is equal (or greater) then it HAS been blended already and the alpha value will be discarded.

That said ... using a Z-Buffer would cost you a load of RAM but would be faster overall as it would be able to throw away the pixels far earlier in the pipeline. Seeing as all the shields would just need to be written to a given Z-plane you wouldn't even need to go through the hell I suggested earlier. If the Z value it receives is less than whats there already then it will render it if it is greater or equal then it will discard it, fortunately before the blend calculation is ever performed.

That said ... you could also do it by using the stencil buffer which would require a Z-buffer anyway.

Anyway ... hope one of those methods is of some help.

Edit3: DO you render the forcefield with some form of feathering around the edge? Most likely that edge is caused by the fact that the alpha fades off slightly and then the "slightly alpha" pixels are getting written to the z-buffer and hence any subsequent draw doesn't overwrite them.

Try the following settings

ALPHATESTENABLE = TRUE
ALPHAFUNC = GREATEREQUAL // if this doesn't work try less .. i may be being a retard
ALPHAREF = 255

To fine tune the feathering around the edge adjust the alpharef but i'd suspect you need to keep it as above.

恋你朝朝暮暮 2024-08-09 21:49:12

您可以指定混合时使用的 D3DBLENDOP将两个图像放在一起作为 Alpha 通道。听起来您当前使用的是 D3DBLENDOP_ADD - 尝试将其切换为 D3DBLENDOP_MAX,因为这只会使用“最不透明”图像的不透明度。

You can specify the D3DBLENDOP used when blending the two images together for the alpha channel. It sounds like your using D3DBLENDOP_ADD currently - try switching this to D3DBLENDOP_MAX, as that will just use the opacity of the "most opaque" image.

心碎无痕… 2024-08-09 21:49:12

由于两个力场的颜色相同,因此很难从模型中准确说出您想要实现的目标。你想混合颜色并限制阿尔法吗?只取其中一种颜色?

根据上述讨论,尚不清楚您是否设置所有相关渲染状态:

D3DRS_ALPHABLENDENABLE = TRUE(默认值:FALSE)

D3DRS_BLENDOP = D3DBLENDOP_MAX(默认值:D3DBLENDOP_ADD)

D3DRS_SRCBLEND = D3DBLEND_ONE(默认值:D3DBLEND_ONE)

D3DRS_DESTBLEND = D3DBLEND_ONE(默认值:D3DBL) END_ZERO )

听起来你正在设置前两个,但是后两个呢?

It is hard to tell exactly what you are trying to accomplish from your mock up since both forcefields are the same color; do you want to blend the colors and cap the alpha? Just take one of the colors?

Based off the above discussion it isnt' clear if you are setting all the relevant render states:

D3DRS_ALPHABLENDENABLE = TRUE (default: FALSE)

D3DRS_BLENDOP = D3DBLENDOP_MAX (default: D3DBLENDOP_ADD)

D3DRS_SRCBLEND = D3DBLEND_ONE (default: D3DBLEND_ONE)

D3DRS_DESTBLEND = D3DBLEND_ONE (default: D3DBLEND_ZERO)

It sounds like you are setting the first two, but what about the last two?

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