So I have already implemented the reflection part:

uniform sampler2D texture;
uniform vec2 resolution;
uniform vec3 overlayColor;

void main()
{
vec2 uv = gl_FragCoord.xy / resolution.xy;

if (uv.y > 0.3)// is air - no reflection or effect
{
    gl_FragColor = texture2D(texture, vec2(uv.x, uv.y));
}
else
{
    // Compute the mirror effect.
    vec4 color = texture2D(texture, vec2(uv.x, 0.6 - uv.y));
    // 
    vec4 finalColor = vec4(mix(color.rgb, overlayColor, 0.25), 1.0);
    gl_FragColor = finalColor;
}
}

source http://fc03.deviantart.net/fs70/f/2013/080/d/9/jungle_tranquility_animated_by_fadwaangela-d5yb54c.gif

Now the question is how are these ripples implemented?

  • 3
    This is no complete answer, but a series of hints: you need an uniform to "animate" the effect - i.e. a time-like variable. Using that time value, you can shift the uv.xy with a (sin(time),cos(time)) offset vector. Of course, you must figure out the amplitudes of the sine and cosine offsets. I'd start with just offsetting the uv.y first and see how I can adjust the effect further. – teodron Jan 2 '15 at 12:02
  • Thank you so much for these hints. It turned out this is what I need after trying @LeFauve's implementation. – cepro Jan 2 '15 at 16:30
up vote 9 down vote accepted

I tried to implement what teodron suggested:

void main()
{
    vec2 uv = gl_FragCoord.xy / resolution.xy;
    float sepoffset = 0.005*cos(iGlobalTime*3.0);
    if (uv.y > 0.3 + sepoffset)// is air - no reflection or effect
    {
        gl_FragColor = texture2D(texture, vec2(uv.x, -uv.y));
    }
    else
    {
        // Compute the mirror effect.
        float xoffset = 0.005*cos(iGlobalTime*3.0+200.0*uv.y);
        //float yoffset = 0.05*(1.0+cos(iGlobalTime*3.0+50.0*uv.y));
        float yoffset = ((0.3 - uv.y)/0.3) * 0.05*(1.0+cos(iGlobalTime*3.0+50.0*uv.y));
        vec4 color = texture2D(texture, vec2(uv.x+xoffset , -1.0*(0.6 - uv.y+ yoffset)));
        // 
        //vec4 finalColor = vec4(mix(color.rgb, overlayColor, 0.25), 1.0);
        gl_FragColor = color;
    }
}

It looks pretty close (it's hard to tell without the base image) but you can tweak the parameters.

You may see it in action there: https://www.shadertoy.com/view/Xll3R7

Some remarks:

  • I had to invert the y coordinate since I was getting the image upside down, but it may depend of what you pass into resolution.xy; if the result is inverted for you, just uninvert uv.y
  • I changed your uniform declarations so it works with shadertoy. You can ignore those changes.
  • You will need however to add a uniform providing the time and use it in place of iGlobalTime (which is the time in seconds)
  • I added a tide effect since it looks like there is one on your example but it's hard to tell (see sepoffset variable). You may remove it if you don't like it
  • I removed the overlay color since it was not looking good, and your example didn't had one
  • To tweak the effect to your taste:
    • change the factor of iGlobalTime to speedup/slowdown the effect (you can change each of them separately if you want, let's say accelerate the x movement and slow down the y movement)
    • change the cos() factor to amplify/attenuate the effect

EDIT: I changed the yoffset to include the modification from @cepro

  • 1
    Great effort! +1 – teodron Jan 2 '15 at 15:22
  • 3
    Thank you for your help :). This really does give a pretty close result. But I think it misses one last ingredient. Notice, in the picture, that the closer those ripples are to the camera (bottom of the screen) the bigger they are (vertically stretched). So maybe we need to scale the y offset by the uv.y? float yoffset = ((0.3 - uv.y)/0.3) * 0.05*(1.0+cos(iGlobalTime*3.0+50.0*uv.y));. I have tried this and I kinda like the result. – cepro Jan 2 '15 at 17:06
  • Good catch for the closer ripples @cepro. I missed it – LeFauve Jan 3 '15 at 7:12
  • IMO there's something wrong with the modified wave pettern. While the waves increase for me, they've got that odd "mirrored" pattern in them (GTX 680 in latest Chrome). – Mario Jan 3 '15 at 8:41

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.