# 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 on other sites
Hi,

You can watch this video:

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 on other sites
Thank you very much, b_thangvn, but that is not really what I'm looking for.

Here is a video with the effect I'm interested in:

##### Share on other sites
What are you using to render the background?
Anyway, I think that you just need a good algorithm for calculating how to shift each line - like "y = sin( time + y )". :D

##### Share on other sites
That's a long video. Can you give the play time of the effect?

##### 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 on other sites
Take a look at this guys sin distortion.

Clicky

If you were to lower the scale of distortion, and have a smooth timestamp, it should look fine, how are you currently doing it?

##### Share on other sites
Warp feedback looks pretty good, and is relatively cheap to do in a shader (you need to RTT to feed the image back into itself). Check out the demo from the fire section, you'll need a copy of DOS4GW to run it.

##### 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.

##### Share on other sites
A nice heat wave distortion can be achieved by using a 3D noise texture and animate the v-coordinate upwards, so it looks like rising hot air.

##### 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_Xfloat4 ps_main( float2 texCoord  : TEXCOORD0 ) : COLOR{   return tex2D( Texture0, texCoord + float2(0, sin( texCoord.y *16 + fT ) * 0.04 ) );}

##### Share on other sites
Don't you also have to distort the x coordinate?

##### 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 on other sites
[quote]Original post by snake5
sorry for the ascii art, I don't have time for good images :P):[quote]

If you can type this faster than drawing it, I'm amazed ;)

##### 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 on other sites
Quote:
 Original post by Mark53Well, 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.

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 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 byfor(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 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