Hi IOTD Watchers,
Some of you 'may' remember my IOTD from a few months ago - it was a
Multi-Fractal Terrain demo rendered in sort-of-realtime using ray-casting
onto an infinite, implicit surface with SIMD optimised multi-fractals :)
(demo at http://www.pocketmoon.com)
Well now I'm aiming at the whole planet rather than just a flat terrain.
It's vanilla C & OpenGl.
With traditional terrain rendering, there is a (potentially huge) data set
defining the terrain. Even with wavelet compression I doubt you'd get a
small planet into a reasonable amount of memory - but it may be worth
Another approach, taken here, is to have no stored or precalced heightfield.
The surface of the planet is instead represented by an 'implicit surface',
i.e a function, f, that returns the surface height given a position P. So
h = f(p) :)
'f' is a multifractal function based on the work of Ken Musgrave, who's
currently working on a 'commercial' fractal world called Mojo
(http://www.pandromeda.com). I build up many layers of random noise at
increasing frequencies and magnitudes. For the flat terrain I used Perlin's
Noise2 function within 'f' (http://www.noisemachine.com). For this Fractal
Planet i've moved to the Noise3 function which obviates the need to map a 2D
function around a sphere. The Noise3 function is inherently slow (lots of
lerping) so at some stage I'll implement a SIMD version (as I did for Noise2
previously) which should easily double its speed for PIII users.
The height function is also used to generate the surface texture (based on
an index into a 1D surface type (colour) array - re Musgrave) This makes the
texture match the terrain (varied by latitude).
The surface mesh is a simple bintree with level zero being a 8-sided diamond
(Octahedron). The first screenshot (top left) shows the mesh split down to
level 6 with the whole sphere tesselated down to 17000+ triangle. Each
subsequent image (TL->TR->BL->BR) is a further level (or two) down. NB the
terrain height has been GREATLY exaggerated to aid visualisation!
The intention is to be able to get down to ground level and still see
detail. To get a 10 meter resolution mesh would require around 40 splits
(based on a 6000KM radius planet) which would result in a sphere of >4
trillion triangles! I doubt a even a GeForce3 could handle this ;) So lets
CULL and LOD! Currently I have two pre-Opengl culling routines: Horizon
Culling and View Cone Culling. Horizon culling is done at the level 6 mesh.
At altitude 'a' above the surface, the horizon is sqrt(2*a*Radius+a*a) away
so I do a quick distance check for each tri and stick them in my horizon
culled pot. I then split these tris down to level 12 and carry out the
view-cone cull. This set is where I plan to carry out the final LOD
splitting stage. It will be some sort of ROAM-esque LOD - probably with
splitting based on distance and 'wedgies', with a merge queue to free up
Usually, LOD systems are based on a predefined heightfield so they work
bottom up - simplifying the smallest triangles upwards to larger ones based
on some visual error metric. In this case I have no predefined data so I
work downwards from the large tri's. A key point is that this can be tied in
with the multi-fractal function - each frequency of noise contributes less
and less to the overall height but represents features of smaller and
smaller scale. For a point far away we may need only 2 or 3 octaves of
noise. As we get closer we add more and more additional octaves to get the
fine detail. We only need to split if the split pair is noticably different
when rendered, so for flatish pairs we can defer a given split till we are
much closer. Initially though I'll just try pure distance based splitting on
the final culled triangle set. Once this is in and I've added some flight
controls I'll post a demo.
Hope to release the demo soon at http://www.pocketmoon.com!