Tuesday, May 24, 2011

Ou est le swimming pool? #1


What a shame, the world didn't end yet. And of course that old fool from Family Radio didn't admit he was wrong either. "What can I say, I'm not a Genius." Yeah, God works in mysterious ways, so maybe you'd shut up next time? Making up stories to scare the hordes of sheep, tssk. For those who spend all of their money before 21 May: HAHAHA. Read Darwin next time.

Sorry, I got carried away. Still a little bit angry about this documentary from Louis Theroux I just on Belgian TV one week ago:
The most hated family in America
Very cute, but fundamental idiots like grandpa Harold or those intolerant assholes from the link above caused dozens of wars, crusades and other stupidity throughout history. Claiming to be God’s messengers, but doing nothing but threatening with Hell, Doom, Sinfloods… Let me say this:
- For the mouth speaks what the heart is full of (Matthew 12) -
And now go jerk off yourselves.



To the happy news then. First, we get some more help on the modeling part. Another bandito from Spain, Sergi.
http://nueveparadas.blogspot.com/
He’ll be making models, maps, and eventually some of the textures that are required for them. Let's hope he can boost the development speed! The other good news; I just started adding some water-effects.
=====================================================================
WATER
=====================================================================

Do you want to go to the plage with me?

H20, the magical substance that is believed to be the source of life. And the source of eye-candy when it comes to visual scenes. Where there is water, there is life. No wonder scientists are curious about what they might find on Europa, one of Jupiters moon's that is covered with a thick shell of ice.

And where there is water, there are decent graphics programmers (ahum). No doubt that water is one of the eye-catchers when it comes to showing your game(engine). Now I'm not a real expert on this matter, as the last time I implemented it was years ago. So I might missed a few new gadgets. But since one of the readers here asked for some water-rendering tips not too long ago, I thought why not writing a little tutorial? For young and old:

1.- Basic water techniques (non-shaders, beginners stuff)
2.- (Basic) shader tricks
3.- Adding reflections and refractions

Users who already did some water and shaders might skip part one (and two). Although some of the tricks are quite universal. I won't mangle too much with waves and fluid dynamics though, as I simply never did that before. Anyway, have fun!



BASICS
-----------------------------------------------------------------------
You could try to implement an aqua volume that reflects and refracts light exactly physically correct. Or you just fake it with some tricks, with a wink to mister Fresnel. As usual, games tend to pick the second approach. And although water in games like Crysis or Halflife2 looks pretty real by now, we can nicely see how the fake tricks evolved from a blue texture to sparkling clear water.

Doom and Duke Nukem simply painted an animated(2 or 3 frames!) texture on the floors. So much for water. But pretty soon things evolved to a transparent water surface quad, with a moving wave texture rolling on it. You can laugh about it, but we have to start somewhere. Plus some hardware can't do much better anyway (The Wii? Just joking). Well, how do we do this?

- Make a quad(or a grid of quads) -> the water surface
- Apply a seamless (mipmapped) texture that "rolls" over the surface.
You can do this by accumulating the UV coordinates (glTexcoord2f( x+time, y+time )
- Disable specular and diffuse lighting before rendering. Don’t apply light, or
an ambient color only.
- Make it half transparent (glColor4f( rgb, transparency)
- Let it blend. Don’t forget to disable these settings after rendering the water btw!
glEnable( GL_BLEND );
glEnable( GL_ALPHA_TEST );
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Done. You can't get it much simpler than that. But at least most players will understand they are dealing with water here. How about some more effects dude? Sure.


Multi Texturing & Caustics
-----------------------------------------------------------------------
Apply the wave texture twice, using multi-texturing. Let the second texture scale and move in a different direction. Eventually use a different (wave) texture.
http://www.opengl.org/resources/code/samples/sig99/advanced99/notes/node60.html

Hey… if you can draw a second(or third, or fourth) texture on that water-plane… Then it’s also possible to draw a “caustics” texture onto the walls/floors/ceilings nearby the water-surface right? Sure, you can use the very same wave texture, grayscale it, and render it on top with additive blending modus. Man, you can even render it on the objects / characters that wade through the water.

Underwater fog
-----------------------------------------------------------------------
You can cut your world in 2 halfs: everything below and above the water surface. Everything rendered below the surface can use a much thicker fog value. This effect will make deeper or distant underwater polygons fade out in a basic color.
Use dark blue for an ocean, or brown for a muddy canal. If you want to do it really good, you'll need to use "height-fog". This is not a standard OpenGL feature, and requires a shader though, but a very simple one :)

Or instead of using traditional fog, you can simply decide the fog value based on the height of a vertex(or pixel, if you can use shaders).

fog = min( 1.0f, (watersurface.y - pixel.y) * thicknessFactor );

This would be a crude pixel-shader formula. If you can't use pixel-shaders, you can still do it per vertex. For each vertex you render, use glColor3f( fogRGB );, where fogRGB is computed based on the vertex height. However, this won't work very well if you have relative large polygons though. If you wonder in which order you should render things now:
1.- Stuff underwater with fat fog.
2.- Stuff above water with normal fog
3.- (Transparent) waterplane


Here the Swimming Pool map anno 2011. I only had a few textures to decorate it, and no other contents. But here some first water effects nevertheless. The brownish look is simply (height) fog. The deeper the pixel, the darker. I didn't make caustics yet by the way...


Waves (& LOD)
-----------------------------------------------------------------------
A single flat quad can’t make 3D wave shapes of course. Split your quad up in multiple smaller quads ("subdivide"). When rendering the water surface, you'll calculate the height(Y) coordinate for each vertex based on a wave formula. Now this can be damn tricky! I have zero experience with wave formula's, but I suspect it's a mixture of lot's of sines & factors. So let's keep the code simple, you can go figure out that formula yourself :)

function wave(x,y:single) : single;
begin
// Very simple wave function...
// _elapsedTime just increases with the deltatime every cycle
result := cos(x + _elapsedTime) + sin(y + _elapsedTime*4) * 0.2;
end;
...
For y:=0 to 100 do begin
glBegin( GL_QUAD_STRIP ); // Render horizontal rows of 1x1 meter quads
// Render the first 2 vertices on the left side
vertex1 := vec3(0, wave(0,y+0) , y+0);
vertex2 := vec3(0, wave(0,y+1) , y+1);
glTexcoord2f( 0 * uvScale, (y+0)*uvScale ); glVertex3fv( @vertex1 );
glTexcoord2f( 0 * uvScale, (y+1)*uvScale ); glVertex3fv( @vertex2 );
for x:=0 to 5 do begin
vertex1 := AffineVectorMake(x, wave(x,y+0) ,y+0);
vertex2 := AffineVectorMake(x, wave(x,y+1) ,y+1);
glTexcoord2f( x*uvScale, (y+0)*uvScale ); glVertex3fv(@vertex1 );
glTexcoord2f( x*uvScale, (y+1)*uvScale ); glVertex3fv(@vertex2 );
// Eventually calculate the normals here as well,
end; // for x
glEnd;
end; // for y


LOD (Level of Detail)
Wait a minute. I end up with a few billion vertices if I want to make waves on a 10x10 km water surface! So, decrease the detail for distant quads. If you look over an ocean, do you see any waves at the horizon? Unless you have a monster tsunami, you won't. Simple, because it's way too far to see such details. So neither do we have to render them.

The trick is to make the grid less dense at distances. But how? Again, I never did LOD on heightfields or water-surfaces so I have to apologize. But I might have some ideas though. From the top of my head:

1- Make a 3x3 initial grid. Around the camera(player)
2- Evaluate all (9) quads you just made
3- If 1 or more points from a quad are inside X meters from the camera (just check the distance, simple enough), subdivide the quad:
* don't do any (wave/heightMap) height calculations yet!
* don't think about triangles or OpenGL yet, just manage cells and points

4- Repeat step 2 & 3, but with a smaller radius around the camera.
Do this until the radius comes nearby the camera. The more steps you take, the
more divisions (but also the more work of course).

You don't have a 3D mesh yet, just a bunch of quads/points. Now you have to do 3 or 4 things to make soup out of this:

- Triangulate the shape (=make triangles)
- Calculate uv coordinates (interpolate)
u = ((point.x - fieldOffset.x) / totalFieldWidth) * repeatUcount
or
u = (parentQuad.leftVertex.u + parentQuad.rightVertex.y) / 2
- Calculate height ( y = getHeight(xz) or getWave(xz) )
- Optional, calculate normals (based on your surrounding point coordinates)

Sounds not too difficult, except for the triangulating part. You can keep track in each cell(quad) while dividing. Normally, a cell has 4 points on the corners. When subdividing, a new point will occur in the center, and 4 new cells will be generated. More tricky are cells that have extra points from a divided neighbor cell. See the picture for possible triangulating options.

LOD is a useful technique for large surfaces such as water with waves or heightMaps (terrains). The implementation above is just my quick thought. You might find better ways on the web.



Specular light
-----------------------------------------------------------------------
So far we didn't use any lights except an overall ambient color. Which might be a little bit odd of course. If you shine a flashlight on the bathtub, you won't see a nice circle spot caused by diffuse light. But what you do see is reflected light (=specular light). Well, sort off. Don't shoot me if that is not exactly correct, I slept during physics lessons.

Anyway, as water often comes with a sun or moon in outdoor scenes, you can activate specular light from that specific source. Be careful, you'll need sufficient triangles to do this (or use per-pixel lighting(and thus shaders)). That means you have to sub-divide your water quad. See LOD above. If your triangles are too big, the specular light effect will be chunky as well.

If you are doing real 3D waves, you will need to calculate correct vertex-normals as well, or your specular light suck.

Here you can clearly see the effect of specular light. Per-pixel specular with simplified Fresnel that is though.


Miscellaneous
-----------------------------------------------------------------------
I’ll keep it short. Don’t forget water splashes, underwater bubble sprites or drawing (additive transparent) ripple sprites on top of the surface whenever something falls in the water. There are advanced tricks for this as well, but with some simple sprites and particles you can come quite far already. Don’t underestimate the impact of these effects! You can have a horny Lagune-shader, but the illusion of water will still break in thousand shards when a fat man bombs himself into the water without making a splash.



So, enough for this long post. To be continued. Take two or three weeks to read it, cause I’m going to Poland this Sunday for a little vacation… If that fucking Volcano in Iceland let us go, that is…

4 comments:

  1. another detailed and interesting articles, and good screenies too !! :)

    ReplyDelete
  2. It's amazing to see how you can do the things in OpenGL, especially with these good results.

    My knowledge in OGL stop with GLScene lol.

    great work

    ReplyDelete
  3. Incredibly informative! awesome post as always.

    ReplyDelete
  4. Hi, thanks for giving me the tips few months ago about water rendering:) And here we have a much more detailed tutorial :)

    Have you seen the watter effect in Resistance 2?
    Check this out on you tube:

    Resisistance 2 Water Tech

    Regards:)

    ReplyDelete