Tuesday, August 25, 2009

Water Shader Update & Atmosphere Added

I reintegrated the atmosphere shell/shader and updated the water shell with a heightmap of the terrain under it so I can have the water depth affect the transparency and color and I think it adds a lot - although now everything looks tropical - which I don't mind.

Water Depth Effects:

Also I made my underwater texture have fake caustics (static) on them which is subtle but nice:

Fake Caustics texture:

From Above:

And here are some pictures of the atmosphere and water shader together from various spots on the planet.

You can view all the My First Planet Images in the gallery section on my website

- I pretty much eliminated banding in the generated terrain texture images by increasing the resolution of my terrain height maps from 8bit to 16bit.
- The atmosphere still has problems and needs tweaking - there is no sun and the sky to space transition is too abrupt amongst other things.
- PSSM VSM shadows slowed the frame rate down too like 30-40 fps so I left them out for now.
- I need to solve the issue of how noticable texture repeating is from up in the atmosphere and I need to add some noise to the texture generation to make things more interesting.

Wednesday, August 12, 2009

Mipmapping RTT in Ogre3D and Look Up Table Filtering

Two things to write about in this post about the texture generation. First of all, when you create textures via render to texture in Ogre3d you do not get mipmaps auto-generated for you. Strangely enough, DirectX still seemed to do some kind of mipmapping, but OpenGL did not. To implement it in Ogre3d you have to do a render to texture for each mipmap level you want to generate. Here's how I did it...

for(unsigned int i = 0; i <= numMipMaps; ++i) {
// select mipmap level
target = mTexture->getBuffer(0,i)->getRenderTarget();

if(target->getNumViewports()) {
// get an existing viewport
mViewport = target->getViewport(0);
else {
// add a new viewport
mViewport = target->addViewport(mRTTCam);
// render the quad
mBeginEnd //issue a _begin and _end render call

The results were decent - not as good as hardware generated mipmaps, but better than no mipmaps and yes, it does affect the speed. Rendering 2 mipmaps isn't so bad a hit but rendering 6+ becomes noticable - sorry I don't have any hard numbers.

Secondly, I was trying to combat texture banding due to the low resolution of the height maps, slope maps, and look up table. I use filtering on the height maps and slope maps so those are blended decently, but the look up table couldn't be filtered because the look up table is really 16 look up tables combined into one texture. When I introduced filtering on the look up table the banding would be lessened but I'd get artifacts where there was bleed between look up tables. Recall the look up table texture is a 1024x256 texture that is 4 256x256 textures end on end horizontally. So the horizontal access is all that really matters because vertically there is no bleed (the texture address mode is set to clamp, not wrap). The horizontal access is the slope value and so what I did was compress the slope value and center it on the middle of the look up table texture so instead of values being from 0 - 255, now they are from 15 - 239 so I don't have to worry about bleeding artifacts anymore (no pun intended).

Here's a couple before/after images showing how the trilinear filtering on the look up table improves the banding issue:

Without filtering...

(click to zoom in)

And with filtering...

(click to zoom in)

1. How can I use Multiple Render Targets to render textures faster - group them up so I can render 4 textures with one shader?
2. How can I cheaply introduce noise to the altitude and slope values to make the blending better and more random looking?
3. Why is the terrain slightly blueish and dark?
4. When should I work atmosphere and water back in.

Saturday, August 8, 2009

Planet Terrain Texture Generation Optimization Continues

I put a lot of profiling code in Ogre's SceneManager::manualRender() function and in SceneManager::_setPass() as well as in the GL_RenderSystem::_render() function etc. Turns out my profiling code in GL_RenderSystem seems to have skewed my profile results.

Profiling the code wasn't giving me many optimization ideas so I took another approach. You know how when you have slow code sometimes you just cut parts of it out till you find out what is slow about it - not the best way to optimize but it can give you some insight when you don't fully understand the design and implementation of libraries you are using (Ogre3d in my case). I could see from the profiling results that it would be good to focus on the Render To Texture(RTT) code so I started removing all the texture look ups in my RTT shader except one and the timing/stuttering issues improved noticeably. I started by adding back in each texture look up one by one to see if adding a certain texture brought back the stuttering/slowness.

It seems that some of my terrain textures were 1024x1024 while the rest were 512x512 (I have 16 terrain textures that get blended in the RTT shader). So I converted those to 512x512 and that helped - and it seems that going above ~8 512x512 textures gives unacceptable slowness and more stuttering. For some reason this is only an issue with OpenGL Render System not DirectX Render System which seems to handle 16 blends fine with no stuttering and faster render times. I may look into this more later because I think I need at least 12 terrain textures.

I might try having several large shared height maps and slope maps - one per cube face to start - instead of one small height map and slope map per terrain patch. I just like the simplicity of the current approach, but it is probably going to be too slow.

Also, I've moved up to the latest Ogre release v1.6.3

Lastly, I tried GLIntercept which was very nice and easy to use and can output textures, all the OpenGL calls and lots more. Did I mention it's free?

At some point I need to start saving rendered terrain textures and height maps and normal maps to disk then reading them back from disk instead of generating them every time.