Writing A Basic Water Shader [GLSL / GLSL Beginner level]

Started by
2 comments, last by ajm113 12 years, 11 months ago
Hello I want to write a basic water shader that doesn't use anything fancy. I'm pretty much new to the world of GLSL so I don't understand everything yet. So I was hopping someone could help me write a shader as I go here?

Anyways I got a quad to blend two textures. The first one is the water texture, the second is the reflection and nothing really special is happening. I do want to expand on this of course and get a pretty good looking basic water shader.

For my first question; Whats the best way to get a simple reflection using a 2D texture? I want to achieve a very simple reflection of the sky just by using one image, but I'm not sure whats the best for the job without using a 3D cubemap texture.

Second question; How can I 'move' a texture around in GLSL? So I can get the water texture to move around to seem like it's real water.

Third question; How can I figure out how to change the color of the ocean using the water texture and a vec4 including the reflection in one fragment shader?

Thank you for reading and I hope to learn something new from the community! :)


Encase if your wondering what my fragment shader looks like:

Fragment



uniform sampler2D waveTextureId;
uniform sampler2D waveTextureIdRef;

uniform float waveTime;


void main()
{

vec4 color1 = texture2D(waveTextureId, vec2(gl_TexCoord[0]));
vec4 color2 = texture2D(waveTextureIdRef, vec2(gl_TexCoord[1]));


gl_FragColor = 0.6 * vec4(color1 + color2);
}


Vertex



uniform float waveTime;

void main(void)
{

gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;


gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

}




Thanks, ajm
Check out my open source code projects/libraries! My Homepage You may learn something.
Advertisement
Making the water texture move can easily be achieved by passing a (delta-)time constant to your shader each frame and by moving the UV-coordinates for your texture read accordingly
Depending on what kind of water you want to create, adding a moving bump map to simulate tiny ripples on the surface might be a good idea, and if you want larger waves you could use sinusoidal functions in your vertex shader to transform your vertex positions (your water plane needs a good amount of vertices for this to look right though)

I have never looked into single-texture reflections, I've only done cubemap and dual paraboloid reflections before so I'm not going to say it's impossible because maybe it is in some form (and if it is, I bet someone here could definitely explain it to you), although I think it's unlikely you'll get any good results

About your color question, you could use a grayscale water texture together with your own water color (or multiple colors) to be able to make quick changes to your final water color, although most ocean shaders use different colors depending on the depth of the actual water (but I assume this is too much for a really simple implementation)

To get a good water simulation you'll probably also need some form of refraction, although this could again be too much if this is just a very simple implementation


Hope I could be of help :)

I gets all your texture budgets!

Single texture reflections (planar reflection) are definitely possible, and probably preferable in most cases. The technique is pretty simple (a while since I have done it though):
  1. Reflect your scene along the reflection surface (going to be a <0,1,0> plane for water).
  2. Set up a clip plane to stop anything above the <0,1,0> plane from being drawn (i.e. in the reflected scene all geometry that was below the water surface is clipped away).
  3. Reflect the camera in the plane again, to bring it back above the <0,1,0> plane (i.e. back to its original location).
  4. Render the scene (remember to reverse your back face culling), this gives you your reflection texture.
  5. Do your main render, pass in the reflection texture and access it using the screen coordinates, and use some technique to perturb the UVs slightly. You will have to do some fiddling to fix the edges of the texture as the perturbation will cause out of bounds UVs to be generated for which you haven't rendered anything. You could render a slightly wider FOV when creating the reflection texture, then scale back the UVs so that no perturbation will take the UVs out of bounds.

You can't use planar reflections on "proper" water where you have correct normals for which you wish to calculate a reflection vector, as they only contain a small subset of the geometry that can be visible from any location. But then a cube map reflection won't solve this problem correctly either as they only show the scene from a single location, and the further the pixel you are shading is from that location the less correct it will be. I guess when it comes down to is there is no way of doing physically correct water reflections.
Thanks for the help! I have the basic reflection going and I'm able to change the waters color, but it's still kinda buggy.

11tmwxe.png

That is what it looks like at the moment, I got everything from getting the water texture and reflection to move around and getting very basic reflection code in there, but the texcoords are out of wack for the reflection. Meaning the reflection image shows and moves around the camera, but it's stretched badly. D:

here is my shader now:

Vertex


uniform float waveTime;
varying vec3 vTexCoord;

void main(void)
{

//Get Multitexturing coords...
gl_TexCoord[0] = gl_MultiTexCoord0;
gl_TexCoord[1] = gl_MultiTexCoord1;


//Move the water...
gl_TexCoord[0].x += waveTime;
gl_TexCoord[0].y += waveTime-2.0; //Make the water move direction vary a little.


// Normal in Eye Space
vec3 vEyeNormal = gl_NormalMatrix * gl_Normal;
// Vertex position in Eye Space
vec4 vVert4 = gl_ModelViewMatrix * gl_Vertex;
vec3 vEyeVertex = normalize(vVert4.xyz / vVert4.w);
vec4 vCoords = vec4(reflect(vEyeVertex, vEyeNormal), 0.0);
// Rotate by flipped camera
vCoords = gl_ModelViewMatrixInverse * vCoords;
vTexCoord.xyz = normalize(vCoords.xyz);
// Don't forget to transform the geometry!



gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;

}





uniform sampler2D waveTextureId;
uniform sampler2D waveTextureIdRef;

uniform float waveTime;
varying vec3 vTexCoord;


void main()
{

vec4 color1 = texture2D(waveTextureId, vec2(gl_TexCoord[0]));
vec4 color2 = texture2D(waveTextureIdRef, vec2(vTexCoord));


gl_FragColor = 0.6 * vec4(color1 + color2) * vec4(0.0, 1.0, 1.0, 0.50);
}


I'm finding GLSL very fun to play around with, but knowing what function is best for the job is a little beyond me.
Check out my open source code projects/libraries! My Homepage You may learn something.

This topic is closed to new replies.

Advertisement