I want to implement the algorithm for a 2D water surface described here and here.

Before somebody finds out, beats me up and deletes this, I'm gonna say it myself. I posted this question over at stackoverflow too. I know cross posting is a terrible thing, but I just found this Game Development Q&A site. My question is closely connected to game development, because I want to use the effect in a game. Also I use SFML, which is a library mostly used for developing games. I think there are more GLSL and graphics experts here, so maybe somebody is able to help me. Again please excuse this.

So some background information: I am using SFML and C++. I want to recreate the effect described in the articles above. But instead of using two int arrays and calculating on the CPU I would like to use SFML's sf::RenderTexture's (FBO's basically) and a GLSL shader to run everything on the GPU.

I've made some good progress so far. I was able to set up 3 sf::RenderTextures and ping-pong between them correctly (because other than int array you can't read and write to the same sf::RenderTexture at the same time). I was also able to adapt the algorithm for the height field creation form being in the range -32.767 to 32.767 to the range 0 to 1 (or to be more precise -0.5 to 0.5 for the calculation). Also adding new ripples works to some extend. So up to this point you can actually see a little of waves going on.

Here comes my problem now: The waves disappear really, really fast and I don't even apply any damping yet. According to the algorithm the ripples are not stopping if there is no damping applied. It's even the other way around. If I apply "amplification" the waves look close to what you would expect them to look like (but they still disappear without any damping applied to them). My first thought was that this is, because I use float's in range 0 - 1 instead of integers, but I only see this being a problem if multiplication is used, but I only use addition and subtraction.

Another idea that a user at stackoverflow suggested was, that I was sampling the texels edges and not their centre, giving me interpolated values and thus creating the unexpected damping, but this turned out to be false as well.

Here is my SFML C++ code :

#include <SFML/Graphics.hpp>
#include <iostream>

int main()
    sf::RenderWindow window(sf::VideoMode(1000, 1000), "SFML works!");

    sf::RenderTexture buffers[3];
    buffers[0].create(500, 500);
    buffers[1].create(500, 500);
    buffers[2].create(500, 500);
    sf::RenderTexture* firstBuffer = buffers;
    sf::RenderTexture* secondBuffer = &buffers[1];
    sf::RenderTexture* finalBuffer = &buffers[2];

    firstBuffer->clear(sf::Color(128, 128, 128));
    secondBuffer->clear(sf::Color(128, 128, 128));
    finalBuffer->clear(sf::Color(128, 128, 128));

    sf::Shader waterHeightmapShader;
    waterHeightmapShader.loadFromFile("waterHeightmapShader.glsl", sf::Shader::Fragment);

    sf::Sprite spritefirst;
    spritefirst.setPosition(0, 0);

    sf::Sprite spritesecond;
    spritesecond.setPosition(500, 0);

    sf::Sprite spritefinal;
    spritefinal.setPosition(0, 500);

    while (window.isOpen())
        sf::Event event;
        while (window.pollEvent(event))
            if(event.type == sf::Event::Closed)

            if(event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Escape)

        waterHeightmapShader.setParameter("mousePosition", sf::Vector2f(-1.f, -1.f));
        // if mouse button is pressed add new ripples
            sf::Vector2i mousePosition = sf::Mouse::getPosition(window);
            if(mousePosition.x < 500 && mousePosition.y < 500)
                sf::Vector2f mouse(mousePosition);

                mouse.x /= 500.f;
                mouse.y /= 500.f;

                mouse.y = 1 - mouse.y;

                std::cout << mouse.x << " " << mouse.y << std::endl;

                waterHeightmapShader.setParameter("mousePosition", mouse);

        waterHeightmapShader.setParameter("textureTwoFramesAgo", firstBuffer->getTexture());
        waterHeightmapShader.setParameter("textureOneFrameAgo", secondBuffer->getTexture());

        // create the heightmap
        finalBuffer->clear(sf::Color(128, 128, 128));
        finalBuffer->draw(sf::Sprite(secondBuffer->getTexture()), &waterHeightmapShader);



        // swap the buffers around, first becomes second, second becomes third and third becomes first
        sf::RenderTexture* swapper = firstBuffer;
        firstBuffer = secondBuffer;
        secondBuffer = finalBuffer;
        finalBuffer = swapper;

    return 0;

And here is my GLSL shader code :

uniform sampler2D textureTwoFramesAgo;
uniform sampler2D textureOneFrameAgo;
uniform vec2 mousePosition;

const float textureSize = 500.0;
const float pixelSize = 1.0 / textureSize;

void main()
    // pixels position
    vec2 position = gl_TexCoord[0].st;

    vec4 finalColor = ((texture2D(textureOneFrameAgo, vec2(position.x - pixelSize, position.y)) +
                        texture2D(textureOneFrameAgo, vec2(position.x + pixelSize, position.y)) +
                        texture2D(textureOneFrameAgo, vec2(position.x, position.y + pixelSize)) +
                        texture2D(textureOneFrameAgo, vec2(position.x, position.y - pixelSize)) - 2.0) / 2) -
                       (texture2D(textureTwoFramesAgo, position) - 0.5);

    // damping
//    finalColor.rgb *= 1.9;  // <---- uncomment this for the "amplifiction" ie. to see the waves better
    finalColor.rgb += 0.5;

    // add new ripples
    if(mousePosition.x > 0.0)
        if(distance(position, mousePosition) < pixelSize * 5.0)
            finalColor = vec4(0.9, 0.9, 0.9, 1.0);

    gl_FragColor = finalColor;


Please remember that this is all just about the height field creation. There is no shading of the water or a background texture yet.

I have done a lot of research, but I can't find anything that helps me, I am really stuck now! So does anybody know why the waves disappear by them self without damping?

  • \$\begingroup\$ Why would you expect the waves to not disappear? Unless you apply boundary conditions that reflect the waves, the initial disturbance in the system will average out over the course of the simulation. \$\endgroup\$
    – user830
    Feb 13, 2013 at 16:24
  • \$\begingroup\$ Also your shader doesn't quite look right to me. This is the correct algorithm as I know it. pastebin.com/wxJJLbM2 \$\endgroup\$
    – user830
    Feb 13, 2013 at 16:46

1 Answer 1


Why would you consider the wave disappearing unexpected?

The shader averages/spreads differences in the heightmap. All it does is basically damping. Each iteration it will flatten out the waves, making them a little less high and a little wider.

To have a more realistic wave-propagation you might want to try to use 2 textures.

Use one for heights, and one for velocities, and spread (see advection, wave-propagation) their values just like you would with only the heightmap. In this case an additional step is needed to update velocities based on height-differences (gravity). A high pixel flows towards a low pixel, with means the velocity at and/or between those to will increase a little per timestep.

A good term to search the interwebs for would be "shallow water equations". Especially Stam's method might interest you, as it is well suited for fast but visually appealing results. See: http://www.gamasutra.com/view/feature/1549/practical_fluid_dynamics_part_1.php


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .