Not a feature update today, more of a thought-of-the-day(tm):
I spent quite a few hours the other day
trying to work out how a specific texture in my project was apparently being
point sampled while others using the same sampler states were being tri-linear filtered as they should have been. I tried using Pix to determine just what state the GPU was in but without much success - Pix doesn't seem to like my project very much.
This led on to much poking around
and experimental changes looking for more evidence that might point to the underlying problem. It turned out in the end however to be nothing
to do with samplers or really DirectX at all – there was a bug elsewhere in the C++ code that generates the texture co-ordinates for the geometry in question which was causing it to make them offset massively
from the origin, which in turn led to a catastrophic loss of precision in the GPU
interpolators creating the point sampled effect I was seeing.
Usually having
stupid (u,v) values causes texture wriggling or shimmering either of which would have given the game away immediately but in this
particular case it happened to make the texture look perfectly stable but point sampled.
The moral? Try to avoid myopia when
tracking down bugs – just because it looks like a duck and quacks like a duck doesn't necessarily mean it really is a duck - it might just be a dog.
Charting my attempts to create a procedural Universe and other random thoughts...
Friday, January 27, 2012
Wednesday, December 7, 2011
Seeing the Wood for the Trees
Moving from the planetary scale to something a bit smaller, I've been experimenting with adding some vegetation to my terrain based planets - specifically trees for now although I also hope to add grass, flowers and other flora in due course. Sticking with the Osiris project's procedural generation axiom I want to use algorithms to create not just the placement of the trees but also their appearance as far as possible. I need to be pragmatic however and while I doubt it's practical to procedurally generate down to the individual leaf level it feels like there is a suitable compromise to be found
There are several decisions that need to be made when adding trees to a terrain model and functional requirements that need to be implemented:
Where to place the trees?
The first thing to decide is where to put the trees. A planet is a pretty big place and while I want some nice densely forested areas it wouldn't be very interesting to have a uniform covering of trees everywhere - there needs to be some logic to it. This is one of the areas that I haven't spent as much time on as I hope to, an interesting reference I have found though is Generating Spatial Distributions for Multilevel Models of Plant Communities which appears to offer a fairly practical yet real world representative algorithm for modelling where plants should grow within an area and selecting from a range of tree types.
A larger scale decision needs to be made first though to identify the areas to be populated in the first place. Ideally this would be a mixture of randomness combined with climate and geophysical parameters so for example particular types of tree would tend to grow near water, within certain height ranges, within certain latitudes and on particular types of soil and slope.
For now however to get the system going so I could focus more on the generation of the trees and rendering them I have opted for a simpler noise based approach where each terrain tile is assigned a possible maximum number of trees then for each of these potential trees a random point is chosen within the tile. This point is then used to evaluate an exponential turbulence function, low values of which are ignored to create gaps between the tree clumps while high values are used as the probability that a tree will indeed be placed at that point. Because of the marble vein like pattern formed by such a turbulence function this tends to produce interesting shaped clumps of trees with organic looking edges.
This really is a quick and dirty approach and doesn't even take into account factors such as the slope of the terrain - the only limiting factor in fact is that trees aren't placed below the water line!
What type of tree to grow?
Generating a unique tree mesh for each of potentially millions of tree instances is obviously prohibitively expensive in terms of computation and memory so I instead store a palette of tree type meshes one of which is used for each instance placed on a terrain tile. Each instance can have a unique orientation around the tree's primary axis along with a unique scale so there is still scope for plenty of variation on the terrain even with only a limited selection of meshes to choose from.
The decision as to which tree from this palette to use at each point is fairly closely tied to the above decision about where to place the tree in the first place. The same environmental and geophysical attributes that drive one inform the other as you should for example expect fir trees on colder steeper terrain and palm trees in hotter drier climes.
For now though I took the simplest of shortcuts and pick a random entry from my tree palette.
What should a tree look like?
This is one of the areas I've spent more time on as it's central to getting pretty much anything tree like on screen. Of course I could simply gone out to TurboSquid or similar and picked up some tree meshes that I could have plonked down on the terrain but that wouldn't really be holding true to the whole procedural ethos - it also wouldn't have been very interesting.
Instead, I wanted to be able to "grow" my own tree meshes using procedural techniques which would be both an interesting technical challenge and allow me to produce as many variations as I felt necessary without finding pre-built meshes for each. There are a couple of fairly well known approaches to growing trees in this fashion, space colonisation such as utilised here can produce good quality results while L-System or grammar based approaches are also highly effective.
I decided for this project on a fairly procedural parameter driven approach as I felt it was a little more controllable for producing a wide variety of plants although I think it would be interesting to try space colonisation at some point if time allows. My primary reference is Jason Weber and Joseph Pen's excellent paper Creation and Rendering of Realistic Trees where they describe a procedural algorithm that takes a set of parameters describing first the trunk, each of three generations of stems/branches and finally leaves to constitute a complete tree.
The parameters offer a great deal of control over the primary visual characteristics of the tree and as they helpfully provide a detailed description of the algorithm and exemplar parameters for four types of tree it's reasonably straightforward to implement and get something usable out of it - an achievement not all published papers manage in my experience. To speed up development however I decided not to implement the entire system in C++ but instead use the Lua scripting language that I had previously integrated to facilitate procedural architecture generation (which I plan to post about in the future). The core algorithm from their paper is written in Lua which generates a representation of the final tree from it's parameters, there is a shared common file that implements the guts of the code and a separate much smaller file per tree type that pretty much declares the parameters for the tree in a Lua table then makes a few function calls into the common code passing those tables as parameters.
Using Lua makes for very rapid iteration as I can change one of the files in Notepad++ then Alt-Tab back to Osiris and have it instantly reload the file and regenerate the tree mesh without having to stop the program. This makes it painless to not only find bugs in the code but also to play with the tree parameters an an almost interactive basis - something that is pretty handy when trying to map parameters designed to produce tree meshes with potentially millions of polygons down to some that produce a similar result with just a couple of thousand.
Once the Lua code has produced the tree representation then the C++ takes over to do the heavy lifting of turning that into a renderable mesh. I've made four types of tree so far using modified versions of the parameters from the Weber and Pen paper:
The tree palette used at runtime also includes leaf-less versions of these to represent dead or sickly trees and to add a bit of variety, there is currently a 15% chance that any particular tree instance won't have leaves.
Rendering trees
With tree meshes to render and a crude approximation of where to place them the final challenge is of course actually rendering them. There are two main challenges that face real-time applications when it comes to rendering trees and flora in general.
Firstly the sheer number of vegetation instances that a typical view can contain presents a major challenge. As you may have noticed from the screenshots above each tree model can contain from a couple of thousand triangles up to ten thousand or more. Even with the phenomenal grunt of modern PC graphics cards there is a very real limit on the number of those that can be drawn at interactive frame rates. Off-screen culling is an obvious start with trees that are off the sides of the view or behind the camera being ignored but on a panoramic view that can still leave many tens of thousands to be processed.
The solution is pretty obviously a level of detail (LOD) approach where trees that are nearer the camera are drawn using a higher resolution representation than those further away - the distant trees are very small on screen so if done well the drop in fidelity shouldn't be very obvious. To this end, once the Lua code has generated the tree representation the mesh generator produces not one but three different versions of the mesh each storing less detail than the previous one. LOD level #0 is the maximum detail version (shown above), level #1 uses fewer segments for the trunk and primary branches, fewer leaf fronds and removes the secondary branches altogether while level #2 reduces the trunk segments and leaf fronds yet further and also loses the primary branches leaving just the low detail trunk itself and the leaves.
By scaling up the remaining leaf fronds slightly in each LOD the apparent volume of the tree is preserved despite being made up of significantly less triangles. Which leaf fronds to keep and which to remove at each level of detail is based upon the K-Means Clustering algorithm to ensure as even a distribution of remaining fronds as possible in an effort to also preserve the tree's overall shape and silhouette.
Although the lowest lod mesh above uses only a handful of triangles when drawing tens of thousands of them it's still a lot of work for the GPU so an additional LOD level is also produced by rendering the maximum detail version of the tree to a render target then saving that off as a texture. This texture can then be used as a billboard sprite to render the most distant trees using just two triangles each. As this billboard sprite is aligned to always face the camera when rendered to the screen several views of the tree are generated from different elevations to prevent the visual effect of the trees looking like they are lying down when viewed from above.
Here you can see that a side elevation is produced along with views from 30 degrees, 60 degrees and 90 degrees of elevation. The appropriate section of the texture is chosen when rendering the billboards to most closely replicate the actual viewing elevation.
I've shown the effect large on screen here for clarity but of course in practice this is all happening on trees that are only a few pixels tall so the crudeness of the effect is effectively hidden.
To complement the tree LOD system I also use hardware instancing where a single tree mesh can be drawn multiple times by the GPU without requiring multiple draw calls from the program. In my scenario each terrain tile has a secondary vertex buffer containing the positions, orientations and scales of each tree instance to draw which is used in conjunction with the vertex buffers for the tree meshes themselves to allow all instances of a particular tree type within the tile to be drawn with a single draw call.
Such hardware instancing conflicts completely with off-screen culling however as the program doesn't get a chance to test individual tree instances so I only use it for the billboard level LOD rendering where the cost of each instance to the GPU is very low and any culling done on the CPU would probably be more expensive than just trying to draw the off-screen billboards.
Alpha Blending
The second challenge with vegetation rendering is that leaf textures tend to work better when they can be alpha-blended into the scene allowing their rough edges to fade out to produce a nicer effect. For this alpha blending to work properly however the triangles need to be rendered in a back-to-front order so leaves at the back of the tree are drawn before those at the front - performing this sorting is prohibitively expensive on the CPU though and not really an option. Alpha blending also does not play well with the deferred rendering that is used to draw the terrain itself - a double whammy.
For now then I have to live with hard edges to my leaf textures which avoids the sorting problem but definitely doesn't look as good. There are some minor tricks that can be used to improve the situation however by playing with the alpha threshold that is used when deciding which pixels on the leaves are transparent and which are not. As the alpha value on the texture varies smoothly across the boundary from transparent to semi-transparent to opaque pixels by choosing where the threshold is for non-alpha blended rendering I can control how much of the texture is visible. A low threshold for example will cause most of the leaf texture to be used with only the absolutely transparent areas hidden while a higher threshold will cause more of the texture to be considered transparent with less and less rendered to the screen.
By varying the alpha threshold on a per-leaf basis a couple of aesthetic improvements can be made. Most significantly by using the angle between the leaf's normal and the view vector to vary the threshold leafs that are almost edge-on to the view can be made to render less of their pixels removing many of the unsightly splinters of polygon that would otherwise result:
A more subtle effect along the same vein is to also vary the alpha threshold of a leaf based upon it's distance from the centre of the tree. The main aim here is to allow the branches to stick out more around the periphery of the tree which I think looks a little more realistic:
Future Work
While I'm quite pleased with the work so far there are still some fairly major areas I would like to develop. The most obvious one to me is the inclusion of shadow mapping on the terrain so the trees and everything else I place there don't look like they are floating around above it. While it's a reasonably complex effect to get right I think this alone would make a massive improvement to the visual appearance of the terrain and the trees. Other ground cover such as grass and flowers would also add a lot providing something of the ground clutter found in typical real world scenes.
While the tricks I employ improve the leaf edges alpha blending would definitely make them better. I've been reviewing an article in GPU Pro where alpha blending for vegetation is performed as a post-process using a combination of additive and maximal alpha values. This would sit well with my deferred rendering pipeline although it would incur some additional render cost - definitely an avenue I want to explore though.
The placement of trees could obviously be far more intelligent although this will require a degree of topographic and environmental data that I currently don't simulate. Adding more tree types such as palm or fir would also help add variety along with autumnal or snowy appearance styles where the climate dictates.
This post is already way past the TL;DR threshold though so I'll leave it there for now. Hopefully if you made it this far you found it interesting. I'll leave you with a few more screenshots:
![]() |
Terrain based planet with trees |
- where to place the trees
- what types of trees should be present at each point
- create a realistic looking mesh for each different type of tree
- be able to render potentially tens of thousands of trees at real-time frame rates
Where to place the trees?
The first thing to decide is where to put the trees. A planet is a pretty big place and while I want some nice densely forested areas it wouldn't be very interesting to have a uniform covering of trees everywhere - there needs to be some logic to it. This is one of the areas that I haven't spent as much time on as I hope to, an interesting reference I have found though is Generating Spatial Distributions for Multilevel Models of Plant Communities which appears to offer a fairly practical yet real world representative algorithm for modelling where plants should grow within an area and selecting from a range of tree types.
A larger scale decision needs to be made first though to identify the areas to be populated in the first place. Ideally this would be a mixture of randomness combined with climate and geophysical parameters so for example particular types of tree would tend to grow near water, within certain height ranges, within certain latitudes and on particular types of soil and slope.
For now however to get the system going so I could focus more on the generation of the trees and rendering them I have opted for a simpler noise based approach where each terrain tile is assigned a possible maximum number of trees then for each of these potential trees a random point is chosen within the tile. This point is then used to evaluate an exponential turbulence function, low values of which are ignored to create gaps between the tree clumps while high values are used as the probability that a tree will indeed be placed at that point. Because of the marble vein like pattern formed by such a turbulence function this tends to produce interesting shaped clumps of trees with organic looking edges.
![]() |
Tree placement probability map for a terrain tile generated from a turbulence function |
![]() |
Birds-eye view showing the resultant tree placement from the probability map |
What type of tree to grow?
Generating a unique tree mesh for each of potentially millions of tree instances is obviously prohibitively expensive in terms of computation and memory so I instead store a palette of tree type meshes one of which is used for each instance placed on a terrain tile. Each instance can have a unique orientation around the tree's primary axis along with a unique scale so there is still scope for plenty of variation on the terrain even with only a limited selection of meshes to choose from.
The decision as to which tree from this palette to use at each point is fairly closely tied to the above decision about where to place the tree in the first place. The same environmental and geophysical attributes that drive one inform the other as you should for example expect fir trees on colder steeper terrain and palm trees in hotter drier climes.
For now though I took the simplest of shortcuts and pick a random entry from my tree palette.
What should a tree look like?
This is one of the areas I've spent more time on as it's central to getting pretty much anything tree like on screen. Of course I could simply gone out to TurboSquid or similar and picked up some tree meshes that I could have plonked down on the terrain but that wouldn't really be holding true to the whole procedural ethos - it also wouldn't have been very interesting.
Instead, I wanted to be able to "grow" my own tree meshes using procedural techniques which would be both an interesting technical challenge and allow me to produce as many variations as I felt necessary without finding pre-built meshes for each. There are a couple of fairly well known approaches to growing trees in this fashion, space colonisation such as utilised here can produce good quality results while L-System or grammar based approaches are also highly effective.
I decided for this project on a fairly procedural parameter driven approach as I felt it was a little more controllable for producing a wide variety of plants although I think it would be interesting to try space colonisation at some point if time allows. My primary reference is Jason Weber and Joseph Pen's excellent paper Creation and Rendering of Realistic Trees where they describe a procedural algorithm that takes a set of parameters describing first the trunk, each of three generations of stems/branches and finally leaves to constitute a complete tree.
The parameters offer a great deal of control over the primary visual characteristics of the tree and as they helpfully provide a detailed description of the algorithm and exemplar parameters for four types of tree it's reasonably straightforward to implement and get something usable out of it - an achievement not all published papers manage in my experience. To speed up development however I decided not to implement the entire system in C++ but instead use the Lua scripting language that I had previously integrated to facilitate procedural architecture generation (which I plan to post about in the future). The core algorithm from their paper is written in Lua which generates a representation of the final tree from it's parameters, there is a shared common file that implements the guts of the code and a separate much smaller file per tree type that pretty much declares the parameters for the tree in a Lua table then makes a few function calls into the common code passing those tables as parameters.
Using Lua makes for very rapid iteration as I can change one of the files in Notepad++ then Alt-Tab back to Osiris and have it instantly reload the file and regenerate the tree mesh without having to stop the program. This makes it painless to not only find bugs in the code but also to play with the tree parameters an an almost interactive basis - something that is pretty handy when trying to map parameters designed to produce tree meshes with potentially millions of polygons down to some that produce a similar result with just a couple of thousand.
Once the Lua code has produced the tree representation then the C++ takes over to do the heavy lifting of turning that into a renderable mesh. I've made four types of tree so far using modified versions of the parameters from the Weber and Pen paper:
![]() |
"Black Tupelo" (4670 vertices and 5988 triangles) |
![]() |
"CA Black Oak" (11490 vertices and 13694 triangles) |
![]() |
"Quaking Aspen" (8969 vertices and 10416 triangles) |
![]() |
"Weeping Willow" (4850 vertices and 6940 triangles) |
![]() |
CA Black Oak without leaves |
With tree meshes to render and a crude approximation of where to place them the final challenge is of course actually rendering them. There are two main challenges that face real-time applications when it comes to rendering trees and flora in general.
Firstly the sheer number of vegetation instances that a typical view can contain presents a major challenge. As you may have noticed from the screenshots above each tree model can contain from a couple of thousand triangles up to ten thousand or more. Even with the phenomenal grunt of modern PC graphics cards there is a very real limit on the number of those that can be drawn at interactive frame rates. Off-screen culling is an obvious start with trees that are off the sides of the view or behind the camera being ignored but on a panoramic view that can still leave many tens of thousands to be processed.
The solution is pretty obviously a level of detail (LOD) approach where trees that are nearer the camera are drawn using a higher resolution representation than those further away - the distant trees are very small on screen so if done well the drop in fidelity shouldn't be very obvious. To this end, once the Lua code has generated the tree representation the mesh generator produces not one but three different versions of the mesh each storing less detail than the previous one. LOD level #0 is the maximum detail version (shown above), level #1 uses fewer segments for the trunk and primary branches, fewer leaf fronds and removes the secondary branches altogether while level #2 reduces the trunk segments and leaf fronds yet further and also loses the primary branches leaving just the low detail trunk itself and the leaves.
By scaling up the remaining leaf fronds slightly in each LOD the apparent volume of the tree is preserved despite being made up of significantly less triangles. Which leaf fronds to keep and which to remove at each level of detail is based upon the K-Means Clustering algorithm to ensure as even a distribution of remaining fronds as possible in an effort to also preserve the tree's overall shape and silhouette.
![]() |
LOD #0 of CA Black Oak, 11490 vertices and 13694 triangles |
![]() |
LOD #1 of CA Black Oak, 1810 vertices and 247 triangles |
![]() |
LOD #2 of CA Black Oak, only uses 40 vertices and 54 triangles |
![]() |
The billboard sprite rendered for the Weeping Willow tree type |
![]() |
LOD #3 of CA Black Oak, four vertices and two triangles |
To complement the tree LOD system I also use hardware instancing where a single tree mesh can be drawn multiple times by the GPU without requiring multiple draw calls from the program. In my scenario each terrain tile has a secondary vertex buffer containing the positions, orientations and scales of each tree instance to draw which is used in conjunction with the vertex buffers for the tree meshes themselves to allow all instances of a particular tree type within the tile to be drawn with a single draw call.
Such hardware instancing conflicts completely with off-screen culling however as the program doesn't get a chance to test individual tree instances so I only use it for the billboard level LOD rendering where the cost of each instance to the GPU is very low and any culling done on the CPU would probably be more expensive than just trying to draw the off-screen billboards.
Alpha Blending
The second challenge with vegetation rendering is that leaf textures tend to work better when they can be alpha-blended into the scene allowing their rough edges to fade out to produce a nicer effect. For this alpha blending to work properly however the triangles need to be rendered in a back-to-front order so leaves at the back of the tree are drawn before those at the front - performing this sorting is prohibitively expensive on the CPU though and not really an option. Alpha blending also does not play well with the deferred rendering that is used to draw the terrain itself - a double whammy.
For now then I have to live with hard edges to my leaf textures which avoids the sorting problem but definitely doesn't look as good. There are some minor tricks that can be used to improve the situation however by playing with the alpha threshold that is used when deciding which pixels on the leaves are transparent and which are not. As the alpha value on the texture varies smoothly across the boundary from transparent to semi-transparent to opaque pixels by choosing where the threshold is for non-alpha blended rendering I can control how much of the texture is visible. A low threshold for example will cause most of the leaf texture to be used with only the absolutely transparent areas hidden while a higher threshold will cause more of the texture to be considered transparent with less and less rendered to the screen.
By varying the alpha threshold on a per-leaf basis a couple of aesthetic improvements can be made. Most significantly by using the angle between the leaf's normal and the view vector to vary the threshold leafs that are almost edge-on to the view can be made to render less of their pixels removing many of the unsightly splinters of polygon that would otherwise result:
![]() |
Without view dependent alpha threshold edge-on leaf polygons are unsightly |
![]() |
With view dependent alpha threshold the edge-on leafs render fewer pixels and produce a better effect |
![]() |
No distance alpha threshold effect, all leaves are consistent |
![]() |
Distance based alpha threshold - the leaves around the ends of the branches are less visible |
Future Work
While I'm quite pleased with the work so far there are still some fairly major areas I would like to develop. The most obvious one to me is the inclusion of shadow mapping on the terrain so the trees and everything else I place there don't look like they are floating around above it. While it's a reasonably complex effect to get right I think this alone would make a massive improvement to the visual appearance of the terrain and the trees. Other ground cover such as grass and flowers would also add a lot providing something of the ground clutter found in typical real world scenes.
While the tricks I employ improve the leaf edges alpha blending would definitely make them better. I've been reviewing an article in GPU Pro where alpha blending for vegetation is performed as a post-process using a combination of additive and maximal alpha values. This would sit well with my deferred rendering pipeline although it would incur some additional render cost - definitely an avenue I want to explore though.
The placement of trees could obviously be far more intelligent although this will require a degree of topographic and environmental data that I currently don't simulate. Adding more tree types such as palm or fir would also help add variety along with autumnal or snowy appearance styles where the climate dictates.
This post is already way past the TL;DR threshold though so I'll leave it there for now. Hopefully if you made it this far you found it interesting. I'll leave you with a few more screenshots:
![]() |
Tree count: 5 Lod-0, 21 Lod-1, 2938 Lod-2, 132459 Billboards |
![]() |
Tree count: 7 Lod-0, 24 Lod-1, 1209 Lod-2, 57326 Billboard |
![]() |
Tree count: 14 Lod-0, 35 Lod-1, 783 Lod-2, 39456 Billboard |
Thursday, November 10, 2011
Planetary Rings
What feels
like a logical progression after my recent experiments with gas giant
rendering, I thought I would next take a look a adding some rings to my planet,
turning it into more of a Saturn than a Jupiter. Reading up on them, Saturn's rings are a pretty complex structure the formation of which is not completely understood - as ever Wikipedia is a good starting point for information about them. Although they extend from 7,000 to 80,000 Km above the planet's equator they are only a handful of metres thick, being made up of countless particles of mainly water ice (plus a few other elements) from the truly tiny to around ten metres across.
![]() |
Gas giant with rings as seen at dusk from a nearby planet |
My approach
for rendering such a complex system is similar in many ways to that for the gas giant itself, rather than
ray-tracing though I opted for a more conventional geometric representation of
the rings and used a single strip of geometry encircling the planet. For now the rings are fixed in the XZ plane
around the planet’s origin but as the planet itself can have an arbitrary axis
in my solar simulation that doesn’t feel like too much of a limitation to be
going on with.
Rendering the
basic geometry produces this:
What’s
immediately apparent is once again we fall foul straight away of the polygonal
nature of our graphics – although the tri-strip here is a pretty coarse
approximation to a circle, no matter how many triangles we add to it we are
still going to be able to see straight edges if we get close enough. Fortunately the solution is simple and by
adding a check to the pixel shader to reject pixels that are more than our
maximum radius from the planet origin or less than our minimum we immediately
get a nice smooth circular ring:
The only
trick here is to ensure the vertices of the ring geometry are far enough away
from the planet origin that the closest distance of the edges of the triangles
is at least the maximum ring radius rather than the vertices themselves being
that distance otherwise the outer edges of the ring will be clipped by the
geometry:
![]() |
Vertices (in black) need to be further away than the ring radius to prevent the ring (in red) being clipped by the triangle edges |
A little trig shows this simply to be:
vertRadius = ringRadius/cos(PI/numSides)
This only applies to the outer edge of the ring as on the inner edge by definition the triangle edges are closer to the centre than the vertices.
Another point to watch is the rings have to of course go both in front and behind the planet itself. This sounds simple but is made slightly more complex that it first appears as I don't have a Z-buffer available when rendering at the planetary scale due to each planet being rendered in it's own co-ordinate space - they are simply rendered back to front - so they don't share a common Z space that could be used for a Z buffer. The range of depths would be massive anyway and likely to cause flimmering artifacts. Add on top of this the fact that planetary atmospheres need to alpha blend on top of whatever is behind them (including the far side of the rings) and a Z buffer isn't really an option.
Instead I render the rings in two passes, the first pass drawn before the encircled planet to provide the bulk of the rings and to allow the planet's atmosphere to alpha blend onto them. The second pass is drawn after the planet has been rendered and uses an additional ray-sphere and stencil test to only render to the portion of the screen covered by the planet and only where the rings are in front of the planet surface.
vertRadius = ringRadius/cos(PI/numSides)
This only applies to the outer edge of the ring as on the inner edge by definition the triangle edges are closer to the centre than the vertices.
Another point to watch is the rings have to of course go both in front and behind the planet itself. This sounds simple but is made slightly more complex that it first appears as I don't have a Z-buffer available when rendering at the planetary scale due to each planet being rendered in it's own co-ordinate space - they are simply rendered back to front - so they don't share a common Z space that could be used for a Z buffer. The range of depths would be massive anyway and likely to cause flimmering artifacts. Add on top of this the fact that planetary atmospheres need to alpha blend on top of whatever is behind them (including the far side of the rings) and a Z buffer isn't really an option.
Instead I render the rings in two passes, the first pass drawn before the encircled planet to provide the bulk of the rings and to allow the planet's atmosphere to alpha blend onto them. The second pass is drawn after the planet has been rendered and uses an additional ray-sphere and stencil test to only render to the portion of the screen covered by the planet and only where the rings are in front of the planet surface.
Of course one
great big slab isn’t very convincing so to get something a bit more realistic I
again opted for a similar technique to the gas giant, I took a nice high
resolution image of Saturn from the internet and cut out a slice of it’s rings,
filtering the result down to a 1x2048 colour ramp texture.
Mapping this
texture onto the ring geometry immediately produces something far more
pleasing:
![]() |
Colour ramp mapped onto ring geometry |
As the ring
geometry has no thickness however, viewing it from acute angles produces some
unsightly single pixel artifacts.
Although in no way scientific, pulling the Fresnel hammer out of the
toolbox however makes the effect far less objectionable:
![]() |
Rings edge-on without Fresnel term showing unsightly aliasing |
![]() |
Rings edge-on with acute Fresnel term - aliasing is reduced at the expense of increased transparency |
Note I’m
using a pretty narrow Fresnel band for this purpose otherwise the ring tends to
fade out too much of the time. The
number of rings in the effect can be varied with a simple scale value just like
for the gas giant.
More Noise
Although the
rings here are pretty decent (largely due to stealing them from a real image I
suspect), there is a distinct lack of detail when viewed from anything
resembling close up.
Ultimately it
would be cool to be able to fade in large numbers of actual meshes to represent
the larger ice chunks that make up the rings when the view is near to or
actually within the rings, but even without that I thought there was something
that could be done to help represent the higher frequency detail these ice
particles and ‘ice-asteroids’ present.
As with so
many other procedural effects, I decided that a bit of noise would probably add
some interest, so I added some code to sample the same noise texture I used for
previous effects. The problem here
though is that the particle detail in the rings is very high frequency in the
cosmic scale of things but from a purely visual basis I wanted to show variety
in the rings from all ranges.
To do this I
re-used a trick from the terrain texturing shader where the scale of the
texture co-ordinates is calculated on the fly using the partial derivative of
the actual texture co-ordinates from the pixel - in this case from the position
of the point being shaded on the plane of the rings. The texture co-ordinates are scaled up or
down in powers of two to produce as close to screen size texels as possible -
in fact two scales are used by rounding the perfect scale up and down to the
closest power of two then blending between these two texture samples to produce
a smooth transition.
![]() |
Ring noise sampled at uniform texel density produces aliasing and tiling at distance |
![]() |
Ring noise sampled at adaptive density to provide more uniform screen coverage without tiling |
The texture
co-ordinate effect you can see here is somewhat akin to mip-mapping but instead
of using a lower resolution version of the texture to represent the same area
of surface at a larger distance, I am instead using the same resolution texture
to represent a larger area at that distance thereby eliminating the tiling effect often seen with high frequency textures at range.
Unlike mip-mapping it also works in the opposite direction, using the
same resolution texture to represent smaller and smaller surface areas as it
gets closer to the camera and each texel covers more than one pixel.
Using this
signed noise value to lighten or darken each shaded point produces a nice fine
grain effect in the rings I hope is vaguely representative of the countless millions of icy
particles that in fact make them up.
As with
everything else in my little solar system the rings need to be illuminated by
the Sun, this is done simply using standard dot product lighting but the little
twist here is to use the noise value from the previous step to slightly perturb
the normal uses for the calculation.
This is a gross simulation of the arbitrary facets of the lumps of ice
making up the rings being illuminated by the Sun and simply adds a bit of
randomisation to the lighting.
A more
dramatic effect which adds solidity to the rings is the shadow of the planet
cast on to them. Rather than using
shadow mapping techniques this is calculated by doing a ray intersection in the
pixel shader from the pixel towards the sun.
To soften the ubiquitous hard edge produced by ray-traced shadows the
distance the ray travels through the planet is used to provide a soft edge
where the distance is small falling off quickly to solid shadow. While not strictly physically accurate when classically treating sunlight as a parallel source, I feel it adds to the effect.
![]() |
Shadow of the gas giant cast onto the rings |
![]() |
Close up of edge of shadow showing the simulated penumbra |
Finally the
colours in the rings can be remapped again using the same RGB vector system I
used before so each final colour component is a dot product of the shader
output and the modulation vector passed in to the shader for that channel
![]() |
Colour re-mapped rings |
![]() |
More drastically colour re-mapped rings :-) |
For now
though, I'll wrap up with another couple of views of my gas giant's rings,
comments as always are welcome!
Tuesday, November 1, 2011
Gas Giants
Moving on from star rendering in my continuing tour of
celestial bodies, I thought it would be interesting to have a go at trying to
procedurally render a different type of entity – Gas Giants.
There are four Gas Giants in our own Solar System, Jupiter, Saturn, Uranus and Neptune each very different from the
other, but for my first go at rendering one I decided on the largest of these –
namely Jupiter – to see how close to it’s distinctive appearance I could manage
at real-time rates.
There is a lot of information available about Jupiter (http://en.wikipedia.org/wiki/Jupiter)
and Gas Giants in general (http://en.wikipedia.org/wiki/Gas_giant)
on sites such as Wikipedia, again the more I found out about the subject the
more interesting it became and I strongly recommend a little background reading
if it’s a subject that has any more than a passing interest for you.
![]() |
Procedural Gas Giant generated and rendered with Osiris |
Composite Jupiter image from the Cassini-Huygens probe (NASA/JPL/Arizona University) |
Like stars Gas Giants are conveniently smooth for rendering
so I decided to use the same ray-tracing approach that I used for stars
described in my previous posts. This is
not only efficient in terms of geometry but also produces lovely smooth spherical
surfaces at all screen sizes which is important as I want to be able to fly
right down near to the surface of my planets.
I also wanted to be able to produce a wide variety of gas
giant effects with only a few input parameters so needed a system that could
make interesting visual surfaces with more variation than just their colour.
As seen in the images above, gas giants tend to follow a
basic structure of having bands of different coloured material at differing
latitudes; these are counter-circulating streams of material called zones and belts and trying to simulate such a major feature seemed like a good starting point. Using the planet space ‘y’ co-ordinate of the
pixel being shaded (essentially the latitude) as a lookup into a colour ramp texture is a nice simple
starting point for this effect. In this
case I generated the (1 x 2048) sized look up texture by taking a slice out of
the above cassini image of Jupiter and filtering it down to the correct size –
this gave me a genuine Jovian colour palette straight off-bat, support for
other colours is described below.
![]() |
Basic colour bands from look-up texture |
Using Noise
![]() |
Noise channel #1 |
![]() |
Noise channel #2 |
![]() |
Noise channel #3 |
![]() |
Noise channel #4 |
![]() |
Combined noise |
![]() |
Colour ramp rendered with noise |
This looks pretty decent from this fairly distant viewpoint
but as the camera moves in towards the surface the pattern loses detail as it’s
stretched out over more screen space.
Although the naturally soft nature of the surface detail makes this far
less unsightly than it would be on a planet surface with more innate contrast,
I thought it could be improved by adding additional octaves of progressively
higher frequency noise.
The problem with adding higher frequencies
however is that you start to get under-sampling artifacts showing up as sparkly
noise in the effect when viewed from too large a distance for that
frequency - an almost classic example of shader aliasing. To avoid this two blend
weights are calculated based upon a combination of the distance of the planet
from the camera and it’s radius. These
two blend weights are passed to the gas giant pixel shader and used to control
the contribution of the upper two bands of noise frequencies. In this way the higher frequency noise blends
in smoothly as the camera nears the surface and fades out as it moves further
away. By choosing the blend distances
appropriately the transition is essentially invisible.
![]() |
Close in to gas giant surface without high frequency noise |
![]() |
Close in to gas giant surface with high frequency noise |
Silhouette Edges
While the plan is to add anti-aliasing in the future (either MSAA if performance allows or possibly FXAA) I realised there is something that can be done in the shader itself to improve matters and provide some other desirable effects. Rather than having a hard edge around the sphere it would be far nicer to have a thin band of alpha pixels fading off around the edge to smooth things out.
Fortunately this is quite simple to do, by using
a Fresnel term calculated with a dot product of the surface normal and the ray
from the camera to the pixel being shaded the alpha value of the pixel can tend
to zero as the surface becomes tangential to the view direction. The only trick required is to control the
amount of Fresnel to use to give a consistent on-screen width of just a couple
of pixels for the fall off zone – without this the alpha zone would grow and
shrink in size as the camera changed distance.
The Fresnel zone is calculated once in C++ and passed to the shader as a
constant.
![]() |
Planet silhouette edges without Fresnel based alpha - aliasing very evident |
![]() |
Planet silhouette edges with Fresnel based alpha - much smoother |
A handy side effect is that by clamping the distance factor
a larger fade out zone can be used when the camera nears the planet surface
producing a completely non-scientific but rather nice atmosphere effect to
soften the horizon at these close-up ranges - this can be seen in the high frequency noise planet close up images above.
Cyclones
Adding noise certainly made the surface of my gas giant more
interesting but I wasn’t satisfied, it still wasn’t quite what I had hoped
for. Reading about gas giants and
looking at the Cassini image at the top of this post it’s quickly becomes
obvious that a primary feature of these planets are large numbers of cyclones
and anti-cyclones ranging in size from barely perceptible to ones such as the
“Great Red Spot” on Jupiter several times larger than the Earth itself
combining to make for a highly turbulent surface.
Modelling planet scale cyclones accurately is a computationally expensive proposition beyond the capabilities of a real time system but there is something that can be done on a purely visual level to give at least a nod towards their presence. To do this I decided to model a system where a potentially large number of cyclones could be represented as a set of cones emanating from the centre of the planet each with their own radius and rotational strength. In the pixel shader the point being shaded is tested against this set of cones and should it fall within one is then rotated around that cone’s axis by the cone’s rotational strength scaled by the points distance from the cone’s central axis.
Testing a point against potentially hundreds of cones in the pixel shader is however a whole heap of processing so an efficient way to do so is required. The method I decided upon was to store the cone information in a relatively low resolution cube map texture that could be sampled using the normal of the planet sphere at the point being shaded to determine which cone the point in question fell within, if any. This way a single texture read provides all the information without having to iterate over each cone individually in the shader.
The cyclone cube map texture uses an uncompressed 32bit format encoded as follows:
Modelling planet scale cyclones accurately is a computationally expensive proposition beyond the capabilities of a real time system but there is something that can be done on a purely visual level to give at least a nod towards their presence. To do this I decided to model a system where a potentially large number of cyclones could be represented as a set of cones emanating from the centre of the planet each with their own radius and rotational strength. In the pixel shader the point being shaded is tested against this set of cones and should it fall within one is then rotated around that cone’s axis by the cone’s rotational strength scaled by the points distance from the cone’s central axis.
Testing a point against potentially hundreds of cones in the pixel shader is however a whole heap of processing so an efficient way to do so is required. The method I decided upon was to store the cone information in a relatively low resolution cube map texture that could be sampled using the normal of the planet sphere at the point being shaded to determine which cone the point in question fell within, if any. This way a single texture read provides all the information without having to iterate over each cone individually in the shader.
The cyclone cube map texture uses an uncompressed 32bit format encoded as follows:
- Red : X component of the unit length axis of the cone that encloses or is closest to this texel
- Green : Y component of the unit length axis of the cone that encloses or is closest to this texel
- Blue : Normalised rotational strength of the cyclone whos cone this texel is affected by
- Alpha : Normalised radius of the cyclone whos cone this texel is affected by, the sign of this value represents the sign of the Z component of the unit length cone axis that is computed in the shader
The format is set up so each byte is interpreted by the GPU
as a signed normalised number so the values -128 to +127 are stored in the
texture bytes which then arrive in the range [-1, +1] when the texture is
sampled in the pixel shader.
The normalised rotational strength of the cyclone and normalised radius values encoded in the texture are scaled in the shader based upon minimum and maximum values passed to it in shader constants.
A little experimentation showed that between 100 and 200 cones of sensible radii could be effectively encoded using a cube map only 128x128 on each side, no two cones being allowed to be within one texel’s worth of angle to avoid filtering artifacts. A benefit of storing axis and radius information in the cube map rather than actual per-texel offsets is that oversampling artifacts in the texture sampling are avoided as the distance calculations in the shader are operating on the same axial values for every pixel affected by that cone – the down side is that each pixel can only be affected by a single cyclone but I am happy to live with that for now.
The normalised rotational strength of the cyclone and normalised radius values encoded in the texture are scaled in the shader based upon minimum and maximum values passed to it in shader constants.
A little experimentation showed that between 100 and 200 cones of sensible radii could be effectively encoded using a cube map only 128x128 on each side, no two cones being allowed to be within one texel’s worth of angle to avoid filtering artifacts. A benefit of storing axis and radius information in the cube map rather than actual per-texel offsets is that oversampling artifacts in the texture sampling are avoided as the distance calculations in the shader are operating on the same axial values for every pixel affected by that cone – the down side is that each pixel can only be affected by a single cyclone but I am happy to live with that for now.
Colour coding the pixels based upon the cone axis they are closest to and therefore affected by produces what is essentially a spherical Voronoi diagram of the points where the central axes of each cone penetrates the surface of the sphere
![]() |
Voronoi diagram showing area of influence of each cyclone |
Colour coding the pixels instead with a greyscale value
indicating the distance from the central axis of their nearest cone produces
the following effect
![]() |
Position and strength of each cyclone |
Clearly
indicating the size and position of each cyclone. Finally, using a combination of this distance
value and a global maximum rotational strength supplied to the shader in a
constant to rotate each sample point around it’s closest cone axis produces the
final cyclone effect:
![]() |
Final Gas Giant with cyclones |
A number of cyclones of different size and strength are
easily visible here – it’s not perfect but I think it’s a worthwhile addition
that makes the overall effect more interesting.
Colours and Bands
The final step is to allow the shader to produce more varied
effects for the potentially large number of gas giants I want my procedural universe
to be able to contain. There are two
additional parameters that I’ve added to help support this increase in variety.
The first is a simple scale value that is used to control
the mapping of latitude values onto the colour ramp texture. By increasing the scale the width of the
bands can be reduced and their number increased while decreasing the scale of
course produces the opposite effect – fewer, wider bands
![]() |
Gas Giant with twice as many bands |
![]() |
Gas Giant with half as many bands |
The second is a colour remapping post-process for taking the
RGB generated by the shader and altering it to add variety. The manner I thought simplest to do this was
to pass three vectors to the shader that are multiplied by the red, green and
blue shader colour values immediately before the shader returns. For example passing (1, 0, 0), (0, 1, 0) and
(0, 0, 1) would leave the colour unchanged as the red output is just the red
input, the green output just the green input and so on.
Passing different values however completely changes the
result allowing the final red, green and blue components that end up on the
screen to be an arbitrary mix of those from the shader proper. Although any range could be used some care
needs to be taken to keep the general luminosity in a sensible range or we end
up with completely black or pure white planets. A degree of over-brightening can however produce some funky alien worlds.
![]() |
Colour remapped gas giant |
![]() |
More dramatically colour remapped gas giant |
Further variation could be achieved by using a different
noise texture, different noise octave
scales and weights or a different colour ramp texture but for now I think this
is sufficient.
Future work
Although the cyclone system was developed for gas giants,
there is no reason it couldn't also be applied to other bodies with
atmospheres. The clouds on my Earth
style planets for example are currently read from a fBm based cube map but it
would be interesting to see how they look with cyclones applied – something
else to put on my already lengthy TODO list I think.
Subscribe to:
Posts (Atom)