Sign in to follow this  

Special Effects Overlay

This topic is 3859 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Good day, I'd like to know how to handle special effects overlay. This is probably not the official term, so I think I ought to explain what I mean. Special effect overlays are always used in 2d games. It's an image drawn over the screen to give a certain effect. These include weatherlike effects, lightbeams and so on. Some games that use these effects (and I can come up within this second) are Legend of Zelda: the Minish Cap, and a lót of RPGMaker games (hehe[grin]). I was wondering how this was done and how I could include these type of effects in my game, so I had a look into the directory of one RPGMaker game. This is one of the images it is done with: Simple, I at first thought...Just do something with alpha and the like, and it'll all come together. Not. When I thought for a second time I got to the conclusion that won't work. The screen will only be darkened for the dark parts, and even if I cut those out (transparent colours), it will only looks ugly. How is this done? -Stenny

Share this post


Link to post
Share on other sites
It's going to be blended one way or another. Judging from the greyscale texture, it could be blended additively, or used as an alpha channel for some other image (possibly a single-colour overlay).

Tell us more about what it looks like in the game and we'll try to work out exactly what blending parameters are being used.

Admiral

Share this post


Link to post
Share on other sites
Thanks :). I'll try to give as much info.

I'm programming in c++ and using DirectX. I'm using Direct3D, but none of its 3D options.

Here is an example of a RPGMaker game that uses the kind of effect I'm looking for:



It's even the same picture used.

If you need more informatiom, please ask[smile]

-Stenny

Share this post


Link to post
Share on other sites
It looks like the light effect image has an alpha channel in it that make it translucent. Then it is drawn on top of the seen. I can't tell if it is blended or not unless I could see the sprites with out the light effect on them.

Share this post


Link to post
Share on other sites
So you guys are suggesting I should use that picture as an alpha channel, and not just draw it over the screen with I-know-what.

Not that I know how to do that, but that's the idea right?

-Stenny

Share this post


Link to post
Share on other sites
You could alpha blend a white full-screen quad onto the screen, using that image as the alpha channel, but it's not the most direct way. Here's one method:

Load the image as a greyscale texture. Create a quad that spans the screen with appropriate texture coordinates - you could do this inline and call DrawPrimitiveUP, or you could put the quad into a vertex-buffer. It really doesn't make much difference in this case. When the time comes to add the effect, bind the texture to sampler 0 and call

IDirect3DDevice9::SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ONE);
IDirect3DDevice9::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

(along with
m_pD3DDev->SetRenderState(D3DRENDERSTATE_ALPHABLENDENABLE, TRUE);
if you don't already have blending enabled.)

before rendering the quad. Rather than trying to render the new texture over the frame-buffer, or modulate the two together using an alpha value, it will simply add the texture's colour components to the existing image. The result is that black areas come out unchanged and white areas are lightened.

Remember to set the blend-mode back once you're done rendering. Presumably

IDirect3DDevice9::SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
IDirect3DDevice9::SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);

Admiral

Share this post


Link to post
Share on other sites
Wow guys! Thanks!

I didn't think it'd be that easy, just setting renderstates. I thought I had to go through parsing the grayscale or something. Anyway, I'm going to bed now, but I'll try tommorow and say if it works. Thanks!

-Stenny

[Edited by - stenny on May 24, 2007 8:59:25 AM]

Share this post


Link to post
Share on other sites
Ok. It's doing something, and it's some things I sorta want, but it's correct. This is what I got, with all things set like you guys told me to:



It's doing nothing. This is what I got when I did nót set back to the old alpha:



And this is what it looks like when I don't draw the overlay, and do not set back to the old alphasettings (D3DBLEND_SRCALPHA and INVSRCALPHA).



That's not completely right. The lights too intense and the other sprites are enlightened too.

-Stenny

Share this post


Link to post
Share on other sites
It looks like you're doing things in the wrong order. Here's how it should go:

1. Render the scene as you were in the first place.
2. Set the blend-mode to add.
3. Render the overlay.
4. Reset the blend-mode states to modulate.

Admiral

Share this post


Link to post
Share on other sites
You don't want to add the original texture as-is. It's quite obvious that the original texture contains large areas of almost pure white, and adding pure white to any underlying colour will give you just more pure white. I'm not a 3D programmer though so I don't know the exact operation you need. There's probably some sort of scaling or overall alpha factor that needs to be inserted somewhere.

Share this post


Link to post
Share on other sites
Ok, I'm almost there. There still one problem, and here's what I've done. I followed up the Admirals advice (thanks mate!) and double checked if I did everything in the right order. I did. Nonetheless, I placed the rendering of the overlay even fúrhter in the gameloop. Also, I did some scaling and this is what I got:



The left image has full alpha and the right, like, 15 %.

I don't want those ugly black lines on the outer edges of the light though.

Any ideas?

-Stijn

Share this post


Link to post
Share on other sites
Quote:
Original post by Kylotan
You don't want to add the original texture as-is. It's quite obvious that the original texture contains large areas of almost pure white, and adding pure white to any underlying colour will give you just more pure white. I'm not a 3D programmer though so I don't know the exact operation you need. There's probably some sort of scaling or overall alpha factor that needs to be inserted somewhere.

You're right, but I figured that that it would be easier to proxy-debug the blending problems without prematurely introducing an alpha factor. The easiest way to tone down the effect would be to set the colour of the quad to translucent white. Something like 0x40FFFFFF0x40404040 would do nicely.

Stijn, that last image looks like a step backwards to me. The second picture in your previous post was almost correct, but the blending mode wasn't being correctly reset. From the looks of things, you're currently telling D3DXCreateTextureEx to pseudo-colour-key out opaque black from the texture, and blending the texture under regular alpha-modulation. I see no evidence of additive blending.

You're going to have to paste us your code. Try to keep it short, but make sure you include everything pertaining to texture-loading, geometry creation, blending-state switches and rendering.

Admiral

Edit: Fixed that colour-value to work with the additive blend-mode.

[Edited by - TheAdmiral on May 25, 2007 1:02:30 PM]

Share this post


Link to post
Share on other sites
Well, I've got a complete graphical engine running underneath this (I know what I'm doing, I just have never looked into Blending and Alpha's). I'll try to keep this shitload of code as short as possible [wink].

First, Direct3D9 COM object is created. It is then set (registered);

- windowed
- D3DFMT_X8R8G8B8
- D3DSWAPEFFECT_DISCARD
- HAL
- Alpha Blending and Alpha Testing is now set off (standard function), but those will be turned on later on.

End of the SetMode() function. Now EnableAlphaBlending and EnableAlphaTesting are called again, but this time they're Enabled (not disabled):

EnableAlphaBlending(bool Enable = true, DWORD Src = D3DBLEND_SRCALPHA, DWORD Dest = D3DBLEND_INVSRCALPHA)
{
if(!m_pD3DDev) { return false; }
if(FAILED(m_pD3DDev->SetRenderState(D3DRS_ALPHABLENDENABLE, Enable))) { return false; }
if(Enable)
{
m_pD3DDev->SetRenderState(D3DRS_SRCBLEND, Src);
m_pD3DDev->SetRenderState(D3DRS_DESTBLEND, Dest);

m_Log->Out("Enabled ALPHA BLENDING\n");
} else {
m_Log->Out("Disabled ALPHA BLENDING\n");
}

return true;
}

bool cNGraphics::EnableAlphaTesting(bool Enable = true)
{
if(!m_pD3DDev) { return false; }
if(FAILED(m_pD3DDev->SetRenderState(D3DRS_ALPHATESTENABLE, Enable))) { return false; }
if(Enable)
{
m_pD3DDev->SetRenderState(D3DRS_ALPHAREF, 0x08);
m_pD3DDev->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);

m_Log->Out("Enabled ALPHA TESTING\n");
} else {
m_Log->Out("Disabled ALPHA TESTING\n");
}

return true;
}




They're called with no arguments set, thus the standard arguments are taken. DirectInput and the MapEngine are set up (of no relevance here). While parsing through a map-file, an cAnim class is loaded, holding the overlay (Lightbeams.png):

class cNAnim
{
private:
cNGraphics *m_Graphics;
LPDIRECT3DTEXTURE9 m_pTexture;

std::string m_Name;
DWORD m_Width, m_Height;
int m_OriginX, m_OriginY, m_FrameWidth, m_FrameHeight, m_Columns, m_Rows;
int m_StartLoop, m_EndLoop;
int m_NumFrames;

int m_Frame;
float m_Speed;

DWORD m_Timer, m_Elapsed, m_FrameTimer;


public:
cNAnim();
~cNAnim();

bool Load(cNGraphics *Graphics, char *Filename, int NumFrames, int FrameWidth, int FrameHeight, int StartLoop = 0, int EndLoop = -1, DWORD Transparent = 0, float Speed = 0, D3DFORMAT Format = D3DFMT_UNKNOWN);
bool LoadEx(cNGraphics *Graphics, char *Filename, int NumFrames, int Framewidth, int FrameHeight, int OriginX, int OriginY, int StartLoop = 0, int EndLoop = -1, DWORD Transparent = 0, float Speed = 0, D3DFORMAT Format = D3DFMT_UNKNOWN);
bool Free();
bool Blit(int DestX, int DestY, DWORD Color = 0xFFFFFFFF, int SrcX = 0, int SrcY = 0, int Width = 0, int Height = 0, float XScale = 1.0f, float YScale = 1.0f);
bool Update();
bool IsLoaded();

bool AnimEnd();

bool SetSpeed(float Speed);
bool SetFrame(int Frame);

/* A whole lotta 'Get' functions */
};

bool cMap::SetOverlay(std::string Filename, int Alpha)
{
if(Filename == "") { return false; }

cNAnim *Overlay = new cNAnim;

Overlay->Load(m_Graphics, const_cast<char*>(Filename.c_str()), 1, 320, 240, 0, -1, 0x00000000, 10);

m_Overlay = Overlay;

return true;
}

bool cNAnim::Load(cNGraphics *Graphics, char *Filename, int NumFrames, int FrameWidth, int FrameHeight, int StartLoop, int EndLoop, DWORD Transparent, float Speed, D3DFORMAT Format)
{
Free();

if(!(m_Graphics = Graphics)) { return false; }
if(!m_Graphics->GetDeviceCOM()) { return false; }
if(!Filename) { return false; }

if(FAILED(D3DXCreateTextureFromFileEx(Graphics->GetDeviceCOM(), Filename, D3DX_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, 0,
Format, D3DPOOL_MANAGED, D3DX_FILTER_TRIANGLE, D3DX_FILTER_TRIANGLE, Transparent,
NULL, NULL, &m_pTexture)))
{ return false; }

m_Name = Filename;

if(!FrameWidth)
{
m_FrameWidth = GetWidth();
} else {
m_FrameWidth = FrameWidth;
}

if(!FrameHeight)
{
m_FrameHeight = GetHeight();
} else {
m_FrameHeight = FrameHeight;
}

m_Width = GetWidth();
m_Height = GetHeight();
m_OriginX = 0;
m_OriginY = 0;
m_Columns = m_Width / m_FrameWidth;
m_Rows = m_Height / m_FrameHeight;

if(!(m_NumFrames = NumFrames)) { return false; }
if(m_NumFrames == -1) { m_NumFrames = m_Columns * m_Rows; }

m_StartLoop = StartLoop;
m_EndLoop = EndLoop;
if(m_StartLoop < 0) { m_StartLoop = 0; }
if(m_EndLoop < 0) { m_EndLoop = m_NumFrames; }
if(m_StartLoop > m_NumFrames) { m_StartLoop = 0; }
if(m_EndLoop > m_NumFrames) { m_EndLoop = m_NumFrames; }

SetSpeed(Speed);

return true;
}





SetOverlay("Data/Images/Overlay/Lightbeams.png", 1) is called. cNAnim::Load is called, and the image is loaded in. Now for the last three functions:


bool cMap::Render(int RenderX, int RenderY, DWORD Color, int StartX, int StartY, int EndX, int EndY)
{
int ForX, ForY;

if(StartX < 0 || StartY < 0) { return false; }
if(StartX >= m_Columns || StartY >= m_Rows) { return false; }
if(EndX > m_Columns) { EndX = m_Columns; }
if(EndY > m_Rows) { EndY = m_Rows; }
if(!EndX) { EndX = m_Columns; }
if(!EndY) { EndY = m_Rows; }

for(ForY=StartY;ForY<EndY;ForY++)
{
for(ForX=StartX;ForX<EndX;ForX++)
{
RenderPile(&m_Ground[ForX][ForY], RenderX, RenderY, Color);
}
}

return true;
}

bool cMap::RenderOverlay()
{
if(m_Overlay)
{
m_Graphics->EnableAlphaBlending(true, D3DBLEND_ONE, D3DBLEND_ONE);
m_Overlay->Blit(0, 0, 0x44FFFFFF, 0, 0, 0, 0, 1, 1);
m_Graphics->EnableAlphaBlending(true, D3DBLEND_SRCALPHA, D3DBLEND_INVSRCALPHA);
}

return true;
}

bool cNAnim::Blit(int DestX, int DestY, DWORD Color, int SrcX, int SrcY, int Width, int Height, float XScale, float YScale)
{
RECT Rect;
LPD3DXSPRITE pSprite;
D3DXMATRIX matTemp;

int FSrcX, FSrcY;

if(!m_pTexture || !m_Graphics || !(pSprite = m_Graphics->GetSpriteCOM())) { return false; }

FSrcX = (m_Frame % m_Columns) * m_FrameWidth;
FSrcY = (m_Frame / m_Columns) * m_FrameHeight;

if (Width == 0 || Height == 0)
{
Rect.left = FSrcX;
Rect.top = FSrcY;
Rect.right = FSrcX + m_FrameWidth;
Rect.bottom = FSrcY + m_FrameHeight;
} else {
Rect.left = FSrcX + SrcX;
Rect.top = FSrcY + SrcY;
Rect.right = FSrcX + SrcX + Width;
Rect.bottom = FSrcY + SrcY + Height;
}

D3DXMatrixIdentity(&matTemp);
D3DXMatrixScaling(&matTemp, XScale, YScale, 1.0f);

if(FAILED(pSprite->SetTransform(&matTemp))) { return false; }

if(FAILED(pSprite->Draw(m_pTexture, &Rect, NULL, &D3DXVECTOR3((float)DestX - m_OriginX, (float)DestY - m_OriginY, 0), Color))) { return false; }

Update();

return true;
}



The map is render (cMap::Render()), and after that function (yes, immediately after Render()), RenderOverlay() is called. The overlay is rendered, and you get the effect of the above two images. Previously, I put the code inside RenderOverlay() at the end of Render(), and I got the former three pics.

I hope that's enough![wink]

-Stijn

[Edited by - stenny on May 25, 2007 12:27:31 PM]

Share this post


Link to post
Share on other sites
The cNAnim::Load doesn't match the overload you are calling (it's one parameter short). In particular, I don't know what format you're creating the texture as. The whole setup is rather more complicated than I expected and its difficult to know what state the samplers and blending engine will be in at any moment, so there are lots of things to go wrong.

Anyway, call me a cop-out but I'm gonna abandon this route now. Considering that you're using a fairly high-level engine design, it's in your interest to minimise state-changes. For this reason, you're probably best off using alpha-blending like everyone else suggested [rolleyes]. I've tidied the image up a little and coded it into the alpha channel of a greyscale png. I've increased the colour-depth to 8-bits so as to remove that nasty dithering, so the texture is a little bigger now (17k as opposed to 7).



You won't be able see the alpha-blending here if you're using IE6, as it can't handle PNGs (I think IE7 and Firefox are fine), but it's there all the same. To draw this, you need only enable alpha-blending (not testing). I hope you have more success this time.

Admiral

Share this post


Link to post
Share on other sites
Dude, you're my hero. Many, many thanks!


(It's cut off on the right side because it's not scaled yet)

Now I only need to know how you edited those alpha values, and I'm ready to go!

Thanks again! [smile]

-Stijn

Share this post


Link to post
Share on other sites
Quote:
Original post by stenny
Now I only need to know how you edited those alpha values, and I'm ready to go!
I did it in Paint Shop Pro:

1. Loaded in the texture you gave me.
2. Upped the colour-depth to 32-bit.
3. Applied a Gaussian blur, just strong enough to fix the dithering.
4. Created a new image with the same dimensions, possessing an alpha channel.
5. Filled the new image in as uniform white.
6. Pasted the newly-blurred texture onto the alpha mask of the new image.
7. Saved the result as an 8-bit greyscale PNG with a single alpha channel.

I'm sure just about any decent image-editing-tool can do the same. Photoshop and the GIMP are both more than capable.

Admiral

Share this post


Link to post
Share on other sites
I know Photoshop is capable, but now I only gotta find the right equivalent of those steps ;). I'll edit when I get there.

Edit: OK. I can't seem to find the answer. If there's some Photoshop god out there, I'd like some help.

-Stenny

[Edited by - stenny on May 25, 2007 5:17:51 PM]

Share this post


Link to post
Share on other sites

This topic is 3859 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this