Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I have a game that runs on mobile devices (OpenGL ES 2.0) and for which I would like to create some sea water using the shaders. Now, the plane on which the sea water texture will be has only 4 vertices and I need it to remain like that (because of performance issues). So, I think, my only option is to simulate the water from the fragment shader. At conceptual level, how should I do it? I must mention I'm a beginner with the programmable pipeline (I barely learned the language, in some extent) and have no idea where should I start.

share|improve this question
    
I have thought of this for a while too but never really did anything. First thing that came to mind was perhaps a displacement with perlin noise shader would do ( or just a texture instead of perlin ). –  Sidar Aug 10 '12 at 14:31
    
Actually, a nice question since tesselation is out of the question for today's ES-SL language and since having many input vertices is also a no-no, fragment operations might be of some help. +1 –  teodron Aug 10 '12 at 16:00

1 Answer 1

up vote 5 down vote accepted

See the paper in the GPU Gems series treating this subject: http://http.developer.nvidia.com/GPUGems/gpugems_ch01.html

What you can do is to adapt that idea (Gerstner waves) and compute the normals for each of your rendered fragment. The way to do that would be to assign a water texture (without too much light information in it, since you're gonna compute colours on in the shader anyway). That texture also comes with a texcoord set mapping (for each of your 4 vertices you get an uv pair of texture coordinates in the range of [0,1]). All in all, you have a plane you can deform in the fragment shader: the [0,1]x[0,1] square described by your uv texcoordset. Each uv pair that will be interpolated and present in your fragment shader can be used to recover a normal vector like you would in the vertex shader. That is useful only if the water surface is relatively far away from the camera: since you can't deform/displace vertices, you can observe light reflecting in different directions and shading that surface according to a time variable. It should work in a sense and it should be easier and more convincing than distorting/moving a texture through texcoord manipulation as older game effects do.

Implementation details enter image description here Consider the above picture to be your water surface given as a quad. When you prepare it for shader drawing, you must supply a texture to it (if you don't want to colour it solidly). When you bind a texture to it, for that to work, you mus associate to each of the vertices an attribute containing the texture coordinates. This is the so called uv set and, if there's no tiling involved on your quad, these could be given as A(0,0), B(1,0), C(1,1), D(0,1), where A, B, C, D are the vertices of your quad.

in the vertex shader

compute the position as you normally would and pass the uv you get as an attribute input variable to the fragment shader through a varying variable. The other, simpler way, could be to pass the object frame vertex coordinates (the positions prior to their multiplication through the worldvieprojection matrix). The varying descriptor will make sure the fragment gets an interpolated position on the quad. Let's call this variable xy and pass it to the frag shader as said.

in the fragment shader

Pass as an input uniform the world transformation matrix because you'll have to transform the computed normals to align with your lights position. Alternatively, pass the light already transformed in the object's own frame of reference and use the directly computed normal (should be quite faster and more elegant).

xy is a 2D vector and we need a 3D point on a water surface. Gerstner waves associate to a flat rectangle a height for each of its inner points. If you then consider the union of all of those xy displaced via that height function, you get the wave surface. We can't discuss positions in the frag shader, but we can compute and use normals for other purposes.

The normal you are looking for can be computed from the xy pair following this rationale: http://mathbin.net/105022 . For the waves to be animated, make sure to pass a time instance as an input uniform to the fragment shader. It is required as an input for the H(x,y,t) height function. That should be all. The rest is just plain lighting computation in a frag shader.

share|improve this answer
1  
Wow, that article really IS a GEM, the entire book seems awesome. I didn't knew about it, because I only searched for embedded systems resources, but the math is the same of course. So, what you're saying is that I should have a "waves" texture map and for each fragment in that texture, I should calculate the normals for my plane depending on textures' intensity values? –  Valentin Radu Aug 10 '12 at 15:02
    
Yes, you could view the set of texture coordinates that you associate to your vertices as faux vertices in your fragment shader. You have as many vertices as fragments in this case and you get them via interpolation in the primitive assembly stage. I'll try to update my answer with a simple figure and some implementation details later on today. Cheers! –  teodron Aug 10 '12 at 15:59
    
That would be grand! Thanks! –  Valentin Radu Aug 10 '12 at 16:50

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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