Sign in to follow this  
Mark53

Heat wave in 2D

Recommended Posts

Hi everyone, I'm having much difficulty in finding an algorithm to render a heat wave in 2D (like when the background distorts under the effect of heat). I've implemented a simple algorithm that just shifts the lines of the picture alternatively but I'm not satisfied with it. Thanks in advance, Mark

Share this post


Link to post
Share on other sites
Hi,

You can watch this video:

http://www.youtube.com/watch?v=Gk3QI-X6Cl0

It's my implementation of heat wave from Render Monkey sample. It you like it, then you can download Render Monkey and have a look. Hope it helps.

Best,

Share this post


Link to post
Share on other sites
Imagine I just draw a static image and apply the heat distortion effect on it. I just need the algorithm, I'll adapt it to my needs. But I fear the effect is more complicated than a line shifting with a sinus (it's what I did but the result isn't good). Look at the effect on the video, the lines of the background are smoothly wrapping one another. Of course, you can see this kind of effect in many other games.

Sorry, I forgot to precise that the effect is visible after 31 sec in that video.

Share this post


Link to post
Share on other sites
The effects showed in www.facewound.com are very interesting, I'll have to try some of them. But as regards the heat wave effect, I don't think a regular curve like sine would do the trick.

The warp map idea is very good. If I understood right, it consists in dividing the picture into squares, deforming the squares and then map the content of the deformed squares to the original squares. But it looks quite difficult to implement, maybe there's a way to use a transformation matrix. The fire effect was amazing.

Nice links!

Share this post


Link to post
Share on other sites
A sine wave would do the trick, but only if it's not used like in that example.
Try y = sin( time + y );
Here's what I tried in RenderMonkey to get a similar (if not the same) effect:

// Based on default effect - Screen-AlignedQuad
// Pixel Shader code..
sampler2D Texture0;
float fT; //semantic - Time0_X

float4 ps_main( float2 texCoord : TEXCOORD0 ) : COLOR
{
return tex2D( Texture0, texCoord + float2(0, sin( texCoord.y *16 + fT ) * 0.04 ) );
}

Share this post


Link to post
Share on other sites
You can do that if you want but you don't have to. It depends on what you want to see on the screen.
P.S. You can also put x in the equation - y = sin( y + time + x ) (i didn't put the constants that modify the result).
Might add that "wave effect" (sorry for the ascii art, I don't have time for good images :P):


===\=\=\=\\\ ////=/=/=/=/====\==\=\=\=\\
\\\\=\=\======/=//=/=///// \\\\=\=\=\





[Edited by - snake5 on January 3, 2010 10:52:42 AM]

Share this post


Link to post
Share on other sites
Well, I tried to use y = sin(y + time) but it didn't work at all. All I got was a series of vertical lines...

I don't know what RenderMonkey is, but could you put it under plain C?

Here is what I did in DirectX:

for (int y = 0; y < 240; y++)
{
for (int x = 0; x < 320; x++)
{
int shiftedY = sin(y * 16 + time) * 5;

if (shiftedY < 0)
shiftedY = 0;
else if (shiftedY > 239)
shiftedY = 239;

WORD color = m_temp[shiftedY * 320 + x];

*ptr++ = color;
}

ptr += offset;
}

'm_temp' is the buffer where the original surface is stored and the back surface is accessible through 'ptr'. This algorithm doesn't work.

Share this post


Link to post
Share on other sites
Quote:
Original post by Mark53
Well, I tried to use y = sin(y + time) but it didn't work at all. All I got was a series of vertical lines...

I don't know what RenderMonkey is, but could you put it under plain C?

Here is what I did in DirectX:

for (int y = 0; y < 240; y++)
{
for (int x = 0; x < 320; x++)
{
int shiftedY = sin(y * 16 + time) * 5;

if (shiftedY < 0)
shiftedY = 0;
else if (shiftedY > 239)
shiftedY = 239;

WORD color = m_temp[shiftedY * 320 + x];

*ptr++ = color;
}

ptr += offset;
}

'm_temp' is the buffer where the original surface is stored and the back surface is accessible through 'ptr'. This algorithm doesn't work.


You need to look at the unit used for 'sin'; chances are it's supposed to be in radians, which means you will need to scale (y*16+time) to bring it into a range more appropriate.

Edit: I noticed another problem with this code - you need to add shiftedY to y in order to get your offset read value. Right now, you'll only be searching for pixels in the range -1..1 which is cropped by your if statements afterwords to 0..1.

Calculate the offset (int x,y) first, add it to your current location; check for bounds, then read your value by adding the offset to your original x&y.

One tip I would provide is that when trying to figure out why these types of functions don't work, step through them in your debugger and determine what values are actually being generated so you can verify your numbers are in the expected range.

[Edited by - djz on January 11, 2010 5:42:24 PM]

Share this post


Link to post
Share on other sites
got a few minutes to kill.... here's how I would do this, very simply. Some of the variable calculation could be inlined, but I wanted to show what is happening explicitly. (This is in C#, but it should give you an idea of what to do.


const double DistortionAmount = 3; //arbitrary number, in pixels to scale the offset by
for(int x=0; x< Source.Width; ++x)
for(int y=0; y<Source.Height; ++y)
{
double TimeInRadians = (TimeInMilleseconds / 1000) * 2*Math.Pi; //will cycle through one full revolution of Cos/Sin per second, 2pi radians = 360 degrees
double xNormalized = x / Source.Width; //normalize x+y to range 0...1
double yNormalized = y / Source.Height;
int offset_x = (int)(Math.Sin(TimeInRadians + xNormalized)*DistortionAmount);
int offset_y = (int)(Math.Cos(TimeInRadians + yNormalized)*DistortionAmount);
//put some bounds checking here
Dest.SetPixel(x,y,Source.GetPixel(x+offset_x,y+offset_y));
}



I know that Bitmap.GetPixel/SetPixel are very slow, I was just trying to make the solution as clear as possible. You need to be careful about when and where you cast to make sure you maintain precision without quantizing to ints ahead of the game.

Share this post


Link to post
Share on other sites
Hi djz,

Thanks for your time. I tried similar algorithms, and I found out that the best effect was obtained when the variable parameter (TimeInMilleseconds in your code) is added to the scaled coordinates without being scaled, i.e.:

int offset_x = (int) (Math.Sin(TimeInMilleseconds + xNormalized * 2 * Math.Pi)* DistortionAmount);

Although interesting, the effect is still different from the one used in FF6...

All the best,
Mark

Share this post


Link to post
Share on other sites

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