Smooth terrain normals, multithreaded texture loading, etc.

Published December 21, 2010
Advertisement
Hey guys,

Today's topics include:
- Terrain Tangent/Normal Smoothing
- Multithreaded loading of textures in background
- Entity Imposters cast shadows
- Foliage Imposters account for terrain lighting

Terrain Tangent/Normal Smoothing
I'm sure some of you noticed the sort of 'stairstep' artifacts on the terrain rendering in recent screenshots ... since I'm packing the height/normals into the RGBA channels of a single texture and using vertex-texture-fetch to sample them inside the vertex shader. So when saving the map I do a pass to render all these values into a single texture. Before today I wasn't doing a smoothing pass - and I wasn't doing any filtering inside the vertex shader when constructing the normal ( I'd have to do 4 samples of the height and construct the normal - so I write it into the R,G,B channels then construct the tangent/bi-normal inside the vertex shader ). The solution was to do 9 samples when saving the normal/height texture and average the normals which produced much better results ;-) I should have taken care of that a long time ago.

Multithreaded loading of textures in background...
For a while I've had a texture caching system that keeps a 32x32 or smaller version of all textures possibly needed in the game. When the system gets a request for a high resolution version of a texture it loads it into memory and lets the system know the high res version is available. This worked alright for a while - until I started using models with sets of 2048x2048 color/normal/specular maps all over the place. Then it started to become an issue loading 48MB a piece. If a high resolution texture isn't referenced x seconds after it's been loaded the system un-loads it. So you can see how constant stuttering from texture loads could become a problem. The issue is the Direct3D rendering thread has to pause so the texture can be loaded using the D3DXTextureFromFile() functions.

My solution is to use multithreading - because the D3DDEVICE can only be referenced from one thread at a time (unless you specifiy the multithreaded flag on device creation, which slows everything). The key is to start another thread, load the texture file from the hard-drive into memory, and then once the thread returns and you have the raw data loaded in memory - call D3DXTextureFromMemory from your D3DDEVICE thread. This virtually eliminates thrashing/stuttering from loading resources in the background of the game. I limit the system to starting 1 background texture loading thread. I find that causes the smallest impact on the rest of the game. When running 3-4 background threads at once there were still noticeable hickups in the rendering system.

This system allows me to easily manage many gigabytes of texture data.

Entity and Foliage imposter shadow stuff
In addition to the above stuff I went through and added some more touches to the imposter systems in the game. All of the entity imposters [ for soldiers, tanks, controllable objects, etc. ] now cast shadows into the world. The foliage imposters now take into account the terrain lighting below them. I choose to use this influence instead of just doing shadow mapping, I find the results are much more pleasing to the eye - though they aren't 100% accurate.

On to the screenshots...

Here is an image showing how the terrain normals look now that I'm using a totally different method to compute them, plus a smoothing pass on them.


Here is a comparison screenshot showing how the foliage imposters looked. before/after taking the terrain lighting into account.


Imposter debug screenshots - this is how the imposters look with alpha_clipping disabled - so you can clearly see which ones are imposters and which are geometry. Also visible are the shadows cast by the imposters in both screenshots...

With clip() enabled inside the pixel shader.


Here are screenshots showing the new/smooth terrain normals compared to the old normals...
NEW...

OLD...


Until next time...

- Dan
0 likes 6 comments

Comments

NineYearCycle
Background texture loading, instead of kicking off a background loading thread why don't you have a background loading thread that is always there and add "jobs" or "requests" too it?

That's how I'm used to seeing it done. That way you only ever try to load a single asset at once from a queue of assets that have come from other threads. Part of the "request" is some info about the message to send or a callback to take the loaded asset. That allows the main/rendering/whatever thread to continue doing other things whilst the loading thread handles getting it from disk.

Andy
December 21, 2010 01:32 PM
Jason Z
If you used Direct3D 11, then you could create the resources on the secondary threads too [grin], plus you could do a rendering pass on those secondary threads to eliminate the draw call overhead...
December 21, 2010 02:23 PM
dgreen02
Quote:Original post by NineYearCycle
Background texture loading, instead of kicking off a background loading thread why don't you have a background loading thread that is always there and add "jobs" or "requests" too it?

That's how I'm used to seeing it done. That way you only ever try to load a single asset at once from a queue of assets that have come from other threads. Part of the "request" is some info about the message to send or a callback to take the loaded asset. That allows the main/rendering/whatever thread to continue doing other things whilst the loading thread handles getting it from disk.

Andy


Hmmm, well I don't find any real overhead w/creating and destroying a thread for each texture load. Currently there are no problems doing everything else in the same thread [ obviously things like Havok, RakNet create their own threads ].

Right now I only load a single asset at once as you describe, I guess the only difference would be the overhead to create/destroy a thread for each asset ?
December 21, 2010 05:34 PM
dgreen02
Quote:Original post by Jason Z
If you used Direct3D 11, then you could create the resources on the secondary threads too [grin], plus you could do a rendering pass on those secondary threads to eliminate the draw call overhead...


Ah maybe in a few years once I center the tech around D3D11 [grin]

IIRC all the D3DXLoadTextureFromXXX() functions have multithreaded counterparts in D3D10+ which load the asset in a background thread?
December 21, 2010 05:39 PM
NineYearCycle
Quote:Original post by dgreen02
Hmmm, well I don't find any real overhead w/creating and destroying a thread for each texture load. Currently there are no problems doing everything else in the same thread [ obviously things like Havok, RakNet create their own threads ].

Right now I only load a single asset at once as you describe, I guess the only difference would be the overhead to create/destroy a thread for each asset ?


No the overhead isn't an issue. It's almost purely an architectural question :)

We used a compressed file loading system where files were carefully organised to avoiding seeking which can be the bane of DVD based media. So everything is organised around loading through this managed interface to the filesystem, like a PAK/GZIP file. Having a load request system just meant that there was a centralised access point to these packs.

Andy
December 28, 2010 12:23 PM
dgreen02
Quote:Original post by NineYearCycle
Quote:Original post by dgreen02
Hmmm, well I don't find any real overhead w/creating and destroying a thread for each texture load. Currently there are no problems doing everything else in the same thread [ obviously things like Havok, RakNet create their own threads ].

Right now I only load a single asset at once as you describe, I guess the only difference would be the overhead to create/destroy a thread for each asset ?


No the overhead isn't an issue. It's almost purely an architectural question :)

We used a compressed file loading system where files were carefully organised to avoiding seeking which can be the bane of DVD based media. So everything is organised around loading through this managed interface to the filesystem, like a PAK/GZIP file. Having a load request system just meant that there was a centralised access point to these packs.

Andy


Ah, yes I use my own propriatary .pkg system to hold all the files...I don't have to deal w/DVD access yet.

December 28, 2010 08:49 PM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement