tag:blogger.com,1999:blog-38623496148607354142024-03-13T04:51:31.173+00:00John Whigham's BlogCharting my attempts to create a procedural Universe and other random thoughts...John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.comBlogger40125tag:blogger.com,1999:blog-3862349614860735414.post-58330743804526267802015-10-23T13:15:00.001+01:002015-10-23T13:15:22.343+01:00Infinity: Battlescape KickstarterSomething that might well be of interest to fellow members of the procedural content generation community is the recently launched <a href="https://www.kickstarter.com/projects/309114309/infinity-battlescape" target="_blank">KickStarter for Infinity: Battlescape</a><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://inovaestudios.blob.core.windows.net/ibsite/screenshots/tn/01_ingame/screenshot193.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://inovaestudios.blob.core.windows.net/ibsite/screenshots/tn/01_ingame/screenshot193.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://inovaestudios.blob.core.windows.net/ibsite/screenshots/tn/01_ingame/screenshot221.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://inovaestudios.blob.core.windows.net/ibsite/screenshots/tn/01_ingame/screenshot221.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://inovaestudios.blob.core.windows.net/ibsite/screenshots/tn/02_inengine/screenshot_001.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://inovaestudios.blob.core.windows.net/ibsite/screenshots/tn/02_inengine/screenshot_001.jpg" width="400" /></a></div>
<br />
<br />
What started as the ambitious and massively impressive Infinity project many years ago has grown and evolved over time to now underpin the <a href="https://www.inovaestudios.com/" target="_blank">I-Novae technology </a>and it's great to see it hopefully taking a massive step closer to seeing the light of day as a fully functioning commercial production when so many such other such projects wither and fizzle out.<br />
<br />
I am happy to back their efforts and hope by posting here that I may in some tiny way spread the word.John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com1tag:blogger.com,1999:blog-3862349614860735414.post-34982951655141106142015-05-17T21:52:00.000+01:002015-05-17T21:52:14.822+01:00Start Your EnginesTaking a little break from virtual populations I thought it would be interesting to continue the spirit of this project and try out another new technology I've not dirtied my hands with before. Physics (or more specifically mechanics) has been a staple of game development for about ten years now since ground breaking releases such as <a href="http://en.wikipedia.org/wiki/Half-Life_2" target="_blank">Half Life 2</a> and it's popular community modification <a href="http://en.wikipedia.org/wiki/Garry%27s_Mod" target="_blank">Garry's Mod</a> in 2004 but even though such technology has appeared in countless products in the last decade I've never actually had to implement it myself so I thought it was about time.<br />
<br />
Writing a performant and stable physics system from scratch is a complex and exacting process but fortunately there are a variety of freely available physics solutions that can be leveraged on the PC, one of the most well known of which is the <a href="http://bulletphysics.org/wordpress/" target="_blank">Bullet physics library</a> so that's where I started. My end goal for the initial physics integration was to create a vehicle of some sort that could be driven around on my virtual planet so features pertaining to that objective were the priority. After a little research however I found that the vehicle simulation features of Bullet were quite limited so I looked around for an alternative. On second look I found that the <a href="http://en.wikipedia.org/wiki/PhysX" target="_blank">Nvidia PhysX SDK</a> offered a more complete vehicle simulation model so I refocussed my efforts on that.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/-PFmISJwoqI/0.jpg" frameborder="0" height="340" src="https://www.youtube.com/embed/-PFmISJwoqI?feature=player_embedded" width="480"></iframe></div>
<br />
<br />
Although physics can be complex, fortunately PhysX comes with a useful set of samples that were a great aid getting started. Thanks to these it wasn't too difficult to get the SDK linking in and the basic simulation objects for a vehicle up and running. I then hit a wall however as originally I had hoped I could provide some sort of callback to perform the required ray tests for the vehicle against the terrain heightfield but as far as I can tell PhysX simply does not offer this functionality.<br />
<br />
What it does provide is a heightfield shape primitive that would normally be a logical fit for vehicle simulation but as these are square and essentially flat while my plant is neither having to make use of them made implementing my vehicle more of a challenge than I had originally intended.<br />
<br />
In addition to the problem of mapping square physics heightfield grids to my spherical planet the other major problem is the same one facing virtually every other system involved in planet scale production - namely that of floating point precision. For speed PhysX in line with most real time physics simulation systems operates on single precision floating point numbers by default, but these of course rapidly lose precision when faced with global distances. PhysX can be recompiled to use double precision floating point which would probably sort out the issue but the performance would suffer with such numerically intensive algorithms and as I would like to leave the door open for making more extensive use of physics in the future I didn't really want to go down that route.<br />
<br />
The alternative is to establish a local origin for the physics simulation that is close enough to the viewpoint that objects moving and colliding under physics control do so smoothly and precisely even when single precision floating point is used. The key here is that all numbers in the physics simulation are relative to this origin so they never get too big, although for this to work of course the origin itself needs to be updated to stay close enough to the viewpoint as it moves around the planet's surface. As mentioned before my planet is constructed from a subdivided Icosahedron so has as it's basis 20 large triangular patches each of which is subdivided eight times to give 65,536 triangles. As the viewpoint moves closer to the surface these root patches are in turn progressively subdivided each root patch turning into four new patches each made up of the same number of triangles but covering one quarter of the surface area thus increasing the visual fidelity.<br />
<br />
My planet is represented by two radii, the Inner Radius which defines how far the lowest possible point on the terrain (the deepest underwater trench say) is from the centre of the planet and the Outer Radius which defines how far the highest possible point is from the centre of the planet (the top of the highest mountain say). Between these two distances lies the planet's crust that is being represented by the terrain geometry. To produce an approximation of an Earth sized planet I am currently using an inner radius of 6360 Km and an outer radius of 6371 Km giving a possible crust range of 11 Km or about 36,090 feet - significantly less than the corresponding distance on Earth were you to measure from the bottom of the Challenger Deep to the top of Mount Everest but I don't have much interest in simulating deep sea environments at the moment so I'm focussing on the more humanly explorable (and visible) portions of our planet for now.<br />
<br />
The table below shows the relative size of terrain patches and the renderable triangles they contain at each level of subdivision. As the level increases you can see the number of patches and triangles needed to represent the entire planet grows exponentially, terminating at patch level 18 which would require 90 quadrillion triangles to be rendered!<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFXkkAFdfx2gl72Dzhg60xeYGdO1L8wBEJnC6d4k_6ecxLhDtW3E_12W5dr0JpTu4XDiWop7qOFgqID1JeHqte9mhz01RPX5DTiiLE1lgbJ2weEC0aMmYKXPPjcPb432DZ_D3OmEyhI4s/s1600/PatchMetrics.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="160" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFXkkAFdfx2gl72Dzhg60xeYGdO1L8wBEJnC6d4k_6ecxLhDtW3E_12W5dr0JpTu4XDiWop7qOFgqID1JeHqte9mhz01RPX5DTiiLE1lgbJ2weEC0aMmYKXPPjcPb432DZ_D3OmEyhI4s/s400/PatchMetrics.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The patch and triangle sizes and counts for each level of icosahedron subdivision</i></td></tr>
</tbody></table>
This Icosahedron topology provides a useful basis for physics origin placement as it naturally divides the planet's surface into discrete units, the decision of which subdivision level to use is all that is necessary. The trade-off here is that using deeper subdivision levels produces more accuracy as it restricts the distance any physics object can be from the origin but necessitates moving the origin more frequently because of this limited range. It also has to be borne in mind that generating heightfield data for the physics system is not instant so the maximum travel speed of any vehicle required to drive upon the ground needs to be restricted such that nearby data can be generated fast enough to be ready by the time the vehicle gets there. In practice I don't think this should be a problem for any real world style vehicle, and fast moving vehicles such as planes can use a different less detailed system.<br />
<br />
For now I've chosen to use patch subdivision level 13 as the basis for my physics origin, the reason being that I wanted to obtain a physics heightfield resolution of approximately 1 metre per sample which a 1024x1024 heightfield shape provides for that level (the table shows the patch length of a level 13 patch (L13) is approximately 1 Km). When the physics system is initialised the origin is set to the centroid of the L13 patch under the viewpoint then as the viewpoint moves about and leaves that patch the origin is teleported to the centroid of the new L13 patch above which it now resides. Any active physics objects need to have the opposite translation applied to keep them constant in planet space despite the relocated origin.<br />
<div>
<br />
To avoid there being a delay each time the viewpoint moves from one L13 patch to another while the physics heightfield for the new patch is generated as mentioned above the heightfield data for the adjacent patches is pre-generated. Rather than there being eight adjacent patches as you would have for a square grid, the underlying icosahedral topology results in no less than twelve adjacent patches to each central one as shown below:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEfG5BXj49RubhyphenhyphenDMbRmS69jHpGdJOZz_SfJz4W1qRgxF27nIoOxHSiAUbgFCtJLrUojVt8q2DdWqLp0B4BHeFVtpjhQhTE0uh93Db9pLdqN6Xsn5X8NzBWB2L8ENnzd2TaZQVjKJIIP8/s1600/PatchAdjacency.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="173" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEfG5BXj49RubhyphenhyphenDMbRmS69jHpGdJOZz_SfJz4W1qRgxF27nIoOxHSiAUbgFCtJLrUojVt8q2DdWqLp0B4BHeFVtpjhQhTE0uh93Db9pLdqN6Xsn5X8NzBWB2L8ENnzd2TaZQVjKJIIP8/s200/PatchAdjacency.png" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Colours indicating the 12 patches adjacent to a central one</i></td></tr>
</tbody></table>
This means that in addition to producing a heightfield for the patch under the viewpoint twelve more need to be generated ready for when the viewpoint moves. Once this move to an adjacent patch does occur then any new patches not already generated from the twelve adjacent to the new central patch are generated and so on. As all this data generation takes place on background threads the main rendering frame rate is unaffected.<br />
<br />
Having a 1024x1024 PhysX heightfield shape for each L13 patch is one thing, but the square essentially 2D heightfield has to be orientated properly to fit onto the spherical planet. This is done by establishing a local co-ordinate system for each L13 patch, the unit vector from the planet centre to the centroid of the patch is defined as the Y axis while the X and Z are produced by using the shortest 3D rotation that maps the planet's own Y axis to the local patch one. Although this does produce a varying local co-ordinate space across the surface of the planet it doesn't actually matter as long as the inverse transformation is used when populating the heightfield - this ensures the heightfields always match the global planet geometry. One final adjustment to make sure the entire planet can be traversed is to modify the direction of gravity with each origin relocation so it always points towards the centre of the planet.<br />
<br />
So now there is rolling generation of heightfields as the viewpoint moves around, but even though they are roughly the same size, the 1 Km square heightfields don't really fit onto the triangular patches very well purely because of their shape:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDQ-JbNC1ogwD6h-obW9JqDrQLGjkywFt2O0R539OP4Io4246YXiIaqW8xjuVwjkev-ohFVi-k5rddKfLlhh4aXon2d7U5I4P3x2i19wKUwgShuzzpsOyX9to0ImfGq2MATJHs8N8vnDc/s1600/HeightfieldWithoutHolesFar.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDQ-JbNC1ogwD6h-obW9JqDrQLGjkywFt2O0R539OP4Io4246YXiIaqW8xjuVwjkev-ohFVi-k5rddKfLlhh4aXon2d7U5I4P3x2i19wKUwgShuzzpsOyX9to0ImfGq2MATJHs8N8vnDc/s400/HeightfieldWithoutHolesFar.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Distant view of the physics heightfields for the centre patch and it's 12 neighbours. The radical difference between the triangular mesh patches and the square physics heightfield shapes causes a huge amount of overlap</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAZt66Oz_KfK8jO7ojr6FUZMdkqrZfoTwMoWa4m29R1lBVi_4HFmwfDk-mxhBTJ2vpQ4MfeH1xZti_rxCEZiKN-NRkFK6sBXwh5QLeHMyDnzyuiousRxTwIS_sq4MLdHbs9JJbZ9HuHm0/s1600/HeightfieldWithoutHolesNear.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAZt66Oz_KfK8jO7ojr6FUZMdkqrZfoTwMoWa4m29R1lBVi_4HFmwfDk-mxhBTJ2vpQ4MfeH1xZti_rxCEZiKN-NRkFK6sBXwh5QLeHMyDnzyuiousRxTwIS_sq4MLdHbs9JJbZ9HuHm0/s400/HeightfieldWithoutHolesNear.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>A close up of this debugging view highlights how much of the geometry is covered by two or more physics heightfields - computationally inefficient and problematic for stable collisions</i></td></tr>
</tbody></table>
This overlap of physics geometry is both inefficient and prone to producing collision artifacts due to the inexact intersections of nearly coplanar geometry. Fortunately PhysX has the ability to mark heightfield triangles as "holes" making them play no part in collisions. Marking any heightfield triangles that fall entirely outside of the owning L13 patch as holes produces a much better fit with minimal overlap between adjacent heightfields.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBY9jM9GldFMRgWdF1YRv86neBMgkDKecGxBjhT7MDmMnEgO85tpKkDIvSsFlYbn-sjiHDhrI5SRLUH2iMkNvzPyIgF5FeIF8HXgsgAKG1jrz2JQ_-S35hlAvc-ZMT0NqobBUuCnE8PD0/s1600/HeightfieldWithHolesFar.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBY9jM9GldFMRgWdF1YRv86neBMgkDKecGxBjhT7MDmMnEgO85tpKkDIvSsFlYbn-sjiHDhrI5SRLUH2iMkNvzPyIgF5FeIF8HXgsgAKG1jrz2JQ_-S35hlAvc-ZMT0NqobBUuCnE8PD0/s400/HeightfieldWithHolesFar.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Distant view of the physics heightfields again but this time quads falling outside of the geometry patch are marked as holes and not rendered showing a much better fit to the underlying mesh topography.</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKFl4Sreb4GDIHZZ9pplDV09e4ovdGaIbhQfjPW4Vm3GMYJpHL0rikAcgY1BVntQXsxwGxbXoGffc-zIGyzPYKsbVGq8v0-vljSCpVHcBqGnAok5Qmgmzqwz0_0phyG2djN11fB5g3ngE/s1600/HeightfieldWithHolesNear.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKFl4Sreb4GDIHZZ9pplDV09e4ovdGaIbhQfjPW4Vm3GMYJpHL0rikAcgY1BVntQXsxwGxbXoGffc-zIGyzPYKsbVGq8v0-vljSCpVHcBqGnAok5Qmgmzqwz0_0phyG2djN11fB5g3ngE/s400/HeightfieldWithHolesNear.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>A close up of this second version shows minimal overlay between adjacent physics heightfields</i></td></tr>
</tbody></table>
The final step to achieving my goal is the vehicle itself. I'm no artist and don't have a procedural system to create vehicles at the moment so I turned to the internet and found a free 3D model for a suitable vehicle that was licensed for personal use - a <a href="http://en.wikipedia.org/wiki/Humvee" target="_blank">Humvee</a> seemed appropriate considering that all driving is off-road at the moment in the absence of roads.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQZCbzKVxXLzqePhHthwSoRtuhuiylxTDI_YwAlRFfvX8-vkx07XK4nWgDEuAhVYHGHCyvsXaFdwKJrseuCsKALASTc75HXN5yxwapyhZlL7VOI1KvDToWUxuFticmE-l6fyEA5Z5ttwU/s1600/Humvee.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="291" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQZCbzKVxXLzqePhHthwSoRtuhuiylxTDI_YwAlRFfvX8-vkx07XK4nWgDEuAhVYHGHCyvsXaFdwKJrseuCsKALASTc75HXN5yxwapyhZlL7VOI1KvDToWUxuFticmE-l6fyEA5Z5ttwU/s400/Humvee.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Downloaded Humvee model in Maya. At around 26K triangles it's about right for the balance of detail and real-time performance I want.</i></td></tr>
</tbody></table>
This gave me something to render visually but of course the physics model has to be set up appropriately to match with the wheels the right size and in the right place, the suspension travel and resistance set to reasonable values along with the mass of all components and the various drive-train parameters set up to give a suitable driving experience for example.<br />
<br />
In total there are quite a few major parameters controlling the physics vehicle model with many more more advanced controls lurking in the background. The all important feel of driving the vehicle relies on the interplay of these many variables so it is important that they can be experimented with as easily as possible to allow tweaking. To this end I put the parameters into a JSON file which can be reloaded with the press of a key while driving around allowing engine power, chassis mass, suspension travel and the like to be altered and the change evaluated immediately for fine tuning.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBOIXDbZ9CwLiVhvOH3BPxWUMuc5vPgsDjxvFYmv0iv5RvgBsWwwFfcbyFkgbMJ7EpT0DOMiVhm7I778VPspe4od_ug1zsGmQxoaLkHIcYcjwZaZTsNX2m-Uy4QPEkBJcJ_-QWHkduCr4/s1600/VehicleJson.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBOIXDbZ9CwLiVhvOH3BPxWUMuc5vPgsDjxvFYmv0iv5RvgBsWwwFfcbyFkgbMJ7EpT0DOMiVhm7I778VPspe4od_ug1zsGmQxoaLkHIcYcjwZaZTsNX2m-Uy4QPEkBJcJ_-QWHkduCr4/s320/VehicleJson.png" width="289" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Example of the JSON for the Humvee simulation model. There are plenty of more advanced parameters the PhysX vehicle model exposes but for my purposes these are sufficient</i></td></tr>
</tbody></table>
By making it data driven it also allows me to easily set up different vehicle models simulating everything from fast bouncy beach buggies to lumbering military vehicles or even civilian buses or lorries which sounds like a lot of fun.<br />
<br />
Fortunately when playing with all this PhysX includes a really handy visual debugger tool that lets you see exactly what the physics simulation thinks is going on - which can often be quite different to what is being rendered if you have bugs in the mix.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM7joAhn62iAjDJSlmnwSRPxE0zlbSnTIltte_dJvHiCCa0L24dPf06yTn5fMag8U6VmW5T2MyqfL0guXuiB6QOBoWXbwr1PGlWT04YgQAPzSbhwnUhDXB2jMAC6144ptOyw8aCx8cUAQ/s1600/PhysXDebugger.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="223" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjM7joAhn62iAjDJSlmnwSRPxE0zlbSnTIltte_dJvHiCCa0L24dPf06yTn5fMag8U6VmW5T2MyqfL0guXuiB6QOBoWXbwr1PGlWT04YgQAPzSbhwnUhDXB2jMAC6144ptOyw8aCx8cUAQ/s400/PhysXDebugger.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The PhysX Visual Debugger Tool showing the simplified representation of the Humvee represented in the simulation</i></td></tr>
</tbody></table>
With PhysX in and working, heightfield shapes set up to collide against and a vehicle to drive hooked up to basic keyboard inputs for steering, acceleration, brake and handbrake I had everything I needed but while fun to drive around I felt it looked somehow wrong...which after a little thought I realised was because the vehicle didn't have a shadow on the terrain. Without this it didn't really feel that it was sitting on the ground more floating some distance above it, and it certainly wasn't clear when it caught air leaping over bumps on the ground.<br />
<br />
To fix this I added some fairly rudimentary shadow mapping allowing the vehicle mesh to cast a shadow. There are many techniques to be found for shadow mapping each producing better or worse results in different situations in return for different degrees of complexity. To get something up and running as quickly as possible I implemented plain old PCF shadows using a six-tap Poisson disc based sampling pattern. Even though one of the simplest techniques out there, I was happy with the improvement it provided in making the vehicle feel far more 'grounded':<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGmu8d2NMOhpkrExgzNr7gjsmq7qfjcOKo5lWtayny1HjZ3he3Noa1ZI4KCls3ivtd4coORF2-8SmE6zPZLOfD_W162ork0V5Gs0Abm9a35-suNhpSMzs8cm2uqAe_3ta6Skxw9VCDt9w/s1600/HumveeNoShadows.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhGmu8d2NMOhpkrExgzNr7gjsmq7qfjcOKo5lWtayny1HjZ3he3Noa1ZI4KCls3ivtd4coORF2-8SmE6zPZLOfD_W162ork0V5Gs0Abm9a35-suNhpSMzs8cm2uqAe_3ta6Skxw9VCDt9w/s400/HumveeNoShadows.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Without shadows the Humvee looks like it's floating above the terrain somewhere even though it's actually sitting upon it - an effect known as Peter-Panning in shadow mapping parlance</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmP0xKYi6nV0gF6oLLKlpeXM8vuRr_f7FfYUjPsKpbBEB3RS1jEU5M3foPg4kWb-VJwI5qQmQakAnb5-453QitTAWR8Awqyf_EUgrMcG2_fSpveP6jWK3qJ3UXBoYuiry8ClStm-1Ohbw/s1600/HumveeWithShadows.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhmP0xKYi6nV0gF6oLLKlpeXM8vuRr_f7FfYUjPsKpbBEB3RS1jEU5M3foPg4kWb-VJwI5qQmQakAnb5-453QitTAWR8Awqyf_EUgrMcG2_fSpveP6jWK3qJ3UXBoYuiry8ClStm-1Ohbw/s400/HumveeWithShadows.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Casting a shadow onto the terrain makes it obvious the vehicle is sat upon the ground while self-shadowing goes a long way to help make it look more part of the scene in general</i></td></tr>
</tbody></table>
So that's it, I achieved what I wanted in making a drivable vehicle to tour around my burgeoning procedural planet, as a next step I think some roads might be nice to drive along...</div>
John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0tag:blogger.com,1999:blog-3862349614860735414.post-7311054258750432432015-03-11T19:46:00.001+00:002015-03-11T19:46:28.507+00:00Population Explosion<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">I described in an earlier post my algorithm for placing the
capital city in each of my countries, expanding on that I wanted to gather some
more information about the make up of each country so I can make more informed
descisions about further feature creation and placement.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">One key metric is the population for the country as knowing
how many people live in each country is key in deciding not just how many
settlements to place but also how big they should be. Until this is known much of the rest of the
planetary infrastructure cannot be generated as so much of it is dependent upon
the needs of these conurbations and their residents.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://upload.wikimedia.org/wikipedia/commons/4/4d/World_population_density_map.PNG" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://upload.wikimedia.org/wikipedia/commons/4/4d/World_population_density_map.PNG" height="181" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>World population density by country in 2012.<br />(Image from<a href="http://en.wikipedia.org/wiki/Population_density" target="_blank"> Wikipedia</a>)</i></td></tr>
</tbody></table>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">A completely naive solution would be to simply choose a
random number for each country but this would lead to future decisions that are
likely to be completely inappropriate for both the constitution of the
country's terrain and it's geographical location. A step up from completely random would be to
weight the random number by the country's area so the population was at least
proportional to the size of the country but again this ignores key factors such
as the terrain within the country - a mountainous or desert country for example
is likely to have a lower population density than lush lowlands.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">To try to account for the constitution of the country's
physical terrain rather than just use the area I instead create a number of
sample points within the country's border and test each of these against the
terrain net. As mentioned in a previous
post, intersecting against the triangulated terrain net produces points with
weights for up to three types of terrain depending on the terrains present at
each corner of the triangle hit. By
summing the weights for each terrain type found on each sample point's
intersection I end up with a total weight for each type of terrain giving me a
picture of the terrain make across the entire country. I can tell for example that a country is 35%
desert, 45% mountains and 20% ocean.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">This is of course just an estimate due to the nature of
sampling but the quality of the estimate can easily be controlled by varying
the distance between sample points at the expense of increased processing
time. Given a set number of samples
however the quality of the estimate can be maximised by ensuring the points
chosen are as evenly distributed across the country as possible.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">The number of samples chosen for the country is calculated
by dividing it's area by a fixed global area-per-sample value to ensure the
sampling is as even across the planet's surface as possible - currently I'm using one sample per 2000 square kilometres</span><span style="font-family: Verdana, sans-serif;">. Once the number is known the area of each
triangle in the country's triangulation is used to see how many sample points
should be placed within that triangle.
Any remainder area left over after that many samples worth of area has
been subtracted is propagated to the area of the next triangle - this also
applies if the triangle is too small to have even a single sample point
allocated to it to make sure it's area is still accounted for.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">If a triangle is big enough to have sample points allocated
within it those points are randomly positioned using barycentric co-ordinates
in a similar manner to how the capital cities were placed. There is nothing in this strategy to prevent
samples from adjacent triangles from falling close to each other but in general
I am happy that it produces an acceptably even distribution with a quality/performance
trade-off that can easily be controlled
by varying the area-per-sample value.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">So given the proportion of each type of terrain making up a
country how do I turn that into a population value? I assign a number of people per square
kilometre to each type of global terrain, multiply that by the area of the
country then by the weight for that type of terrain to get the number of people
living in that type of terrain in the country then finally sum these values for
each terrain type to get the total number of people in the entire country.</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">I've produced these people-per-Km<sup>2</sup> values largely
by using the real world figures
for population density <a href="http://en.wikipedia.org/wiki/List_of_sovereign_states_and_dependent_territories_by_population_density" target="_blank">found on Wikipedia</a> as a basis. Using figures a year or two old, the Earth has an approximate average population density of just 14 people per square kilometre when you count the total surface area of the planet but this rises to around 50 people per square kilometre when you count just the land masses. </span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">As shown on the infographic above however the huge variety factors influencing real world population density lead to some areas of the planet being very sparsely populated with only a couple of people per square kilometre while other areas have 1000+. There isn't enough information present in my little simulation to reflect this diversity yet however so I'm currently working with a far more restricted diversity based around the real world mean. The values I am currently using are:</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0eKWIS1HqA7zSnzGLjsyZyWzSn2hWADCdUeQyUHDEVI28OBaL4wzBxRIaSN0O5QOpPjErgNpmjKYSW75V2AR-t4o9NIJXQbKlVUt6rDUdalYUhC3eu5WSALn7JEhzqZWb_Ic-cu9xwcc/s1600/TerrainPopulationDensities.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh0eKWIS1HqA7zSnzGLjsyZyWzSn2hWADCdUeQyUHDEVI28OBaL4wzBxRIaSN0O5QOpPjErgNpmjKYSW75V2AR-t4o9NIJXQbKlVUt6rDUdalYUhC3eu5WSALn7JEhzqZWb_Ic-cu9xwcc/s1600/TerrainPopulationDensities.png" height="174" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Figures for how many "people" to create per square kilometre for each terrain type</i> </td></tr>
</tbody></table>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">So given the fairly random distribution of terrain on my
planet as it now stands what sort of results drop out of this?</span><span style="font-family: Verdana, sans-serif;"> Currently </span><span style="font-family: Verdana, sans-serif;">the terrain looks like this:</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEQUqCcVw5LRkkPxE1sExZe5Dq0r5bgs9ivYCA7eThhQTEi7rE_zA9wJzpHKppQK6dFxQcHpQR6bOWygXLiaT6WrlV41KCgI6x_WWEZqx5q0Ck3RfnE3pwsc662ufi-JHM3HUpMllBl2Y/s1600/WholePlanet.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEQUqCcVw5LRkkPxE1sExZe5Dq0r5bgs9ivYCA7eThhQTEi7rE_zA9wJzpHKppQK6dFxQcHpQR6bOWygXLiaT6WrlV41KCgI6x_WWEZqx5q0Ck3RfnE3pwsc662ufi-JHM3HUpMllBl2Y/s1600/WholePlanet.jpg" height="223" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The visual planetary terrain composition, currently the ocean terrain type has a slightly higher weighting so there is more water surface area than any other single terrain type</i> </td></tr>
</tbody></table>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">while making 100 countries produces the figures shown here</span></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEiEjICL_n-cJC3T52no38C0sUW9fN6dagoNogJiPPC9J2QGi01o00c_sB33PBNaVFSCnw_AC2tKiWZ6gRYoaPh64wMfIjMHwecCViDFXMSFoiIS9CMn5ogCp-K_1PvWf0t75VY4iVWik/s1600/CountryList.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhEiEjICL_n-cJC3T52no38C0sUW9fN6dagoNogJiPPC9J2QGi01o00c_sB33PBNaVFSCnw_AC2tKiWZ6gRYoaPh64wMfIjMHwecCViDFXMSFoiIS9CMn5ogCp-K_1PvWf0t75VY4iVWik/s1600/CountryList.png" height="347" width="640" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Terrain composition for a number of the countries along with their resultant population densities by total area and by landmass area</i></td></tr>
</tbody></table>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">As can be seen basing the terrain population densities on
real world values has generated a planetary population not too different to our own but as my planet is currently 24% water rather than the 70% or so found on
Earth the actual densities are probably on the low side. The global terrain composition is currently
made up like this:</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span></div>
<div class="MsoNormal">
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGFxJZjXnrLG3gvgAvSoeRqKh0fBD0iNQlRFXx7lfcyaHvIOJ3SjqZ28tFMpw1GAiZjGh3HXHf3Y_REsnPaM7lZiQFfHvoJjqIMlKihKPJDX1UyTllw5l0DZTpLoOIiSxLPQDMzVdaz9s/s1600/TerrainPopulations.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGFxJZjXnrLG3gvgAvSoeRqKh0fBD0iNQlRFXx7lfcyaHvIOJ3SjqZ28tFMpw1GAiZjGh3HXHf3Y_REsnPaM7lZiQFfHvoJjqIMlKihKPJDX1UyTllw5l0DZTpLoOIiSxLPQDMzVdaz9s/s1600/TerrainPopulations.png" height="115" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i style="font-size: 12.8000001907349px;">Proportion of the planet covered by each terrain type and resultant contribution to the planet's population</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">What is interesting is that the sampling strategy ensures
that the population count properly reflects the proportion of the planet that
is land mass - you can see countries such as Sralmyras which are 32% water have
a lower population density by area of just 13 while Kazilia which is 7% water
has a density of 17 people per Km</span><sup style="font-family: Verdana, sans-serif;">2</sup><span style="font-family: Verdana, sans-serif;">.</span></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
<span style="font-family: Verdana, sans-serif;">With a population count my plan is to now use that in
conjunction with the terrain composition profile to derive a settlement
structure for each country so I know how many villages, towns and cities to
create. Watch this space.</span></div>
John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0tag:blogger.com,1999:blog-3862349614860735414.post-61598039305687265142015-03-07T23:03:00.000+00:002015-03-07T23:08:24.967+00:00Derivative Mapping<span style="font-family: Verdana, sans-serif;">I've been somewhat unhappy with the lighting on my terrain for a while now especially the way much of the time it looks pretty flat and uninteresting. It's using a simple directional light for the sun at the moment along with a secondary perpendicular fill light as a gross simulation of sky illumination plus a little bit of ambient to eliminate the pure blacks. Normals are generated and stored on the terrain vertices but even where the landscape is pretty lumpy they vary too slowly to provide high frequency light and shade and with no shadowing solution currently implemented there's not much visual relief evident leading to the unsatisfying flatness of the shading.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">While I plan to add shadowing eventually I don't want to open that can of worms quite yet so I had a look around for a simpler technique that would add visual interest. I thought about adding normal mapping but the extra storage required for storing tangent and binormal information on the vertices put me off as my vertices are already fatter than I would like. While looking in to this however I came across a <a href="http://www.rorydriscoll.com/2012/01/11/derivative-maps" target="_blank">blog post by Rory Driscoll from 2012 discussing Derivative Mapping</a>, itself a follow up to <a href="http://mmikkelsen3d.blogspot.co.uk/2011/07/derivative-maps.html" target="_blank">Morten Mikkelsen's original work on GPU bump mappng</a>. This technique offers a similar effect to normal mapping but uses screen space derivatives as a basis for perturbing the surface normal using either the screen space derivatives calculated from a single channel height map texture or the pre-computed texture space derivatives of said height map stored as a two channel texture.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">This was appealing to me not just because I hadn't used the technique before and was therefore interested just to try it out, but also from an implementation point of view I would not have to pre-compute or store anything on the vertices to define the tangent space required for normal mapping saving a considerable amount of memory given the density of my geometry. It also solves the problem of defining said tangent space consistently across the surface of a sphere, a non-trivial task in itself.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Thanks to the quality of the posts mentioned above it was a relatively quick and easy task to drop this in to my terrain shader. I started with the heightfield based version as with the tools I had available creating a heightfield was easier than a derivative map but while it worked the blocky artifacts caused by the constant height derivatives where the heightmap texels were oversampled were very visible especially as the viewpoint moved closer to the ground. I could have increased the frequency of mapping to reduce this but when working at planetary scales at some point they are always going to reappear. To get round this I moved on to the second technique described where the height map derivatives are pre-calculated in texture space and given to the pixel shader as a two channel texture rather than a single channel heightmap. The principle here is that interpolating the derivatives directly produces a higher quality result than interpolating heights then computing the derivative afterwards. I had to write a little code to generate this derivative map as I didn't have a tool to hand that could do it but it's pretty straightforward.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Although this takes twice the storage and a little more work in the shader the results were far superior in my context with the blocky artifacts effectively removed and the effect under magnification far better.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEk6QmojCYcLFo7cv26xbSozcHspNHPz7NvnfAyFIcAikItRLUOi8heLnvF2b8fSMdgP7eHl4Dwh5YjP8gW8kebsdBBKLc9PMJNoRM31owpzBA77bMJCp5uCgmxLiVOcqpi1Qesl1KtIo/s1600/DesertWithoutMapping.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgEk6QmojCYcLFo7cv26xbSozcHspNHPz7NvnfAyFIcAikItRLUOi8heLnvF2b8fSMdgP7eHl4Dwh5YjP8gW8kebsdBBKLc9PMJNoRM31owpzBA77bMJCp5uCgmxLiVOcqpi1Qesl1KtIo/s1600/DesertWithoutMapping.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>A desert scene as it was before I started. With the sun overhead there is very little relief visible on the surface</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW3PNK-bE10PuoxrAUg4ptj4fAvfGFkcw2pSD2pTATWrK5DXHi7_Tg8xiQhI0I4UbYmBmjZkbFKKj3aiZNYjK34sLdbVXlybs6sknHjCAOOlkJND8JYAEstMCR0LD_cVKmKZ7Yf17I5wo/s1600/DesertWithMapping.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiW3PNK-bE10PuoxrAUg4ptj4fAvfGFkcw2pSD2pTATWrK5DXHi7_Tg8xiQhI0I4UbYmBmjZkbFKKj3aiZNYjK34sLdbVXlybs6sknHjCAOOlkJND8JYAEstMCR0LD_cVKmKZ7Yf17I5wo/s1600/DesertWithMapping.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The same view with derivative mapping added. The surface close to the viewpoint looks considerably more interesting and the higher frequency surface normal allows steeper slope textures to blend in</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">As you can see here the </span><span style="font-family: Verdana, sans-serif;">difference between the two versions is marked with the derivative mapped normals showing far more variation as you would expect. The maps I am using look like this:</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN6OpKFb8277jGhz446B7ScQHitWWcYEv5FZFlvuf4RarURWFZEoRAiQwGbwsPU1ag4V5baQpUVuGtVjweHJsg2QiVZ6SALfyqK7IJF2kBRxMalPwFtqsGfQUn36slU9M3TEAOiBl0UyU/s1600/Bump.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiN6OpKFb8277jGhz446B7ScQHitWWcYEv5FZFlvuf4RarURWFZEoRAiQwGbwsPU1ag4V5baQpUVuGtVjweHJsg2QiVZ6SALfyqK7IJF2kBRxMalPwFtqsGfQUn36slU9M3TEAOiBl0UyU/s1600/Bump.jpg" height="200" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The tiling bump map I am using as my source heightfield</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUXlyKDN89QAjD1nolTjqF_AAWxH34QVC8RY9gUaYEz4iKL8GHpdG-81pd1BtVNW3tgKwbwhyphenhyphenXjDkgG0cAehim3z31PfW6SLpcxcX6akU5asAim5gR_l6VA4NvDN1nc45iNFmxRX2ilsk/s1600/DerivativeMap.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUXlyKDN89QAjD1nolTjqF_AAWxH34QVC8RY9gUaYEz4iKL8GHpdG-81pd1BtVNW3tgKwbwhyphenhyphenXjDkgG0cAehim3z31PfW6SLpcxcX6akU5asAim5gR_l6VA4NvDN1nc45iNFmxRX2ilsk/s1600/DerivativeMap.jpg" height="200" width="200" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The derivative map produced from the heightfield. The X derivatives are stored in the red channel and the Y in the green.</i></td></tr>
</tbody></table>
<br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Here is another example this time in a lowlands region:</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHOdJvtTVDWtMmyEFLKGNigCUBndXkgfw07BXe9T_oC-i4xFBFjIjQpK5pVxomVJvnZi8gLd_KsS6ZzAbcGGz9CLUKHm6fK1Mwu7nolpLytjIHnGrDYXNM4eqgp86E5LIaZ8DFMdv6JFo/s1600/LowlandsWithoutMapping.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhHOdJvtTVDWtMmyEFLKGNigCUBndXkgfw07BXe9T_oC-i4xFBFjIjQpK5pVxomVJvnZi8gLd_KsS6ZzAbcGGz9CLUKHm6fK1Mwu7nolpLytjIHnGrDYXNM4eqgp86E5LIaZ8DFMdv6JFo/s1600/LowlandsWithoutMapping.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>A lowlands scene before derivative mapping is applied</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjclwUNO9WHl0EhwNnP6RVQD6IM-Gz7JAjAJ_Wnl5e7Mm20uiSOPr5JFYGwcjrtbOrUhV0eH71ijGQUOi1aZGBKbscr2kmJPlguTgspTvv9OnOAmhpsEBjMDY7WDq2CHjV2qRUbhEKq5h0/s1600/LowlandsWithMapping.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjclwUNO9WHl0EhwNnP6RVQD6IM-Gz7JAjAJ_Wnl5e7Mm20uiSOPr5JFYGwcjrtbOrUhV0eH71ijGQUOi1aZGBKbscr2kmJPlguTgspTvv9OnOAmhpsEBjMDY7WDq2CHjV2qRUbhEKq5h0/s1600/LowlandsWithMapping.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">The same scene with derivative mapping.</td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">Particularly evident here is the additional benefit of higher frequency normals where it is the derivative-mapped normal not the geometric one that is being used to decide between the level ground texture (grass, sand, snow etc.) and the steep slope texture (grey/brown rock) on a per-pixel basis. This produces the more varied surface texturing visible in the derivative mapped version of the scene above.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Finally, here are a couple more examples, one a mountainous region the other a slightly more elevated perspective on a mixed terrain:</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjl7IxDi53SRCUX6LVxUR7eQ1URlB64Yd88aCgKlUd4XT5RrPiLE59TbFooCon6h6k95xCA5qIbdaOYo2gxbHhjZw-z6Nn5tDc6aCjvhGwvT7YPJlXVINQrXJkY2YohahrDpjlDT33RHaE/s1600/MountainsWithoutMapping.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjl7IxDi53SRCUX6LVxUR7eQ1URlB64Yd88aCgKlUd4XT5RrPiLE59TbFooCon6h6k95xCA5qIbdaOYo2gxbHhjZw-z6Nn5tDc6aCjvhGwvT7YPJlXVINQrXJkY2YohahrDpjlDT33RHaE/s1600/MountainsWithoutMapping.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Mountain scene before derivative mapping</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6FfGIgcTTSIT5RBC2YAL-UfYJxL53wktENTK_heqreczw7oVPguJsfC1YNZAfkkh7Gbvj3i2QqurQ6EqVgUCWky9cqGN0GGELV4riS6J2aHIEVKNpS7OCRiVc_evYACQn6GxcRXqYyuU/s1600/MountainsWithMapping.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6FfGIgcTTSIT5RBC2YAL-UfYJxL53wktENTK_heqreczw7oVPguJsfC1YNZAfkkh7Gbvj3i2QqurQ6EqVgUCWky9cqGN0GGELV4riS6J2aHIEVKNpS7OCRiVc_evYACQn6GxcRXqYyuU/s1600/MountainsWithMapping.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The same scene with the mapping applied, the mountain in the foreground shows a particularly visible effect </i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3TNydPgkbkJbZZEQK394JaR-p8pYMmmVSk_mJ-zmbtk0eeCc2VULrD0CooLLVKRmxo0VDa59N6jTBC_bGKKcz3qtN8py0vt2v5eZdYdwxsS2mQXBA98PxRnZjbg4MJ_wRsdDziEyKEuU/s1600/MixedWithoutMapping.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3TNydPgkbkJbZZEQK394JaR-p8pYMmmVSk_mJ-zmbtk0eeCc2VULrD0CooLLVKRmxo0VDa59N6jTBC_bGKKcz3qtN8py0vt2v5eZdYdwxsS2mQXBA98PxRnZjbg4MJ_wRsdDziEyKEuU/s1600/MixedWithoutMapping.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>A mixed terrain scene without mapping</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnrZ0qt3S5NQvonfn2JFd_Pw9Rsh4PvwQ83erDHs3PPzdjQ5nvt192R8-mpxvvHI9UMMV9DvpAOvQYwXQZ45ArWBVB63C5fPflJrNJhZ0CCCAdklfEIiZJC5-_dxYJC8jeHQ4c4LVBQO4/s1600/MixedWithMapping.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnrZ0qt3S5NQvonfn2JFd_Pw9Rsh4PvwQ83erDHs3PPzdjQ5nvt192R8-mpxvvHI9UMMV9DvpAOvQYwXQZ45ArWBVB63C5fPflJrNJhZ0CCCAdklfEIiZJC5-_dxYJC8jeHQ4c4LVBQO4/s1600/MixedWithMapping.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The same scene with the derivative mapping applied. The boundaries between the flat and steeply sloped surface textures in particular benefit here with the transitions being far smoother</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">To make it work around the planet a tri-planar mapping is used with the normal for each plane being computed then blended together exactly as for the diffuse texture. For a relatively painless effect to implement I am very pleased with the result, the ease of use however is entirely down to the quality of the blog posts I referenced so definitely thanks go to Rory and Morten for that.</span><br />
<div>
<br /></div>
John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0tag:blogger.com,1999:blog-3862349614860735414.post-34429820076279250552015-03-02T10:22:00.002+00:002015-03-02T10:22:43.672+00:00In Deep Water<span style="font-family: Verdana, sans-serif;">I've been thinking about water, particularly how the oceans of a planet affect the evolution of the people living on it, their choices for habitation, agriculture and infrastructure. Of course there are many other factors that influence these things but oceans seem like a good place to start, and with 71% of the Earth covered by them there is certainly plenty of reference material about.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">I had a couple of thoughts about how to procedurally generate the water masses for my planet, the most straightforward and probably most commonly used is to simply decide upon an elevation level to define as the sea level with everything generated by the noise functions under that counting as water. You can then render the water at that elevation and the GPU's Z buffer will sort out the intersection with the land. While I still want the simplicity and benefits of an established sea level, I wanted to have a look at making water region generation more integral to the overall planet's procedural system rather than it being treated essentially as a post process. </span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Eventually it would be nice to have bodies of water at different elevations so mountain lakes, tarns and similar could be created preferably with rivers and streams connecting them to each other, waterfalls and larger bodies of water but that's all for the future.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span><span style="font-family: Verdana, sans-serif;">The TL;DR version: this video shows the effect I'm going to describe here as it now stands:</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/IxeZcHu_dJc/0.jpg" frameborder="0" height="266" src="http://www.youtube.com/embed/IxeZcHu_dJc?feature=player_embedded" width="320"></iframe></div>
<br />
<span style="font-family: Verdana, sans-serif;">To make water body creation part of the terrain system the heightfield generation itself has to be aware of the presence of water so there needs to be a way to define where the water should go. My first attempt at this was using the country control net as I thought the country shapes would make pretty decent seas and by combining a couple of adjacent ones some reasonable oceans. By creating the countries as normal then simply flagging some of them as water such regions can be established, the heightfield generator can then ray-cast the point under consideration against the country net and if it finds it's within a water "country" use a noise function appropriate for sea beds that will return low altitude points below the global sea level.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">When I tried this however a couple of points became apparent, firstly having to ray cast against the country net's triangulation slows down the heightfield generator which is significant when it has to be run so frequently to generate the terrain and while optimisation might mitigate some of this cost I felt a more significant problem was the regularity of the water zones created. With each being formed from a single country there were for example no islands generated which is quite a big drawback and for me pretty much discounted this approach.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Instead of the country net then how about using the terrain net instead? By adding a terrain type of Ocean that generates a plausible seabed heightfield that lies below sea level to the existing terrain types such as mountain, hilly and desert deep water regions could be formed using the same controllable system employed for those other type of terrain. The turbulence function described previously that perturbs the terrain type determination will also then affect the water region borders creating some interesting swirls and inlets.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">The blending of mountainous or hilly terrain into the seabed generator around the transition areas also produces plenty of islands of various sizes</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtIa5Y6US8-yw2bXqRk2GOcdPeAnw-5bhGgUSVdxmG4K_hXF5MB4td73sxs5NLv-B5MRyor4zjA8KNMdZu1Iu36le709WCJf-yWmzeZpl3dlOWBEZH8LW5Bj0wSn0srHG3lMqPsC6kbU8/s1600/Islands.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtIa5Y6US8-yw2bXqRk2GOcdPeAnw-5bhGgUSVdxmG4K_hXF5MB4td73sxs5NLv-B5MRyor4zjA8KNMdZu1Iu36le709WCJf-yWmzeZpl3dlOWBEZH8LW5Bj0wSn0srHG3lMqPsC6kbU8/s1600/Islands.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Islands formed by perturbing the ray cast against the terrain net</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">There are some drawbacks to using the terrain net instead of the country one however, there is nothing for example to prevent an entire country from ending up under water or possibly more problematically the vast majority of a country could be under water leaving an implausibly small section remaining that simply would not really exist in the real world. On balance however I thought this is the better of the two systems so am going with this for now.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<h4>
<span style="font-family: Verdana, sans-serif;">Rendering Water</span></h4>
<span style="font-family: Verdana, sans-serif;">Rendering of realistic water is a long standing challenge for real time graphics and one that I've looked at myself from time to time in my various forays into terrain generation and rendering. For this project I thought a good place to start with generating the actual geometry for the water surface would be to use essentially the same system as I already use for the terrain, namely a subdivided icosahedron.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">The existing triangular patch generator can easily be extended to determine whether any vertices in the patch are below sea level or not, and if so a second set of vertices for the ocean level surface patch is generated. These vertices represent the triangular area of the surface of a sea level radius sphere centred on the centre of the planet and encompassed by the patch in question. Although the plan is to have realistic waves on the water these will be generated by the vertex shader displacing the vertices so a smooth sphere is enough to start with.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim-QZwtN4mNv_Ul551N0bCP4gQBwRMTLp2ZK2_63NvBMegU4qlgvmUTAiJiFcI_sy_tdsG3H_cfDgPc2W3uaR9L1kfYftZgGaCHd_UeO9qqTMSBB2iuqURZUZj1sRbjb1pqRoFTD1p9tI/s1600/WireSphere.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEim-QZwtN4mNv_Ul551N0bCP4gQBwRMTLp2ZK2_63NvBMegU4qlgvmUTAiJiFcI_sy_tdsG3H_cfDgPc2W3uaR9L1kfYftZgGaCHd_UeO9qqTMSBB2iuqURZUZj1sRbjb1pqRoFTD1p9tI/s1600/WireSphere.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Wireframe of the water surface before being perturbed in the vertex shader using the displacement of the simulated water surface</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">The level of detail system described previously can also be leveraged to decide which water geometry patches to render by simply running it in parallel on both the terrain patches and the water ones - a different distance scalar can also be used for the water patches enabling lower detail geometry to be used for the water potentially improving performance as long as the visual quality doesn't suffer too much.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Even though the identical geometric topology allows the water patches to use the same index buffer as the terrain there is an additional and more significant optimisation opportunity here that would save significant memory. With each water patch representing what is essentially an identical shaped section of the sphere's surface at that level of detail they could actually all be drawn using a single vertex buffer with a suitable transformation matrix to put it at the correct position and with the correct orientation. Setting this up is a little fiddly so I'm leaving it for a future task but should memory become an issue it's likely to be one of the first things I'll revisit.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">As for the actual water visuals, the movement and appearance of water is notoriously difficult to simulate especially as the real thing behaves so radically differently in different situations - shallow water is totally different to deep for example and white water rapids completely at odds with languid meandering rivers. To make such a difficult problem manageable I chose to focus on just one aspect and when talking at planetary scales deep water seemed like the logical choice - the oceans created as described above will dominate any other water features I add in the future.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">There are quite a few references and demos for deep water rendering but the one I chose was the <a href="https://developer.nvidia.com/sites/default/files/akamai/gamedev/files/sdk/11/OceanCS_Slides.pdf" target="_blank">NVIDIA "Ocean" demo</a> from their now superceded graphics SDK</span><span style="font-family: Verdana, sans-serif;"> which is in turn based on such classic work as Jerry Tenssendorf’s paper “<a href="http://graphics.ucsd.edu/courses/rendering/2005/jdewall/tessendorf.pdf" target="_blank">Simulating Ocean Water</a>”. I liked this demo as it is well documented and being GPU based heavily targeted at real time graphics.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">This demo uses a couple of compute shaders to perform the necessary <a href="http://en.wikipedia.org/wiki/Fast_Fourier_transform" target="_blank">FFT </a>followed by a couple of pixel shaders to produce two 2D texture maps, one storing the displacement for the vertices over the square patch of water and the other storing the 2D gradients from which surface normals can be calculated and a 'folding' value useful for identifying the wave crests.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7ufdIey11H1PKi_ggH-wUFcu77IQyPG_AKr6U2NcwJRhW7aGUAuPEQhI2Vcm3JNOKq5g2AN_4tKDW9Rbs2lQ05GLcCy_9840j7kzj8fnWz4PIKCtml67XZkukOSjjp1SrLs2Bve3KcbI/s1600/WireWaves.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7ufdIey11H1PKi_ggH-wUFcu77IQyPG_AKr6U2NcwJRhW7aGUAuPEQhI2Vcm3JNOKq5g2AN_4tKDW9Rbs2lQ05GLcCy_9840j7kzj8fnWz4PIKCtml67XZkukOSjjp1SrLs2Bve3KcbI/s1600/WireWaves.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>High LOD wireframe showing how the smooth sphere vertices have been displaced to create the waves. Note that normally a lower LOD version is used as the majority of the shading effect comes from the normals computed in the pixel shader rather than the wave shapes produced in the geometry.</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">The displacement map is fed in to the water geometry's vertex shader to displace the vertices creating the waves while the gradient/folding texture is fed in to the pixel shader to allow per-pixel normals to be created for shading and wave crest effects to be added.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Using these textures as the primary inputs, there are a number of shading effects taking place in the pixel shader here to give this final ocean effect. Although the NVIDIA demo produces a nice output I decided to largely ignore the final pixel shader as it's use of a pre-generated cube map for the sky colour along with a simulated sun stripe was a bit too hard coded for my needs. Instead I fiddled around some and came up with my own shading system.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Firstly the gradient texture is used to compute a surface normal which is then used with the view vector to calculate the reflection vector (using the HLSL reflect function). I then create a ray from the surface point along that reflection vector and run it through the same atmospheric scattering code that is used for rendering the skybox, this gives me the colour of the sky reflected from that point and ensures that the prevailing sky conditions at that location at that time of day are represented. This is important to ensure effects such as sunsets for example are represented in the water but even at less dramatic times of the day gives the water somer nice variegated shades of blue.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrAVVag_BrD5zYm9pgGtb0FHB0_JI_l4Inr971xJscBG1xu5U4gKuHUtwxnMnIzx7-pcMMur4m7axtechafNwjzxqRA590XakxF51jtJ4LlwDIR8PkZbXi1_VbvKZriPu3jcx66Sej8rY/s1600/SunReflection.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgrAVVag_BrD5zYm9pgGtb0FHB0_JI_l4Inr971xJscBG1xu5U4gKuHUtwxnMnIzx7-pcMMur4m7axtechafNwjzxqRA590XakxF51jtJ4LlwDIR8PkZbXi1_VbvKZriPu3jcx66Sej8rY/s1600/SunReflection.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Bright early morning sun reflected in the water</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLaxnRm6RbnjzKMFFUleZbWUQCOAH9TRc-6tMdnIKcpfC556yIBdS3bczT3ap95axBOMW0KQnVqUNimJ6udJK9whY6u3KMYZEdj1I7V19rXOyTxcS1OkRQzVsj4OvGCnNFFNbUb1MBZLI/s1600/Sunrise.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLaxnRm6RbnjzKMFFUleZbWUQCOAH9TRc-6tMdnIKcpfC556yIBdS3bczT3ap95axBOMW0KQnVqUNimJ6udJK9whY6u3KMYZEdj1I7V19rXOyTxcS1OkRQzVsj4OvGCnNFFNbUb1MBZLI/s1600/Sunrise.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The last traces of sunset bounce off the ocean surface, an effect that would be difficult to achieve with direct illumination</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">Capturing the sun's contribution in this unified manner is especially useful as it's size and colour varies so much based upon the time of day, trying to represent that as a purely specular term on water can be a challenge often ending up with a sun "stripe" that doesn't match the rendered sun - especially near the horizon.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">The <i>folding</i> value from the gradient texture is used to add a foam effect at the top of the waves to make the water look a bit choppier. The foam texture itself is a single channel grayscale image with the degree of folding controlling which proportion of the grayscale is used. A low folding value for example would cause just the brightest shades of the foam texture to be used while a high one would cause most or all of the greyscale to be present producing a much stronger foam component to the final effect. A global "<i>choppyness"</i> value is also used to drive the water simulation which affects how perturbed the surface normals are in addition to introducing more foam - this value can be changed dynamically to vary the ocean from a millpond to a roiling foamy mass: </span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJMilnmmqjCA3Dp15b7BYfsBHMDiGKPX9DIow_7bGbMJDKRxHN0-dxzsOiz_ahr26WQuYGM__gu-0yu82hVDtF1HZmckKnRgBKqFxhBnkniuCXdlXhpyaSBwDP5tWcXvTfaseyCJga14U/s1600/Foam1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJMilnmmqjCA3Dp15b7BYfsBHMDiGKPX9DIow_7bGbMJDKRxHN0-dxzsOiz_ahr26WQuYGM__gu-0yu82hVDtF1HZmckKnRgBKqFxhBnkniuCXdlXhpyaSBwDP5tWcXvTfaseyCJga14U/s1600/Foam1.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>A fairly low "Choppyness" value produces pleasing wave crests and moderate surface peturbation</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5TV9_p_JtbXJXxK9Wa5ZAnOAaUGpi_3uhXjgL2Q4vzEhaHqBuyWDIF8tIQBXDFmHFGO9E3Eq5E22tDo3hyTYfzLoRRKBO6S2Qv_GlToug-J5xiciDJYvPZ7Gx7tKyXW2HFTqCkPPEb00/s1600/Foam2.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg5TV9_p_JtbXJXxK9Wa5ZAnOAaUGpi_3uhXjgL2Q4vzEhaHqBuyWDIF8tIQBXDFmHFGO9E3Eq5E22tDo3hyTYfzLoRRKBO6S2Qv_GlToug-J5xiciDJYvPZ7Gx7tKyXW2HFTqCkPPEb00/s1600/Foam2.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>A higher "Choppyness" produces more agitated wave movement, sharper surface relief and considerably more foam.</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">In addition to foam at the wave crests I also wanted foam in evidence where the water meets the land, to accomplish this a copy of the depth buffer is bound as a pixel shader input and the depth of the pixel being rendered compared against it. This produces a depth value representing the distance between the water surface and the terrain already rendered to that pixel. This depth delta is added not just to the foam amount to render but is also used to drive the alpha component letting the water fade out in the shallows where it meets the land. This can be seen in both the images above where the water meets the land.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">The benefit of using the screen space depth delta rather than a pre-computed depth value stored on the vertices is that it reacts dynamically to both the movement of the vertices driven by the water surface displacement map and to anything else that penetrates the water surface. The latter can't be seen just yet other than where the water geometry meets the terrain as I don't have any such features but in the future should I have ships, jetties or gas/oil rigs the alpha/foam effect will simply work around where they intersect the water helping them feel more grounded in-situ.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: Verdana, sans-serif;">Problems of scale</span></h3>
<span style="font-family: Verdana, sans-serif;">As mentioned above one of the fundamental problems with water rendering is that it behaves and appears so radically different depending on it's situation, but another problem with rendering water especially with planetary scale viewpoints is how it appears from different distances. The 512x512 surface simulation grid I'm using looks good close up but simply tiling it produces unsightly repeating patterns when viewed from larger distances.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsDxc7gglRld4kQoknLRcx5a3zeO9pco3uAAepygTALl-y7Tyg3GyFunb74b1c-pw941CEepo9xyZv9mT07dCwUHRYaKM0Q84B27jH9rC1-NysFzv5N8Ue4tZkvn9dNa7yY-AUUStoF3k/s1600/Tiling.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsDxc7gglRld4kQoknLRcx5a3zeO9pco3uAAepygTALl-y7Tyg3GyFunb74b1c-pw941CEepo9xyZv9mT07dCwUHRYaKM0Q84B27jH9rC1-NysFzv5N8Ue4tZkvn9dNa7yY-AUUStoF3k/s1600/Tiling.jpg" height="166" width="320" /></a></div>
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Not only does the limited simulation area become very apparent but the higher frequency of the surface normal variation produces very visible aliasing in both the reflection vector used to compute the water colour and the wave crest effect producing unsightly sparkling in the rendered image.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Rather than simply increase the simulation area which would produce just a limited improvement and incur increased simulation cost instead I vary the frequency at which I sample the simulated surface with distance. The pixel shader uses the HLSL partial derivative instructions to determine an approximate simulation texture to screen pixel ratio then scales the texture co-ordinates to obtain an equally approximate 1:1 mapping. This effectively causes the simulation surface to cover increasingly large areas of the globe as the viewpoint moves further away.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">This is in no way physically accurate but produces a more pleasing visual effect than the aliasing, and by blending between adjacent scales a smooth transition can be achieved to hide what would otherwise be a very visible transition line between scales - an effect very similar to a mip line when trilinear or anisotropic filtering is not being used. </span><span style="font-family: Verdana, sans-serif;">Take a look at the second half of the video above to see how this looks in practice as the viewpoint moves from near the surface all the way out into space.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span><span style="font-family: Verdana, sans-serif;">There is more to the effect than just scaling the simulated water surface to cover larger and larger areas though, as while this eliminates most of the tiling artifacts it also inevitably makes all water features larger which can look increasingly unrealistic. To alleviate this undesirable consequence certain aspects of the effect are toned down with increasing distance from the viewpoint. The first is the per-pixel normal calculated from the simulation's gradient texture which has the gradient's effect scaled down to produce less variation with distance making the resultant variations in reflected sky colour more subtle while the second is the foam wave crest effect which is also scaled down and ultimately removed with distance:</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnZGLtLIXLzxCfDs22CxKe011YA_xrXTONTGyBKcLpEYlPAZcgjWpQ5VuW2TJ2Wp96dHC366Zz3qf6BLW_Loae4KVrRMBIl9GgTwQ_LqWnLFjr2X0M2QInOgrivkvGDZp2BgS_kyNvnlQ/s1600/FoamFade.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhnZGLtLIXLzxCfDs22CxKe011YA_xrXTONTGyBKcLpEYlPAZcgjWpQ5VuW2TJ2Wp96dHC366Zz3qf6BLW_Loae4KVrRMBIl9GgTwQ_LqWnLFjr2X0M2QInOgrivkvGDZp2BgS_kyNvnlQ/s1600/FoamFade.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>More distant view showing how the wave crests peter out and the water adopts a smoother aspect with distance</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Combining these helps make distant water a bit more appropriately indistinct. </span>
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<br />
<h3>
<span style="font-family: Verdana, sans-serif;">Making it all 3D</span></h3>
<span style="font-family: Verdana, sans-serif;">The final challenge with the water was taking the two dimensional result of the simulated water surface and applying it to my three dimensional planet. There are a variety of ways to map 2D squares to the surface of a sphere but each has major trade-offs involving distortion in some way or other. I decided to keep it simple and use the same triplanar projection system used for texturing the terrain - i.e. using the 3D world space position of the point being shaded to sample the texture in the XZ, XY and ZY planes then using the surface normal to blend between them.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">The only trick here is to make sure the directions of the normals from the gradient map are consistent so they are oriented correctly for the plane they are being applied to, getting this wrong and the sun and sky will not reflect properly in the water.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: Verdana, sans-serif;">Next Steps</span></h3>
<span style="font-family: Verdana, sans-serif;">I'm pretty happy with the water effect now, it has some artifacts still but I feel I've spent enough time on it for now and the effect is good enough not to irritate me. Next I think is integrating it's placement more into the geographic terrain generator especially in the area of trying to make interesting coastlines along with processing it's impact on the countries themselves.</span>John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com3tag:blogger.com,1999:blog-3862349614860735414.post-5331795886953636132015-02-07T10:23:00.000+00:002015-02-07T10:23:02.563+00:00Capital CitiesFollowing on from Country Generation, I thought I would take a look at placing some capital cities in my newly established countries as a precursor to more general settlement placement encompassing everything from hamlets and villages up to towns and other major cities.<br />
<br />
As a quick recap, here is my planet with the country boundaries highlighted:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ1kpZ0mjDSv4ZmGCQRwIsBw9FyFG_flOrfm6xmbry3TMTRGVcIcIIK4m9nkjHPyd78WWqThIXu-tyS4ix4GrCub6zkh0OHLzV0HN38nwQd4nH629djt-HYcit2ULYejh8m6_11DvvYmE/s1600/Boundaries.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ1kpZ0mjDSv4ZmGCQRwIsBw9FyFG_flOrfm6xmbry3TMTRGVcIcIIK4m9nkjHPyd78WWqThIXu-tyS4ix4GrCub6zkh0OHLzV0HN38nwQd4nH629djt-HYcit2ULYejh8m6_11DvvYmE/s1600/Boundaries.jpg" height="207" width="400" /></a></div>
<br />
<br />
So the challenge here is to pick an essentially random location within the country boundaries that is suitable for it's capital city. As I already have a triangulation of the country to be used when deciding within which country a point on the planet's surface lies, I can use this to place the city. Rather than picking random points on the planet in the hope of eventually finding one that lies within the country of interest however it makes more sense to pick points within the triangulation to start with.<br />
<br />
The simplest way therefore would be to just pick a random triangle from the country's triangulation then pick a random point within it. The first is trivial while the second can also be easily accomplished using barycentric co-ordinates to convert three random numbers in the range [0, 1] into a point within the triangle.<br />
<br />
While this works, I didn't want capital city placement to be completely random, for my purposes I didn't want a capital city that was too close to the country boundaries but with the naive approach described above there is nothing to stop a capital being right on the border. This is purely a subjective restriction and I'm sure somewhere in the world there is a capital very close to a border but it just didn't feel right to me.<br />
<br />
Unfortunately determining the distance between a point within a country and it's border isn't a trivial task, requiring the distance to each line segment making up the border to be calculated and the minimum taken. Determining what is an acceptable distance is also not as easy as it might be due to the wildly varying shapes of the countries. An acceptable distance for a generally circular country might be impossible to satisfy for a different country that was long and thin.<br />
<br />
There isn't much that can be done to alleviate the first issue other than test as few candidate points as possible and possibly use some sort of spacial index to reduce the number of border segments being processed - while it's a bit brute force however it's not prohibitively expensive so improving it isn't top priority.<br />
<br />
Without any particular knowledge of a country's shape however determining an acceptable distance from the border to place the capital city is a bit more of a challenge. Rather than try to work out some sort of heuristic from the border vertices I chose instead to use a sampling approach. For each country a number of random points are generated using the simple algorithm above and the distance from each to the country's border calculated. The point that is furthest from the border is chosen as the position of the capital city.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdO6L4RAvO1Hzs_-H-yQBkdhYPN4EDLRiQTsCgNnep6X-RwKd5ste_15JppUtm3P9KS3BWN-IMCJj9Lswrr99e3z5QGLNmgZNc-m-8452dX669h-WZsxh6QdcWgTBxvX1lCytb73No1hk/s1600/CityLocations.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdO6L4RAvO1Hzs_-H-yQBkdhYPN4EDLRiQTsCgNnep6X-RwKd5ste_15JppUtm3P9KS3BWN-IMCJj9Lswrr99e3z5QGLNmgZNc-m-8452dX669h-WZsxh6QdcWgTBxvX1lCytb73No1hk/s1600/CityLocations.jpg" height="207" width="400" /></a></div>
<br />
<br />
The number of points to test is a little trial and error - sample too few and you might not pick a point that's far enough away from the border to be acceptable while sample too many and you will inevitably end up with a point pretty much in the middle of every country which is too uniform and also unacceptable.<br />
<br />
I ended up taking 25 sample points for each country which I think gives a decent spread of capital positions while still not generating any too close to the borders.<br />
<br />
I am hoping the system can be extended to start placing secondary and tertiary settlements by adding other factors to the heuristic such as distance from already placed settlements (scaled by the relative sizes of each settlement) along with some physical factors such as the candidate site's proximity to rivers or bodies of water to encourage settlements in coastal areas and the general flatness of the terrain to make lowlands and valley floors more appealing than steep mountain sides.<br />
<br />
For reference I thought it would be worth including an illustration of the triangulation generated from the country boundaries, as you can see the simple ear-clipping algorithm used tends to produce long thin triangles. These don't affect the functionality of the system but at some point in the future I might be adding these triangles to some sort of spacial organiser to improve query performance in which case a triangulation that was a bit more evenly distributed across the surface might be better, but it will do for now.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQizidNLUGD-1vGRo_zTVEQr1x0oqml5W9bP2Xk4nGwmilcgXrg3d_eNFAxQjueLhn_OXxLxIqqJQlEEJ61hvenKTPd1XcAwmRbUFkX154qDIEdlMirbXqdb8kYRFI38Znhk_dc7Vrg7w/s1600/Triangulation.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQizidNLUGD-1vGRo_zTVEQr1x0oqml5W9bP2Xk4nGwmilcgXrg3d_eNFAxQjueLhn_OXxLxIqqJQlEEJ61hvenKTPd1XcAwmRbUFkX154qDIEdlMirbXqdb8kYRFI38Znhk_dc7Vrg7w/s1600/Triangulation.jpg" height="207" width="400" /></a></div>
<br />
<br />
Then of course there is generation of the actual cities themselves complete with road network and buildings, so there is as ever plenty to do!John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com1tag:blogger.com,1999:blog-3862349614860735414.post-77643717036785834082015-01-17T20:48:00.001+00:002015-01-17T20:48:30.342+00:00Terrain Visuals<span style="font-family: Verdana, sans-serif;">My posts on this particular project so far have been about preparation for generating a plausible political landscape complete with infrastructure rather than the physical landscape most of my other projects have focussed on but before I go any further I think it necessary to have at least enough of a physical landscape to make placement and routing of the more man made features interesting..putting things on a flat coloured sphere is only going to hold my attention for so long I expect!</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">While I've previously worked with voxels to facilitate truly three dimensional terrain including caves and the like with the focus of this project elsewhere the simplicity of a heightfield based terrain makes sense. When doing this in the past I've used a distorted subdivided cube as the basis of the planet - taking each point and normalising it's distance from the planet centre to produce a sphere - which has some benefits for ease of navigation around each "face" and for producing a natural mapping for 2D co-ordinates but the stretching produced towards where the cube faces meet is unpleasant and besides, I've done that before.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Caveat: Before I go any further however I should point out that nothing I am doing here is rocket science - if you have ever done your own terrain projects in the past the odds are nothing here is news to you but I'm presenting what I'm doing as it will hopefully form the basis for more interesting things to come or if you're thinking about a terrain project for the first time perhaps it will be of use.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: Verdana, sans-serif;">The Base Polyhedron</span></h3>
<span style="font-family: Verdana, sans-serif;">For this project therefore I've chosen to base my planet on a regular <a href="http://en.wikipedia.org/wiki/Icosahedron" target="_blank">Icosahedron,</a> a 20 sided polyhedron where each face is an equilateral triangle. The geometry produced by subdividing this shape is more regular than the distorted cube as you are essentially working with equilateral triangles at all times. Each progressive level of detail takes one triangle from the previous level (or one of the 20 base triangles from the Icosahedron at the first level) and turns it into four by adding new vertices at the midpoints of each edge. Each of the new vertices is then scaled to put it back on the unit sphere:</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg_w4X0qojEU6zcFv0_ZcXOtYlwyWOYdMn88cZRv4ZIu8aXRDDEoWJkEiYMXWc_RIbGLHRN71BiF9XPbMQvZ_g1JQJNZ-hdj_8rIixEmdqjotrHqMwVIbzmVOkCo9S5_-buYK-t1HBV4Y/s1600/TriSubdivision.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjg_w4X0qojEU6zcFv0_ZcXOtYlwyWOYdMn88cZRv4ZIu8aXRDDEoWJkEiYMXWc_RIbGLHRN71BiF9XPbMQvZ_g1JQJNZ-hdj_8rIixEmdqjotrHqMwVIbzmVOkCo9S5_-buYK-t1HBV4Y/s1600/TriSubdivision.png" height="291" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Subdividing an equilateral triangle into four for the next level of detail produces three new vertices and four triangles to replace the original one.</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">This is about as simple a scheme as you can get and can be found in many terrain projects, but simplicity is what I'm after here so it's perfectly suited. As modern graphics hardware likes to be given large numbers of triangles with each draw call rather than render single terrain triangles the basic unit I render (which I'm calling </span><i style="font-family: Verdana, sans-serif;">A Patch</i><span style="font-family: Verdana, sans-serif;">) is a triangle that's already been subdivided eight times, producing an equilateral triangle shaped chunk of geometry that has 256 triangles along each edge - a total of 65,536 triangles which should feed the GPU nicely especially as they are drawn as a single tri-strip. Were I to draw the entire planet at my lowest level of detail then I would be drawing 20 of these patches giving around 1.3 million triangles which empirical testing has shown my GPU eats up without pausing for breath. Good start.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: Verdana, sans-serif;">Level of Detail (LOD)</span></h3>
<span style="font-family: Verdana, sans-serif;">While a million triangles might not give a decent GPU pause for thought these days, what this does show is that with each level of subdivision multiplying the triangle count by four you do get to some pretty high triangle counts after just a few levels of subdivision. With planets being pretty big as well, those million triangles still don't give a terribly high fidelity rendition of the physical terrain - on an Earth sized planet the vertices are still more than 26 Km apart at this level,</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">As the viewpoint moves towards the planet's surface then I need to subdivide further and further to get down to a usable level of detail, but by then the triangle count would probably be too much to even store in memory never mind render were I to try to render the entire planet at that detail so of course some sort of level of detail scheme is required.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">In keeping with my general approach the scheme I'm using is simple, when rendering I start by adding the 20 base patches mentioned above to a working set of candidates for rendering, then while that set isn't empty I take patches out of it and first test them for visibility against the view frustum then again against the planet's horizon in case they are effectively on the back side of the planet. If either of those tests fail I can throw the patch away, but if both pass I then calculate how far away from the viewpoint it's centroid is and if less than a certain factor of the patch's radius I subdivide it into four patches at the next level of detail and add those into the set of patches for further consideration. If the patch is not near enough to the viewpoint to be subdivided I add it to the set of patches to render.</span><br />
<br />
<span style="font-family: Verdana, sans-serif;">The end result is a set of patches where near to the viewpoint are increasingly subdivided patches - i.e higher LOD each covering a smaller area at a higher fidelity - while moving away from the viewpoint are progressively lower LOD level patches each covering larger areas of the terrain at lower fidelity. The ultimate goal is to provide a roughly uniform triangle size on screen across all rendered patches, note that t</span><span style="font-family: Verdana, sans-serif;">his is intentionally a pretty basic system with no support for more advanced features such as geomorphing so the LODs pop when they transition but it's good enough for my purposes.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkjZzbNEhDSYF0RH4QlzlgS-9XWlPzwvwPZ4bmcWrN0AeXUu9fCX_fywiDA1JgNasLLhyae5e4amaYaWAr01OFLbkOO1jPdFQlv6BXXa_FEkhPcZuXoGPSdjyfAs36ZYu7lt6uvfzeLTg/s1600/LODs.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkjZzbNEhDSYF0RH4QlzlgS-9XWlPzwvwPZ4bmcWrN0AeXUu9fCX_fywiDA1JgNasLLhyae5e4amaYaWAr01OFLbkOO1jPdFQlv6BXXa_FEkhPcZuXoGPSdjyfAs36ZYu7lt6uvfzeLTg/s1600/LODs.png" height="206" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Colour-coded patches of different LODs, note how the triangle size is different in each colour with the smaller triangles closest to the viewpoint</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">A useful feature of the system is that by changing the threshold factor used to decide whether to subdivide a patch or not a simple quality control can be established. Increase the threshold and patches will subdivide at greater distances increasing the number of patches and therefore triangles rendered using higher detail representations increasing fidelity but lowering performance while decreasing the threshold has the opposite effect - patches subdivide closer to the viewpoint causing fewer high detail patches to be rendered reducing the triangle count and improving performance at the expense of graphical fidelity</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMvaKnATvyIMn63mZpgf72zBCriZHP8jJ-_CMMYzUaVbHa06a7xtq_T58meGhgAl4cs06cyuEidCArGvRxOQSgHkMOew2ggteFyxN4Kov6udd30rFar05Ljz_23pmMX0OOyPqHhyyC-1I/s1600/LowLOD.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMvaKnATvyIMn63mZpgf72zBCriZHP8jJ-_CMMYzUaVbHa06a7xtq_T58meGhgAl4cs06cyuEidCArGvRxOQSgHkMOew2ggteFyxN4Kov6udd30rFar05Ljz_23pmMX0OOyPqHhyyC-1I/s1600/LowLOD.png" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>At low detail (a large transition distance) the geometry is relatively coarse. 900K triangles are being rendered here</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwVmxGIQuYtWLxEamrnz6znZWnTVSiBNSg_oRySyl82e7AgOsn10xPnMeEdmGrl8XRIkP9F72V0o9wcnnT3Nat9Aoeel3idhPvVz3cDLefHuQNXANCjVKK4U77t6rPMMx1Kw_DHQ8nGhA/s1600/MediumLOD.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwVmxGIQuYtWLxEamrnz6znZWnTVSiBNSg_oRySyl82e7AgOsn10xPnMeEdmGrl8XRIkP9F72V0o9wcnnT3Nat9Aoeel3idhPvVz3cDLefHuQNXANCjVKK4U77t6rPMMx1Kw_DHQ8nGhA/s1600/MediumLOD.png" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Moving the transition distance towards the viewpoint gives a medium detail level with more higher detail patches being rendered making the triangles near the viewpoint smaller to improve fidelity. 1.8 Million triangles are being rendered here</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkWxgdZOjJBKgYWDYOq1i6IyeAnexacw_yVmpQgZPBkkx4rmvYMgdltansoboVwanqf6dvOSprz9aBOQm5xbpGOrYiHED1QfRVr2xc5hiJHqmzSVgPfTT_CG0jxNyfdZ9atLWERf1-3qc/s1600/HighLOD.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkWxgdZOjJBKgYWDYOq1i6IyeAnexacw_yVmpQgZPBkkx4rmvYMgdltansoboVwanqf6dvOSprz9aBOQm5xbpGOrYiHED1QfRVr2xc5hiJHqmzSVgPfTT_CG0jxNyfdZ9atLWERf1-3qc/s1600/HighLOD.png" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Closer still and we have a high detail level with even more high detail patches being used. 5.7 Million triangles are being rendered here with the ones near the viewpoint almost a pixel in size.</i></td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: left;">
<span style="font-family: Verdana, sans-serif;">In theory this value could be changed on the fly as the frame rate varies due to rendering load to try to maintain an acceptable frame rate, but I just set it to a decent fixed value for my particular PC/GPU for now.</span></div>
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">As anyone who has worked with terrain LODs before will tell you however this is only part of the solution, where patches of different LODs meet you can get cracks in the final image due to there being more vertices along the edge of the higher LOD geometry than along the edge of the lower LOD geometry. If one of these vertices is at a significantly different position to the equivalent interpolated position on the edge of the lower LOD geometry there will be a gap. Note that as the higher LOD geometry is typically closer to the viewpoint you only tend to see these in practice where the vertex is lower than the interpolated position otherwise the crack is facing away from the viewpoint and is hidden.</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipo038QtCzROzk9KOWf9rabBVDrZlVFEHED_uri1AP9QBMIPrCh_xglAy4akxZUOPBF7r24pH_mh5wGP8dGkiiDozxq97hdVbhJ8kH1O_mbw1npKaFMWL3Ojyflx1pDAEB3o_u2_1mtwA/s1600/LODCracks.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipo038QtCzROzk9KOWf9rabBVDrZlVFEHED_uri1AP9QBMIPrCh_xglAy4akxZUOPBF7r24pH_mh5wGP8dGkiiDozxq97hdVbhJ8kH1O_mbw1npKaFMWL3Ojyflx1pDAEB3o_u2_1mtwA/s1600/LODCracks.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Cracks between patches of different LODs due to the additional vertices on the high detail patch not lining up with the edge they subdivide on the lower detail patch. (Note I've zoomed in on the effect here to highlight it, whilst still visible normally the cracks are much smaller on screen)</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">There are various ways to tackle this problem, one I've used before is to have different geometry or vertex positions along the edges of higher LOD patches where they meet lower LOD patches so the geometry lines up, but this adds complexity to the patch generation and rendering, and in it's simplest form at least limits the LODing system to only support changing one level of LOD between adjacent patches.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">For this project I've taken a different approach and rather than changing the connecting geometry I generate and render narrow skirts around the edges of each patch. These are the width of one triangle step, travel down towards the centre of the planet and are normally invisible but where a crack would exist their presence fills that gap hiding it with pixels that while not strictly correct are good enough approximations to be invisible to the eye. The pixels aren't correct as they are textured with the colours from the skirt's connecting edge on the patch so are a stretchy smear of vertical colour bars but like I say they are close enough, especially as the gaps are usually just a pixel or two in size when using reasonably dense geometry.</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMzgC876h5DF7b3VozqVy2PoSar9N2G3epFDEWjhWFSXyk8JVDSOjs_t3ExsQOvnJOBj55CrRHzBrTAhWon-qBOQKL61AOXkEAjSQ2qvWQkD2FB84WAt3yMfxOP8NMJafUvpQSOlijtQ8/s1600/LODCracksFilled.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMzgC876h5DF7b3VozqVy2PoSar9N2G3epFDEWjhWFSXyk8JVDSOjs_t3ExsQOvnJOBj55CrRHzBrTAhWon-qBOQKL61AOXkEAjSQ2qvWQkD2FB84WAt3yMfxOP8NMJafUvpQSOlijtQ8/s1600/LODCracksFilled.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>With one triangle wide skirts on the patches the gaps are filled with pixels close enough to the surface of the patches in colour that they become invisible to the eye.</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">Comparing the before and after in wireframe makes the strip of new geometry along the join clear:</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNBG_lLTf4mK_jSLLoOFtP6yPQbYBURW6lG_DLyXFPhWuVgj7lTnz3Y-pc8tCBKDghtHTVItNb1nuVB_O9At4fxvWcLo6YxyBLQPaYtFVL9RAfA20nO5i7P4Xp7PTBNOvQ6qTZZ6k6kBM/s1600/LODCracksWireframe.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNBG_lLTf4mK_jSLLoOFtP6yPQbYBURW6lG_DLyXFPhWuVgj7lTnz3Y-pc8tCBKDghtHTVItNb1nuVB_O9At4fxvWcLo6YxyBLQPaYtFVL9RAfA20nO5i7P4Xp7PTBNOvQ6qTZZ6k6kBM/s1600/LODCracksWireframe.png" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Wireframe without the skirts shows why the cracks appear</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBUu6r5hMpedh4ouCQBDCGxltRkKc1bbQGZ93Oat4ywL-0SfDHKGJB7Fl3k-tLW0IKb4m9aph2fvdwzSVVvwRzsPT459nhglq-Fp-P92GBr6hvrlDn99OdAxkVo1cLtL9fHqU-xjVbN2g/s1600/LODCrackesFilledWireframe.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBUu6r5hMpedh4ouCQBDCGxltRkKc1bbQGZ93Oat4ywL-0SfDHKGJB7Fl3k-tLW0IKb4m9aph2fvdwzSVVvwRzsPT459nhglq-Fp-P92GBr6hvrlDn99OdAxkVo1cLtL9fHqU-xjVbN2g/s1600/LODCrackesFilledWireframe.png" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Wireframe when rendering the patch skirts shows the additional strip of geometry pointing straight down from the edge of the lower detail patch. The higher detail patch also has a skirt but here they are back facing and culled by the GPU.</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">This does of course add extra geometry to each patch (1536 triangles in my case with 256 tris along each patch edge) which I am always rendering regardless of the LOD transition situation to enable them to be sent with the same single draw call per patch, but this is a small GPU overhead and doesn't appear to have any practical effect on performance.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: Verdana, sans-serif;">Floating Point Precision</span></h3>
<span style="font-family: Verdana, sans-serif;">Anyone who's had a go at rendering planets will almost certainly have encountered the hidden evil of floating point precision. Our GPUs work primarily in single precision floating point which is fine for most purposes but when we're talking about representing something tens of thousands of kilometres across down to a resolution of a centimetre or two the six or so significant digits offered is simply not enough. This typically shows up as wriggly vertices or texture co-ordinates as the viewpoint moves closer to the surface, certainly not desirable.</span><br />
<br />
<span style="font-family: Verdana, sans-serif;">To avoid this I store the single precision vertices for each patch not relative to the centre of the planet but relative to the centre of the patch itself. The position of this per-patch centre point is stored in double precision for accuracy which is then used in conjunction with the double precision viewpoint position and other parameters to produce a single precision world-view-projection matrix with the viewpoint at the origin.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">That final point is significant, by putting the viewpoint at the world origin for rendering I ensure the maximum precision is available in the all important region closest to the viewer.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: Verdana, sans-serif;">The Heightfield</span></h3>
<span style="font-family: Verdana, sans-serif;">So now I have a reasonable way to render my planet's terrain geometry viewed anywhere from orbital distances down to just a metre or so from the ground I need to generate a heightfield representative enough of real world terrain to make placement of settlements and their infrastructure plausible. I've played around with stitching together real world <a href="http://en.wikipedia.org/wiki/Digital_elevation_model" target="_blank">DEM</a> data before but have never been too happy with the results, especially when trying to blend chunks together or hide their seams, so for this project I'm sticking to a purely noise based solution for the basic heightfield.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Relying so heavily on noise made me wary however or re-using my trusty home grown noise functions so instead I've opted to use the excellent <a href="http://libnoise.sourceforge.net/" target="_blank">LibNoise</a> library which produces quality results, is very flexible, is fast and importantly for this multi-core world is thread safe - the last being particularly important </span><br />
<span style="font-family: Verdana, sans-serif;">as I have a huge number of samples to generate.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">A common feature of purely noise based heightfields however that you see frequently on terrain projects is that they tend to look very homogeneous with very similar hills and valleys everywhere you look. To make my planet more interesting I wanted to try to get away from this so had a think about how to introduce more variety.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">The solution I came up with was to implement a suite of distinct heightfield generators each using a combination of noise modules to produce a certain type of terrain. As a starting point I went with mountainous, hilly, lowlands, mesas and desert, but how to decide which terrain type to use where? One solution would be to use yet another noise function to drive the selection, but I thought it would be interesting to try something else, and ideally a solution that gave me more control over the placement of different terrains while still providing variety. The control aspect would allow me to make general provisions for placement such as having deserts more around the equator or mountains around the poles which feels useful. This doesn't violate my basic tenet of procedural generation - procedural isn't the same as random remember.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDY37TrJAoG7kL4cO-u1M-53eBR6xRB0PNtgLPt-wdH6cYe8kXqVO_XcG2I3mjeI2PmWH-zaI5sx-2J95NgFpREV_d-9JIhQlt9gdYepaem00YkoPUTpN1Ze64upB57OpDSyB5-3ZO8Aw/s1600/TerrainMountains.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDY37TrJAoG7kL4cO-u1M-53eBR6xRB0PNtgLPt-wdH6cYe8kXqVO_XcG2I3mjeI2PmWH-zaI5sx-2J95NgFpREV_d-9JIhQlt9gdYepaem00YkoPUTpN1Ze64upB57OpDSyB5-3ZO8Aw/s1600/TerrainMountains.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>An example of the "Mountainous" terrain type, generated by a ridged multi-fractal</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8_GrBkJb9Y5OP_AkN2vZcRCLlk-lbxkrPmlOS3lE8jC6Q2gqwtyoc1A8Yb7lhUmNSxBcLk1xEFwDCVoZ10HFMgNjiFnXL8p-5m8ub4zSeAcOdJTqXanmn-m050-M_7DN1J4ahmB5LqIc/s1600/TerrainHilly.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj8_GrBkJb9Y5OP_AkN2vZcRCLlk-lbxkrPmlOS3lE8jC6Q2gqwtyoc1A8Yb7lhUmNSxBcLk1xEFwDCVoZ10HFMgNjiFnXL8p-5m8ub4zSeAcOdJTqXanmn-m050-M_7DN1J4ahmB5LqIc/s1600/TerrainHilly.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The "Hilly" terrain type uses the absolute value of a couple of octaves of perlin noise to try to simulate dense but lower level hills</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOiPzFTHXpTs6JqYrLTTFF4qvt1Q5GH3zweM5xbye46a46Jd60V0cp-ChCKaefDHcqkTG6V7ERbWxtb0y_jWFW8oF9ZS_297wvaQLKbde1235Yol4K5W0N1qjEDZInck2PSRZ2_Za8VgU/s1600/TerrainMesas.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOiPzFTHXpTs6JqYrLTTFF4qvt1Q5GH3zweM5xbye46a46Jd60V0cp-ChCKaefDHcqkTG6V7ERbWxtb0y_jWFW8oF9ZS_297wvaQLKbde1235Yol4K5W0N1qjEDZInck2PSRZ2_Za8VgU/s1600/TerrainMesas.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The "Mesas" terrain type again uses octaves of noise but thresholds the result to produce low lying flatlands punctuated by steep flat topped hills</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">Instead of noise then I chose to use a control net similar to the country one detailed in my previous post, but instead of country IDs this time I put a terrain type on each control point - I also use the direct triangulation of the control net rather than the dual. Shooting a ray from the planet's surface against this terrain control net gives me three terrain types from the vertices of the intersected triangle while taking the <a href="http://en.wikipedia.org/wiki/Barycentric_coordinate_system" target="_blank">barycentric co-ordinates</a> of the intersection point gives blending weight for combining them.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvvSDsqaSaUPHt_Cz4fcRVM7RcCwRElYf8qZDVbgisfaZMa1VBWx_vJn8mk2ajEy0J2SVT7k7MdBsVm67SclJQSAxcMNVxtCs-rsWTmOejAVpETy5IMrdy-p4qmd62INcBowwJI-7AMKE/s1600/TerrainControlNet.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvvSDsqaSaUPHt_Cz4fcRVM7RcCwRElYf8qZDVbgisfaZMa1VBWx_vJn8mk2ajEy0J2SVT7k7MdBsVm67SclJQSAxcMNVxtCs-rsWTmOejAVpETy5IMrdy-p4qmd62INcBowwJI-7AMKE/s1600/TerrainControlNet.png" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The terrain type control net. Each vertex has a terrain type assigned to it so any point on the surface can be represented as a weighted combination of up to three terrain types.</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">Taking the result of each of these terrain type's heightfield generators, scaling by their blending weights then adding them together gives the final heightfield value. There is an obvious optimisation here too where a triangle has the same terrain type at two or more of it's vertices - the heightfield generator for that terrain type can be run just once and the blending weights simply added together.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTe2juTpk8M8yLWFWGUqlMFXuiCMNpitBDlDQz8Gd4XRKpV3UXmgIfkdMcPB1zsDEecqCbzoyg6UGDDDCYALRCKE3-9U3ETCey7fOunntow7HeO7TCqHOYoOc8sXW56kF8B_NL0y4zQTE/s1600/BlendedTerrain.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhTe2juTpk8M8yLWFWGUqlMFXuiCMNpitBDlDQz8Gd4XRKpV3UXmgIfkdMcPB1zsDEecqCbzoyg6UGDDDCYALRCKE3-9U3ETCey7fOunntow7HeO7TCqHOYoOc8sXW56kF8B_NL0y4zQTE/s1600/BlendedTerrain.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Here the "Hilly" terrain generator's result in the foreground is blending into the "Mountainous" terrain generator's result in the middle distance</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">I quite liked the idea of being able to craft each type of terrain separately so the look of the lowlands can for example be tweaked without affecting the mountains which can sometimes be tricky with monolithic noise systems, the frequency of terrain changes and their locations can also be controlled by changing the density of the terrain control net or playing with the assignment of terrain types to the control points. For now I'm just assigning types randomly to each point but you could for example use the latitude and longitude of the point as an input to the choice or assign initial points then do some copying between adjacent points to produce more homogeneous areas where desired.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: Verdana, sans-serif;">Texturing</span></h3>
<span style="font-family: Verdana, sans-serif;">An interesting heightfield is all well and good and whiled I've stated that physical terrain is not really the focus of this project I do want it to look good enough to be visually pleasing. To this end I did want to spend at least some time on the texturing for the terrain so my desert could look different to my lowlands and my mesas different to my alpine mountains. Past experience has shown me that one of the more problematic areas with texturing is where the different types of terrain meet, producing realistic transitions without an exorbitantly expensive pixel shader can be tricky. The texturing also needs to work on all aspects of the geometry regardless of which side of the planet it's on or how steep the slope.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">The latter problem is solved by using a triplanar texture blend, using the world space co-ordinates of the point to sample the texture once each in the XY, ZY and XZ planes and blending the results together using the normal. This avoids the stretching often seen in textures on heightfields especially when not mapped on to spheres when a single planar projection is used but does of course take three times as many texture sample operations. If you look closely you can also see the blend on geometry furthest from aligning with the orthogonal planes but as terrain textures tend to be quite noisy this isn't generally a problem - and is certainly less objectionable than the single plane stretching.</span><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKxEgfzISx6kXUo220p56KHCCE62JgBodojuPfstfMkhqGRfzaCUkaTNlbpAq2evF4W7B1S2bNvly6kTPCSGCVOHnXpDqzFmqO46TCwfJVMG8nFpjvfo7D_pRKyPWc2KmTINxscWZnkRk/s1600/PlanarMappingXY.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKxEgfzISx6kXUo220p56KHCCE62JgBodojuPfstfMkhqGRfzaCUkaTNlbpAq2evF4W7B1S2bNvly6kTPCSGCVOHnXpDqzFmqO46TCwfJVMG8nFpjvfo7D_pRKyPWc2KmTINxscWZnkRk/s1600/PlanarMappingXY.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Using a single planar mapping in the XY plane produces noticeable texture stretching on the slopes</i></td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEie8paeGyNjxlWoJ-dG60TL8mdjhW3cNmsnwaN4Y90P91MyUtN-4iNJ-V9RTD-SFALZ-bun3PPzWKaCf0kNFDek9kRDb0cAh1utWVGRNkDjeqLgX-P830dZTM4lIHw3_cigO_O65L3xQQI/s1600/PlanarMappingXZ.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEie8paeGyNjxlWoJ-dG60TL8mdjhW3cNmsnwaN4Y90P91MyUtN-4iNJ-V9RTD-SFALZ-bun3PPzWKaCf0kNFDek9kRDb0cAh1utWVGRNkDjeqLgX-P830dZTM4lIHw3_cigO_O65L3xQQI/s1600/PlanarMappingXZ.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Changing to the XZ plane fixes most of those but produces worse stretching in other places</i></td></tr>
</tbody></table>
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVxQ9e5_ARxWPTpzc0dlou1lMX3EgaFGzpxA9EcpOjA3l01iCMbNhVUKj1xgbEtHDCaZvPlCcj8YYwvZyX3jvC6QIsOrqm3oeJzUAQz0DSmV64RLFiyYqsYo1wVuhI_DHC4WO2Ux4VZYc/s1600/PlanarMappingZY.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjVxQ9e5_ARxWPTpzc0dlou1lMX3EgaFGzpxA9EcpOjA3l01iCMbNhVUKj1xgbEtHDCaZvPlCcj8YYwvZyX3jvC6QIsOrqm3oeJzUAQz0DSmV64RLFiyYqsYo1wVuhI_DHC4WO2Ux4VZYc/s1600/PlanarMappingZY.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Third choice is the ZY plane but again we're really just moving the stretching artifacts around</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinojrvq6stOW_ZCruI-t-jOXLqgC99grJPlu8BpuVBXAV3AO0WDMjYBwVb9YCcTsUD-DwECc8z8d7vAY7y72p_pHtwt53QAKDEWgj4z3kdr_lFwhRKksQ86eEZXjnsk23bGDRtsymtaas/s1600/TriPlanarMapping.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEinojrvq6stOW_ZCruI-t-jOXLqgC99grJPlu8BpuVBXAV3AO0WDMjYBwVb9YCcTsUD-DwECc8z8d7vAY7y72p_pHtwt53QAKDEWgj4z3kdr_lFwhRKksQ86eEZXjnsk23bGDRtsymtaas/s1600/TriPlanarMapping.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The solution is to perform all three single plane mappings and use the world space normal to perform a weighted combination of all three. Although this requires three times as many texture reads the stretching artifacts are all but gone.</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">As for which texture to use at each point I've adopted something of a brute force approach. By keeping the palette of terrain textures relatively limited I am able to store a weight for each one on each vertex, in my case eight textures so the weights can be stored as two 32bit values on each vertex. The textures I've chosen are designed to give me a decent spread of terrain types, so they include for example two types of grassland, snow, desert sand and</span><span style="font-family: Verdana, sans-serif;"> rocky dirt.</span><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUWOGB89VNyIuAGs2q3nxrC-qugfz12jExyp8Zo07w1q0rK94AFcRttzXyw3dRq0RSNwcmc2fy8m4EkUQWsA_S4HpaYg1Oc6ESVSahC3DbMrQkLayYPI1fxpgnw8foJ_iD2dm5DtIjaDk/s1600/Textures.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUWOGB89VNyIuAGs2q3nxrC-qugfz12jExyp8Zo07w1q0rK94AFcRttzXyw3dRq0RSNwcmc2fy8m4EkUQWsA_S4HpaYg1Oc6ESVSahC3DbMrQkLayYPI1fxpgnw8foJ_iD2dm5DtIjaDk/s1600/Textures.jpg" height="127" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Example basic textures for dirt, desert/beach sand and grass</td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">By storing a weight for each texture independently I avoid any interpolation problems where textures meet as every texture is essentially present everywhere. It does of course mean that the pixel shader in theory will need to perform up to eight triplanar texture mappings each requiring three texture reads (twice that if normal mapping is supported) but in practice most pixels are affected by only a couple of textures. If I was doing this in a more serious production environment I would be thinking about having several variations of the pixel shader each specialised for a certain number and combination of textures to avoid potentially expensive conditionals or redundant texture reads but as this is just a play project one general purpose shader will suffice.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyRpOCS4Duig3iDaNoBPA5e7ngGs9Bc9DyH33FNX9tUuuc3Os9Vn8AZvyQQrECxDFNhjbR_aYfSG8K5ftR0KBSBdvZQaDAyRQ5lm67pxOlDy1nBHwvHU7HkTqzmonhVnDJ9R3MNxMbTXA/s1600/TerrainGlobeUnperturbed.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhyRpOCS4Duig3iDaNoBPA5e7ngGs9Bc9DyH33FNX9tUuuc3Os9Vn8AZvyQQrECxDFNhjbR_aYfSG8K5ftR0KBSBdvZQaDAyRQ5lm67pxOlDy1nBHwvHU7HkTqzmonhVnDJ9R3MNxMbTXA/s1600/TerrainGlobeUnperturbed.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Blending the terrain type textures based purely upon the terrain control net. The blending works but the linear interpolation artifacts along the net's triangulation boundaries are obvious and unsightly</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">While the textures blend together here the shape of the terrain control net is still quite obvious. To improve matters I fell back to a technique I first trialled while playing with the country generation code, by perturbing the direction of the ray fired from the surface against the terrain control net more variety can be included while still offering the controllability of the net.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">Applying a few octaves of turbulence to the ray gives a much more organic effect:</span><br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDzglUaFZzNOegCYvC2l5gyu3CFs6DI0gb8s207QaneVmyUSjYi7TG5FpoaNIn8gxYc0ijo8eVXTGu-b2FtvSttjBZ7wPxDXjNjV3KGJMhzkAlsSfIHI2tNG0oQQq9j9X6GlJAm5qTXYg/s1600/TerrainGlobePerturbed.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDzglUaFZzNOegCYvC2l5gyu3CFs6DI0gb8s207QaneVmyUSjYi7TG5FpoaNIn8gxYc0ijo8eVXTGu-b2FtvSttjBZ7wPxDXjNjV3KGJMhzkAlsSfIHI2tNG0oQQq9j9X6GlJAm5qTXYg/s1600/TerrainGlobePerturbed.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Using some turbulence to perturb the ray from the planet's surface before intersecting it with the terrain control net produces a far more satisfyingly organic and pleasing effect</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">I'm particularly happy with how this looks from orbital distances but it will suffice closer to the surface for now as well.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: Verdana, sans-serif;">A Note on Atmospheric Scattering</span></h3>
<span style="font-family: Verdana, sans-serif;">You may have noticed in these screenshots that I've also added some atmospheric scattering. For terrain rendering this is one of the most effective ways of both improving the visuals and adding a sense of scale - taking it away makes everything look pretty flat and uninteresting by comparison:</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1y17Nu0CBUs0XPBN8sE98jw21vS_DovZl6dEHvq-eahVCGIXIoyOUBvLCLVC54RAD6hCkqi2iIDikqatvhBXVIeH1wLRav5u3TdV7qmtgU3sys3-W4BDuCu1aOVhyphenhyphenzuxxfWWCuFfLsiM/s1600/ScatteringOff.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi1y17Nu0CBUs0XPBN8sE98jw21vS_DovZl6dEHvq-eahVCGIXIoyOUBvLCLVC54RAD6hCkqi2iIDikqatvhBXVIeH1wLRav5u3TdV7qmtgU3sys3-W4BDuCu1aOVhyphenhyphenzuxxfWWCuFfLsiM/s1600/ScatteringOff.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Without the atmospheric scattering the perspective and scale is unclear</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEin423jnbaYboJIH5fhACPiIF11BNEFnqo-gsGC80IbFW9dX8cxYqoKLqpTuRR4chq9TXUUij5NpsEjXvv5_D6ni73smNkLADbmSut8kY8I5MAJFgYk9EAtw2YiLdDHvNvhKar8227DYNI/s1600/ScatteringOn.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEin423jnbaYboJIH5fhACPiIF11BNEFnqo-gsGC80IbFW9dX8cxYqoKLqpTuRR4chq9TXUUij5NpsEjXvv5_D6ni73smNkLADbmSut8kY8I5MAJFgYk9EAtw2YiLdDHvNvhKar8227DYNI/s1600/ScatteringOn.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>With atmospheric scattering the sense of scale and distance is much improved</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">In a previous project I implemented the system published in <a href="http://www-ljk.imag.fr/Publications/Basilic/com.lmc.publi.PUBLI_Article@11e7cdda2f7_f64b69/article.pdf" target="_blank">Precomputed Atmospheric Scattering</a> by Eric Bruneton and Fabrice Neyret which produced </span><span style="font-family: Verdana, sans-serif;">excellent results, but in the spirit of trying new things I thought for this project I would try <a href="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html" target="_blank">Accurate Atmospheric Scattering</a> by Sean O'Neil found in the book GPU Gems 2. This looked like it might be a bit lighter weight in performance terms and easier to tweak for custom effects.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">I found the O'Neil paper relatively straightforward to implement which was refreshing after some papers I've struggled through in the past and it didn't take too long to get something up and running. The only thing I changed was to move all the work into the pixel shader rather than doing some in the vertex shader.</span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">There were two reasons for this, the first being that when applied to the terrain the geometry is quite dense so the performance cost implication should be minimal and it would avoid any interpolation artifacts along the triangle edges. The second is pretty much the opposite however as rather than rendering a geometric sky sphere around the planet onto which the atmosphere shader is placed I wanted to have the sky rendered as part of the skybox. </span><br />
<span style="font-family: Verdana, sans-serif;"><br /></span>
<span style="font-family: Verdana, sans-serif;">The skybox is simply a cube centred on the viewpoint rendered with a texture on each face to provide a backdrop of stars and nebulae, as this was already filling all non-terrain pixels adding the atmosphere to that shader seemed to make sense but as the geometry is so low detail computing some of the scattering shader in the vertex shader didn't seem like a good plan as I felt visual interpolation artifacts were bound to result.</span><br />
<br />
<span style="font-family: Verdana, sans-serif;">Bear in mind that this only really makes sense if the viewpoint is generally close enough to the terrain that the skybox fills the sky - i.e. within the atmosphere itself. Were I planning to have the viewpoint further away for appreciable time so the planet would be smaller on screen I would instead add additional geometry for the sky to prevent incurring the expensive scattering shader on pixels that are never going to be covered by the atmosphere.</span><br />
<br />
<span style="font-family: Verdana, sans-serif;">To make a smooth transition as the viewpoint enters the atmosphere a simple alpha blend is used to fade the starfield skybox texture out as the viewpoint sinks into the atmosphere:</span><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvbx2MCW_e4CcX0V7JAcZ9IGwPbGqHODnZRNJ-CeKcV03vMhjP3FUVT7VtnRxuoU8YDYPpaxY_8LJZETtUp0swmEJaj_6n6pUn8-PMd7ezYDIvxpUaHwYZcaHQL9nPc26yHem61IlOCRA/s1600/Stars1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvbx2MCW_e4CcX0V7JAcZ9IGwPbGqHODnZRNJ-CeKcV03vMhjP3FUVT7VtnRxuoU8YDYPpaxY_8LJZETtUp0swmEJaj_6n6pUn8-PMd7ezYDIvxpUaHwYZcaHQL9nPc26yHem61IlOCRA/s1600/Stars1.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>From high orbit the stars and nebulae on the skybox cubemap are clear</i></td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSpD3Nx6q-RG0HPouTwK6YgqqdBy-6QMpWnnGX688nObU9vW7R9eHKlmextPjYP9y71VmlWPv-_QFyDf77r76cZhOAwfOuMbdTSH-7vx5flslqYYQwEy29_hM7Nv3IjgQPfbpWZEDBvCA/s1600/Stars2.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSpD3Nx6q-RG0HPouTwK6YgqqdBy-6QMpWnnGX688nObU9vW7R9eHKlmextPjYP9y71VmlWPv-_QFyDf77r76cZhOAwfOuMbdTSH-7vx5flslqYYQwEy29_hM7Nv3IjgQPfbpWZEDBvCA/s1600/Stars2.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Descending into the atmosphere the scattering takes over and the stars start to fade out</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg78i4f_g8vr8NzvcvlUDqnJDuIkrDUgZC3VKD7Nq0f49C11159fLhMJj9OnoGoQ3rB6sEYeB6OyRgZay0kyj0iVcXsIvk1_Ka533nkwsSxqPT-NWaSDdpBfLvbp1JDvZwTM9W6mSmkvMg/s1600/Stars3.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg78i4f_g8vr8NzvcvlUDqnJDuIkrDUgZC3VKD7Nq0f49C11159fLhMJj9OnoGoQ3rB6sEYeB6OyRgZay0kyj0iVcXsIvk1_Ka533nkwsSxqPT-NWaSDdpBfLvbp1JDvZwTM9W6mSmkvMg/s1600/Stars3.jpg" height="207" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Much closer to the surface the stars are only barely visible. Keeping descending and they disappear altogether leaving a clear blue sky.</i></td></tr>
</tbody></table>
<span style="font-family: Verdana, sans-serif;">I think that pretty much covers the terrain visuals, probably in more detail than I initially intended but I think it's useful to get the fundamentals sorted out. I reckon what I have so far should be an interesting enough basis to work forwards from.</span>John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com1tag:blogger.com,1999:blog-3862349614860735414.post-34528604765479880592014-11-23T14:19:00.000+00:002014-11-23T14:19:21.359+00:00SkySagaRather than a project update this time, I'm pleased to share some links to the great new game <i>SkySaga: Infinite Isles</i> that's just been announced. This is being developed by the company I work for <a href="http://www.radiantworlds.com/" target="_blank">Radiant Worlds</a> and we're all super excited about our efforts soon becoming available to the world.<br />
<div>
<br /></div>
<div>
Here's our announce trailer:<br />
<br /></div>
<div>
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/p-X0KV5-aFQ" width="560"></iframe></div>
<div>
<br />
And some gameplay:<br />
<br />
<iframe allowfullscreen="" frameborder="0" height="315" src="//www.youtube.com/embed/SqQOXyXZ03k" width="560"></iframe>
<br />
More information can be found at the game website: <a href="http://www.skysaga.com/">http://www.skysaga.com/</a> including how you can get involved with the alpha program right now.<br />
<br />
Enjoy!<br />
<br />
<br /></div>
John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0tag:blogger.com,1999:blog-3862349614860735414.post-20413871075959750172014-10-15T22:10:00.001+01:002014-10-15T22:10:29.289+01:00Country BoundariesAfter spending some time on a system to make names for places as described in my last project post I thought it would be interesting to look at creating countries for my planet I could then attach such names to. I wanted to create a system where I could determine from any point on the surface of the planet which country it was part of.<br />
<br />
I thought the easiest way to go about this would be to create a triangulated shell around the planet against which a ray from and normal to the surface could be intersected, the triangle that was hit would let me know which country the point was within.<br />
<br />
My very first go at this involved setting up an array of 200 points chosen at random around the surface of a sphere enclosing the planet, these were created by simply choosing random numbers in the [-1, 1] range for the X, Y and Z positions then normalising the resulting vector. Once I had these I use the <a href="http://thomasdiewald.com/blog/?p=1888" target="_blank">QuickHull Algorithm</a> to produce a triangulation of the convex hull formed by these points and assigned a country ID to each - albeit really just a colour at this point. The control net mesh looked like this:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1TY0i85PoUHjnobNMwxbtSZ0jAEUw2gidSh9ZFUDgc0laNb0wOTe7iA2_JKTXJxPQ-7tJqFw_G-EJlp9FrSsHTvH5TBRYCzzgS4qBA8GFNDz-CpsA6WTohSxr6i4e5V3wpvKGOTRFJtg/s1600/01_Triangles.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1TY0i85PoUHjnobNMwxbtSZ0jAEUw2gidSh9ZFUDgc0laNb0wOTe7iA2_JKTXJxPQ-7tJqFw_G-EJlp9FrSsHTvH5TBRYCzzgS4qBA8GFNDz-CpsA6WTohSxr6i4e5V3wpvKGOTRFJtg/s1600/01_Triangles.jpg" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Triangulation of country control net created purely from seed points</i></td></tr>
</tbody></table>
For now I'm just using a sphere created by subdividing an Icosahedron for my planet's terrain and it's purely spherical with no elevation variation to ease visualisation. Taking the centroid of each terrain triangle and shooting a ray from it normal to the surface against the country boundary control net described above then colouring the terrain triangle with the country ID proves the concept of determining countries from surface points:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW1tOA1yYSJIqlyBcvQbfzfOZdgVbAUmBz1zfR0MBVoqaia80Hwk1nubIloMZYuRA6UDgVDo_bZEDsJJBhMKHYstupA1cffZD2JjkB7tl5i6DcrPjyOEuT-L2KX6-NkWFQX8bB8K9xgq4/s1600/02_Triangles_Patches.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhW1tOA1yYSJIqlyBcvQbfzfOZdgVbAUmBz1zfR0MBVoqaia80Hwk1nubIloMZYuRA6UDgVDo_bZEDsJJBhMKHYstupA1cffZD2JjkB7tl5i6DcrPjyOEuT-L2KX6-NkWFQX8bB8K9xgq4/s1600/02_Triangles_Patches.jpg" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Countries found by ray-casting from the surface</i></td></tr>
</tbody></table>
Note the jagged edges of the triangles here (shown in close up in the top corner) in contrast to the smooth ones in the direct rendering of the control net above - remember this second image shows the individual terrain triangles flat shaded, effectively point sampling the country control net.<br />
<br />
Unless you know of a planet with purely triangular countries though it's not particularly realistic. To make the results a bit more organic I played with adding some noise to the vector shot from the surface against the control net which initially produced some slightly more encouraging results:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht7YGFF8m1IhbWOy9aJcTJPwvSVTye3Ps9yT9n31iAyekLpLh6gdUQNY_1K27fFqRgUxOnuj5ZpQzOcFz9qNdMtYJOIZslVHeexP4QaHa7epcT_pR9I7aAijQNEJz-W4XMxf9tiVZdkWI/s1600/03_Triangles_Noise_Patches.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht7YGFF8m1IhbWOy9aJcTJPwvSVTye3Ps9yT9n31iAyekLpLh6gdUQNY_1K27fFqRgUxOnuj5ZpQzOcFz9qNdMtYJOIZslVHeexP4QaHa7epcT_pR9I7aAijQNEJz-W4XMxf9tiVZdkWI/s1600/03_Triangles_Noise_Patches.jpg" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Perturbing the ray from the surface</i></td></tr>
</tbody></table>
The triangular nature of the control net however is still quite apparent and more problematically closer examination reveals there are problem cases where the noise causes unrealistic "bubbles" of a country to leak into adjacent countries due to the perturbation of the vectors being intersected against the control net. This occurs most near the country boundaries as points on one side can end up being displaced enough to end up embedded in the neighbouring country: <br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtqyRh7LdBF54J_Vi3GcdEQL_zZ2C6KPSZX-0ClDL3P8MSVyOszr4OCYplxfhbJNvP7rwl3hhhkO9mb7WCXOwxxE21ODHh69skthVxmhwX-K4gcsDzjI3Dhp3s7B7FFLwV2xEhcjsjoZk/s1600/04_Bubbles.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtqyRh7LdBF54J_Vi3GcdEQL_zZ2C6KPSZX-0ClDL3P8MSVyOszr4OCYplxfhbJNvP7rwl3hhhkO9mb7WCXOwxxE21ODHh69skthVxmhwX-K4gcsDzjI3Dhp3s7B7FFLwV2xEhcjsjoZk/s1600/04_Bubbles.jpg" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>"Bubbles" of one country leaking into it's neighbour</i></td></tr>
</tbody></table>
This is not something you generally see in the real world and was what I felt to be an unacceptable results which unfortunately I couldn't think of a way to correct. I ruled out noise perturbation as a result and went back to the drawing board.<br />
<br />
<h3>
Edge Subdivision </h3>
<br />
Instead of noise then I had to come up with plan B - my second approach was to subdivide the edges of the triangles adding displacements to the introduced points to make the edges more interesting. In this way the boundaries can be more interesting while still preventing "bubble" errors. The process here is to recursively divide any boundary edge over some minimum length introducing a new boundary point in the middle of it. The cross product of the edge vector and the normal gives a tangent which is used to displace this new point by some random amount. The magnitude of this displacement is capped to be a proportion of the length of the edge so long edges have their midpoint displaced further than short edges to keep things in proportion.<br />
<br />
Subdividing the edges did turn my nice triangles into n-gons so I also had to add in a triangulation step to turn each set of country boundary points back into a mesh I could both ray test against and render for debugging purposes. As I knew the boundaries couldn't have holes I used the trusty <a href="http://en.wikipedia.org/wiki/Polygon_triangulation#Ear_clipping_method" target="_blank">Ear Clipping Algorithm</a> as it's so simple to implement and fast enough for this case.<br />
<br />
There is still a potential for error here though as there is a possibility that the displaced midpoint might end up within an adjacent country's boundary causing the boundary edges to intersect creating invalid boundary pockets:<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSKRrXzK62I5uELrPEHvHJYP-7CEPLwnsJmLyeC4E6ZE2tDeR77-10A09nXU371vGr6MhnsPsJyOdErTxFDrRBQUnQVgbyoGXWGoaPArBrseXGK_kDAHG1q13s1dQ0rf7dX3cFNWgrkI0/s1600/06_SubdividedEdge.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSKRrXzK62I5uELrPEHvHJYP-7CEPLwnsJmLyeC4E6ZE2tDeR77-10A09nXU371vGr6MhnsPsJyOdErTxFDrRBQUnQVgbyoGXWGoaPArBrseXGK_kDAHG1q13s1dQ0rf7dX3cFNWgrkI0/s1600/06_SubdividedEdge.png" height="205" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>An edge between two countries that has been subdivided</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUI9_MqYYph_oSWuySZfjh6aZ7yy7PlLeToZYiMBZxP_fXFC4x4u5bIfEvVHSjLNOkz_GS29R4uRhCeMsIsrC1mV2thHntNxmiFl9Tat5o1mcolA4JAtLbYMWnOnEBlPs9CVywoSnbf90/s1600/06_SubdividedEdgeGood.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUI9_MqYYph_oSWuySZfjh6aZ7yy7PlLeToZYiMBZxP_fXFC4x4u5bIfEvVHSjLNOkz_GS29R4uRhCeMsIsrC1mV2thHntNxmiFl9Tat5o1mcolA4JAtLbYMWnOnEBlPs9CVywoSnbf90/s1600/06_SubdividedEdgeGood.png" height="206" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Displacing the newly introduced midpoint perpendicularly to the edge by a distance proportional to the edge's length to make the boundary more interesting...</i></td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk2B20LS8JJXKPdcE48vMcA7haKN9UqjK1OacRRS8wIsUhYbtzTbtT22XTXiF6EEpHcP-VsJ_4lgDNqVHS6ctZ3e4dXwvbvc1Y3Vm8-u_wgRteiMbzSwh-lCYY2AsoVAKEZjUOU_689is/s1600/06_SubdividedEdgeBad.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhk2B20LS8JJXKPdcE48vMcA7haKN9UqjK1OacRRS8wIsUhYbtzTbtT22XTXiF6EEpHcP-VsJ_4lgDNqVHS6ctZ3e4dXwvbvc1Y3Vm8-u_wgRteiMbzSwh-lCYY2AsoVAKEZjUOU_689is/s1600/06_SubdividedEdgeBad.png" height="199" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>...but if you displace it too far the new edges can intersect the other existing edges of one or other boundary making an invalid boundary</i></td></tr>
</tbody></table>
So for each potentially displaced midpoint the two edges that would connect it to the original edge's end points are tested for intersecting any of the remaining edges of the two boundaries that adjoin across the edge being split.<br />
<br />
If no intersection is found the displaced point is valid and I move on testing the two created edges to see if they in turn can be sub-divided, if however either of the two new edges intersect a remaining edge I have to pick a new position for the midpoint and try again. To reduce the chance of getting stuck trying to find a valid position each time I have to reposition it I reduce the maximum magnitude of the displacement with each attempt so the new midpoint gradually tends back to the original edge. If the magnitude gets so low it's not worth dividing the edge at all I just give up, leave the original edge undivided and move on to the next edge. This only tends to happen in highly acute boundary corners where slivers are produced.<br />
<br />
Running the process dividing any edge longer than 50Km gives a noticeably better result than the simple triangles I started with:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ3JcGrJ5AhU8QmsAIeKMsqB5_oof_s0Fgezk9b_WRevJBtIb2ACm5MnR5wRZHK4K7A2MXC0DqZb9XB0YF269R2oNVhLx0HP6sWGFUZ_dhchPNxC2RCZ9mmgzP4U5lcTEFNbgU9df36gQ/s1600/05_SubdividedTris.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJ3JcGrJ5AhU8QmsAIeKMsqB5_oof_s0Fgezk9b_WRevJBtIb2ACm5MnR5wRZHK4K7A2MXC0DqZb9XB0YF269R2oNVhLx0HP6sWGFUZ_dhchPNxC2RCZ9mmgzP4U5lcTEFNbgU9df36gQ/s1600/05_SubdividedTris.jpg" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Country control net produced by subdividing edges of triangular countries</i></td></tr>
</tbody></table>
Although it's better there is no getting away from the fact that I started with triangles and I was still not particularly happy with it. To make the country shapes more interesting my next step was to randomly take pairs of adjoining countries and merge them together producing fewer hopefully more interesting shapes:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkHfbWJ7m1XEc7bRDm_N8-Ekb22Y4Cujm6tauRoIP0wEZ5tvuJXQbHJEwxKj4Qw9I7cVT6Lblbf3zvvMVXP88sX3t5D8iu1eftsKU6A6cPpT8bACUm7EP28ylNecfqDnwZEZI6p_9r7cI/s1600/06_MergedTris.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjkHfbWJ7m1XEc7bRDm_N8-Ekb22Y4Cujm6tauRoIP0wEZ5tvuJXQbHJEwxKj4Qw9I7cVT6Lblbf3zvvMVXP88sX3t5D8iu1eftsKU6A6cPpT8bACUm7EP28ylNecfqDnwZEZI6p_9r7cI/s1600/06_MergedTris.png" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Randomly merging adjacent countries helps obscure their triangular origins somewhat</i></td></tr>
</tbody></table>
Another improvement but again to me it's still quite obvious I started with triangles - there are a large number of nexus points where to my mind an unrealistically high number of countries meet at a single place:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq_HwpluxR7J9ErcWb7-QLo6Jk2orE_9K7qTMmgVbUQvQiNQ6uP56zXhUPmgMP2nZEiZ7Bremi5DDe2Fy_JT-7HTRmop2czwm1k0ytQfjqkWLd7SOkRDH87N2FMtygDBAxQ6Pf71FovG0/s1600/07_NexusPoints.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjq_HwpluxR7J9ErcWb7-QLo6Jk2orE_9K7qTMmgVbUQvQiNQ6uP56zXhUPmgMP2nZEiZ7Bremi5DDe2Fy_JT-7HTRmop2czwm1k0ytQfjqkWLd7SOkRDH87N2FMtygDBAxQ6Pf71FovG0/s1600/07_NexusPoints.png" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Unrealistic nexus points where many countries meet, another consequence of the triangular basis</i></td></tr>
</tbody></table>
I experimented with increasing the number of seed points which meant more smaller triangles to start with but although the effect was improved a bit more it felt more like treating the symptom than a cure.<br />
<br />
<h3>
Dual Meshing</h3>
<br />
Rather than shrink the triangles I thought it would be better to start from a more friendly base so instead of using the triangulation of the convex hull surrounding my initial seed points I instead calculated the <a href="http://en.wikipedia.org/wiki/Dual_graph" target="_blank">dual of this mesh </a>effectively producing faces surrounding each seed point rather than passing through them. This immediately gave me what I thought was a better initial mesh, effectively similar to a 3D Voronoi diagram:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCAV3mqolLPSWSOxEqoAuE3fAe1zRItm9TBE0oW-9fXtV6L56b3jk4hfy4-9zi7wzZuCUJ0gUwkIiidziyCtaSps0oPlODvNXPZDbJHN7xv0HCZKZqTrsUlfFsAMg-KiRmmNpaVZDE248/s1600/08_DualBasis.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCAV3mqolLPSWSOxEqoAuE3fAe1zRItm9TBE0oW-9fXtV6L56b3jk4hfy4-9zi7wzZuCUJ0gUwkIiidziyCtaSps0oPlODvNXPZDbJHN7xv0HCZKZqTrsUlfFsAMg-KiRmmNpaVZDE248/s1600/08_DualBasis.png" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Using the dual of the triangular mesh produces better country shapes as a basis</i></td></tr>
</tbody></table>
Running the edge subdivision on this mesh finally gave me a result I was pretty happy with:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifV3Ce28359DbtqmcYVIoj_-Wrf8F12vIoUVjHCF1vzbZz2d7mSXAkNCZL7ignHy5ch0Maf_hlXL8NfSH2DDhu7PFTHyfACFkSdeiBJszyeEDw9o1ZB69A6IyoinObkzf2WnJVO7ZLv60/s1600/08_DualSubdivided.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEifV3Ce28359DbtqmcYVIoj_-Wrf8F12vIoUVjHCF1vzbZz2d7mSXAkNCZL7ignHy5ch0Maf_hlXL8NfSH2DDhu7PFTHyfACFkSdeiBJszyeEDw9o1ZB69A6IyoinObkzf2WnJVO7ZLv60/s1600/08_DualSubdivided.png" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Subdividing the edges of the new countries makes them more realistic</i></td></tr>
</tbody></table>
I also added back the random merging of adjacent countries to make things more varied:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5fjVANLIALj8ATtjmVugDuu994UcnyZ9npluMu60QVNKhnKzDqDNClLR487qYfQcqb1lf2FvQNeF0R05eDQiFmtXfQ1pMZTdAoe-ZlIqbf3X7ijX2UV9yXlOrJwkghq6sqiYhyW1zJ_s/s1600/08_DualMerged.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5fjVANLIALj8ATtjmVugDuu994UcnyZ9npluMu60QVNKhnKzDqDNClLR487qYfQcqb1lf2FvQNeF0R05eDQiFmtXfQ1pMZTdAoe-ZlIqbf3X7ijX2UV9yXlOrJwkghq6sqiYhyW1zJ_s/s1600/08_DualMerged.png" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Randomly merging adjacent countries provides more variety of scale</td></tr>
</tbody></table>
Note how the irritating nexus points no longer feature, the countries join in what I feel to be a far more realistic manner. The main issue I have with it now though is the countries before merging are a bit too similar in size causing the result to be a bit uniform - I think in the future I'll play around with clustering the seed points somehow instead of having them uniformly random and see how that affects things.<br />
<br />
Note the triangulation of the country boundary control net doesn't necessarily produce a spherical mesh as such as the edges of triangles spanning large areas will at times be much closer to the centre of the sphere than their unit length vertices producing odd looking kinks in the geometry:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSIh6kXrXoXhlG8RGa5oyJr1T9bmXcxqlfhl91PVtLc8E11Kuu5QuxJbSIf83HzH_piG6kWqphE2Hq3zg5z0sjYVExM6OF3frNP-xv_LXIdxL15A08Thi8Zo7gbH4OguigLAGO7AcJ4qg/s1600/09_Kink.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSIh6kXrXoXhlG8RGa5oyJr1T9bmXcxqlfhl91PVtLc8E11Kuu5QuxJbSIf83HzH_piG6kWqphE2Hq3zg5z0sjYVExM6OF3frNP-xv_LXIdxL15A08Thi8Zo7gbH4OguigLAGO7AcJ4qg/s1600/09_Kink.png" height="240" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>An obvious kink in the yellow country caused by the control net's triangles spanning a wide solid angle on the planet</i></td></tr>
</tbody></table>
this isn't a problem however as remember I'm not normally rendering the country boundary geometry directly, I'm just intersecting rays shot from the far denser planet surface mesh against it to determine which country a point is located within.<br />
<br />
<h3>
A Note on Ray-Triangle Intersections</h3>
<br />
While working on this there were a number of times when I had to intersect rays or 3D line segments with triangles. I was using a trusty old intersection routine I've had kicking around for years but found that even with double precision there were times when a ray or line would be reported as missing a triangle that it really should have intersected, usually when falling on an edge between two adjoining triangles. Puzzled by this I did a little research (i.e. consulted Google) and found a better intersection algorithm specifically designed for intersecting with meshes, "<a href="http://jcgt.org/published/0002/01/05/paper.pdf" target="_blank">Watertight Ray/Triangle Intersection</a>"<br />
<br />
Implementing this solved all my problem cases in one fell swoop so it turned out to be a good find, if you are interested in using it do note however that there appears to be a minor error in the code presented in the paper, see near the bottom of <a href="https://anteru.net/2014/08/24/2500/" target="_blank">this blog post</a> for more info.<br />
<br />
<h3>
Conclusion</h3>
<br />
Although this has been focussed on country generation, while working on it I have thought that the system could also be used to produce areas of water for oceans and seas by simply flagging certain boundaries as water zones. This would produce some nicely shaped features while avoiding the problem of parts of a country being underwater if water was placed by a different system. I will need to feed the country boundaries into the elevation production system however for this to work so the water zones and coastlines are given appropriate topology. Something for the future anyway.<br />
<br />
So there you have it, a set of what I feel are quite realistically shaped countries I can use to move on to the next step, possibly placement of towns and cities followed by primary road or rail networks...that sounds like it should be interesting to look into anyway.<br />
<br />John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com3tag:blogger.com,1999:blog-3862349614860735414.post-4827947407167740212014-07-26T22:30:00.001+01:002014-07-26T22:36:28.672+01:00TerraTechThis is just a shout-out to publicise the kickstarter for TerraTech, an outstanding indie game being developed by a group of dedicated developers including a friend of mine:<br />
<div>
<span class="Apple-style-span" style="-webkit-composition-fill-color: rgba(175, 192, 227, 0.230469); -webkit-composition-frame-color: rgba(77, 128, 180, 0.230469); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875);"><br /></span>
<span class="Apple-style-span" style="-webkit-composition-fill-color: rgba(175, 192, 227, 0.230469); -webkit-composition-frame-color: rgba(77, 128, 180, 0.230469); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.296875);"><span class="Apple-style-span" style="-webkit-composition-fill-color: rgba(175, 192, 227, 0.230469); -webkit-composition-frame-color: rgba(77, 128, 180, 0.230469); -webkit-tap-highlight-color: rgba(26, 26, 26, 0.292969);"><a href="https://www.kickstarter.com/projects/payloadstudios/terratech-physics-based-vehicle-construction-and-c" target="_blank">https://www.kickstarter.com/projects/payloadstudios/terratech-physics-based-vehicle-construction-and-c</a></span></span></div>
<div>
<br /></div>
<div>
It only has about a day to go and is 99% there on their funding target, it would be a real shame if this exciting project couldn't go ahead so check it out and support it if you can. There is a free demo on Steam so there is no excuse :-)</div>
John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0tag:blogger.com,1999:blog-3862349614860735414.post-22359733931667513322014-07-13T16:31:00.001+01:002014-07-14T09:08:04.780+01:00NomenclatureI've had to take a bit of a break from project work recently due to other commitments but hopefully I'll be able to get back to at least tinkering with things soon. As a bit of a preamble to that I've been looking at a bit of a fringe task for procedurally generated worlds, namely the procedural generation of names.<br />
<br />
My plan for the near future is to focus more on generating a plausible political and logistical infrastructure for a planet instead of purely working on the visual terrain as I have done many times before so I'm going to need to be able to name things be they continents, countries, regions, cities, airports, premiers or whatever. This is a completely new area to me and so sounded like an interesting diversion.<br />
<br />
There are many ways to procedurally construct names ranging from simply producing random sequences of letters to utilising complex linguistic rules but the one I chose to experiment with was the use of Markov Chains (<a href="http://en.wikipedia.org/wiki/Markov_chain">http://en.wikipedia.org/wiki/Markov_chain</a>), an algorithm that decides what comes next in a sequence by looking only at the preceding X elements and using some probability tables to decide what's most likely to follow. In my case the sequence is the letters of the name being generated so by looking at the last so many characters of the name so far I can choose a plausible next character and so on.<br />
<br />
So where do these probability tables come from? Well to make names that look sensible I thought it best to reference the real world as a starting point, so to generate names for countries I use a set of 275 real world country names as my exemplars - something that's not too hard to come by these days thanks to the internet. These real names are analysed to store the probability that any given sequence of characters is followed by another, for example it may be that 23% of the time the letter 'a' is followed by the letter 't', or that 15% of the time the sequence "tr" is followed by the letter "s" in the real world names.<br />
<br />
The resultant behaviour of the algorithm is heavily dependent upon the size of the exemplar pool but even more significantly upon the number of characters you look at when deciding what might come next. For example running the same algorithm with the same exemplars but different length groups produced the following sets of names:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSVkA_RkeikcAxqch4Z5DH_dpaeTt4ZjG_D8IMM2HhAMoqQNh7k6zJj6nHBfun5xdjDHOo11l5TU01Pp0qXsDWxnqce8pM4BF6X_U7nX0XMCpgRy71bcSL3VaK3jMPbxLE9ePcDJCRfo0/s1600/NameGeneration_1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhSVkA_RkeikcAxqch4Z5DH_dpaeTt4ZjG_D8IMM2HhAMoqQNh7k6zJj6nHBfun5xdjDHOo11l5TU01Pp0qXsDWxnqce8pM4BF6X_U7nX0XMCpgRy71bcSL3VaK3jMPbxLE9ePcDJCRfo0/s1600/NameGeneration_1.png" height="450" width="640" /></a></div>
<br />
It's immediately obvious that the longer the character sequence examined the more rigidly the generated names stick to the exemplars while the shorter the character sequence the more variety the generated names exhibit. The extreme example here is table (1d) where only the last character emitted is used to decide what comes next resulting in a virtually random sequence of characters that bears little resemblance to the exemplars or even anything vaguely pronounceable!<br />
<br />
Of the variations I think the three character sequence version (1b) is most promising but while the longer sequence length prevents too much randomness in the output at times there simply isn't <i>enough</i> randomness meaning that the generated names are quite similar or even identical to the real world exemplars. There is also nothing preventing the same sequence being generated more than once such as "falklands uk".<br />
<br />
To address the first of these issues I decided to run a check on
each generated name to see if it was different enough to the exemplars to be
accepted. This is done by comparing the
generated name to each exemplar and calculating the Levenshtein Distance between
the two strings (<a href="http://en.wikipedia.org/wiki/Levenshtein_distance">http://en.wikipedia.org/wiki/Levenshtein_distance</a>),
essentially the minimum number of single-character edits (insertions,
deletions or substitutions) required to change one word into the other.<br />
<div>
<div class="MsoNormal">
<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
By rejecting any generated names that produced a Levenshtein
Distance to any exemplar that was too low we can help ensure that we don’t
accidentally end up with something too similar to a real world place name. Running the same test again but now with this added exemplar distance check rejecting any generated names with an exemplar distance score of less than two we get a better
selection of plausible but not actually real world names:<o:p></o:p></div>
<div class="MsoNormal">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRNLEZ3dTwecuz4kJV814XxFYlhavDAXxZdpAgLxHOzpCc2ljGhz5CktSy_nA7C2PG9gHgSP2AWIsRuRb1wXIMDJ_3DBnGKT3qXPXAeOzTmUcGN1P4U1qQigN_bI58H7a-Lg2dx8KTFfA/s1600/NameGeneration_2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRNLEZ3dTwecuz4kJV814XxFYlhavDAXxZdpAgLxHOzpCc2ljGhz5CktSy_nA7C2PG9gHgSP2AWIsRuRb1wXIMDJ_3DBnGKT3qXPXAeOzTmUcGN1P4U1qQigN_bI58H7a-Lg2dx8KTFfA/s1600/NameGeneration_2.png" height="452" width="640" /></a></div>
<div class="MsoNormal">
<br /></div>
<div class="MsoNormal">
Note here that the distance check is performed on each separate word of the generated and exemplar names separately to avoid generating names where one word is unique but the other lifted straight from an exemplar - certain key words such as "the", "of" or "island" are exempted from this check however as they are perfectly valid in the output.<br />
<br />
As an experiment, running the test one final time but this time with the distance rejection limit raised from less than two to less than five produced these country names:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6kyVmwDW7de4nQNRhHPpWYXvsm-najAn44s5op-quVyxyNNynY999Wif8dr6-7lSBFYzTJtyjF6-ioPTeh6GwGvypYTjBseG82QwAUqY4pQ77CqkGkFE1Fx4555yVoD5oAHmmTheHn74/s1600/NameGeneration_3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6kyVmwDW7de4nQNRhHPpWYXvsm-najAn44s5op-quVyxyNNynY999Wif8dr6-7lSBFYzTJtyjF6-ioPTeh6GwGvypYTjBseG82QwAUqY4pQ77CqkGkFE1Fx4555yVoD5oAHmmTheHn74/s1600/NameGeneration_3.png" height="334" width="640" /></a></div>
<br />
Comparing these to the previous test's output the biggest change here is with the longer character sequence generated names (3b and to a lesser extent 3c) as the distance check has rejected anything even remotely similar to one of the exemplars causing far more random and less recognisable names. The shorter sequence names at the bottom have been less affected as they were pretty random to start with. Note there is no four character sequence output here as the system was unable to generate any names using that most restrictive of criteria that also passed the distance check - the names were by definition most similar to the exemplars therefore least likely to have an acceptably high distance score.<br />
<br />
So far these have all been country names but how does the system work with a different set of exemplars? To find out I used it to try to generate plausible city names instead - first using a full set of 26914 Earth city names with a three character sequence length and a distance threshold of three:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMarxzJ0vxqUerdGIfcaRr0MAY-HzGK7pQPiwoMjC3pac19i7ob36HNCfTjHD6Yqx9vf63CsBLJuSfeVGg8MYqBNG32PAIEQoe4Fd9TnU7iykQ5LJCSfzPshGZ9ArW54-woDF2FAed7rA/s1600/NameGeneration_4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMarxzJ0vxqUerdGIfcaRr0MAY-HzGK7pQPiwoMjC3pac19i7ob36HNCfTjHD6Yqx9vf63CsBLJuSfeVGg8MYqBNG32PAIEQoe4Fd9TnU7iykQ5LJCSfzPshGZ9ArW54-woDF2FAed7rA/s1600/NameGeneration_4.png" height="104" width="640" /></a></div>
<br />
As you can see there is a lot of variety in the names reflecting the huge real world variety of city names depending on culture and language, as one of my main goals at the moment is to produce a procedural world that exhibits more heterogeneity than it typically found by global application of procedural systems however this one-size-fits-all name style is not really what I'm after.<br />
<br />
By limiting the set of exemplars to those real world cities from a particular country however more localised results can be achieved. Out of interest I ran the system over just those real world city names from the United Kingdom, United States, France and Japan in turn to see how closely the output would reflect their dramatically contrasting morphology:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwE-9_uqbAINNL5gOA9tZ1GYDNHKnxs-6B8Zl0a9itkKJdQdHpoYv1LcncAAKJCJnI8TMoWsJ84LVQrDO0HPAOsXv203v62_-fkwK3OQG6L9x6JQjoDbs6HTKl9SgKvzJKMglMcS1FiAM/s1600/NameGeneration_5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwE-9_uqbAINNL5gOA9tZ1GYDNHKnxs-6B8Zl0a9itkKJdQdHpoYv1LcncAAKJCJnI8TMoWsJ84LVQrDO0HPAOsXv203v62_-fkwK3OQG6L9x6JQjoDbs6HTKl9SgKvzJKMglMcS1FiAM/s1600/NameGeneration_5.png" height="450" width="640" /></a></div>
<br />
I'm no language expert but to my layman eyes the output from each while varied amongst it's peers is noticeably different from those in the other tables which I think is good enough for my needs - I just want each of my virtual countries to have an identity reflected in it's nomenclature.<br />
<br />
These tables have been generated from just a few runs and with specific parameters, but it's important to bear in mind that when used for real the exemplar pool, sequence length and the distance threshold can all be varied each time a name is needed providing a steerable but near limitless potential set of generated names.<br />
<br /></div>
<div class="MsoNormal">
Anyway,
I’m happy enough with it for now so I’m going to move on to something
else...comments as always are welcome</div>
<div class="MsoNormal">
<o:p></o:p></div>
<br />
<div class="MsoNormal">
<br /></div>
</div>
<div>
<br /></div>
John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com4tag:blogger.com,1999:blog-3862349614860735414.post-85046011604703517892013-09-15T14:33:00.000+01:002013-09-15T14:33:00.259+01:00The end of an era...It's with great sadness that I have to announce the company I have greatly enjoyed working for over the last eighteen years has had to close:<br />
<br />
<a href="http://www.develop-online.net/news/45340/Blitz-Games-Studios-closes">http://www.develop-online.net/news/45340/Blitz-Games-Studios-closes</a><br />
<br />
I think it impossible to overstate just what a great group of tremendously skilled people we had there producing a huge variety of projects on many diverse platforms. The games industry is an unforgiving place however and a whole raft of factors conspired against us over the last year or two making it impossible to continue.<br />
<br />
I wish everyone from there the very best of luck finding something new and exciting to work on.John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com3tag:blogger.com,1999:blog-3862349614860735414.post-71838324434896219842013-07-11T22:12:00.000+01:002013-07-11T22:17:38.834+01:00Damn, Voxel Data is BIGI left off the last post with a little question about how many voxels I might need to represent a planet. There were a few guesses of <a href="http://johnwhigham.blogspot.com/2013/06/goodbye-octrees.html?showComment=1372172944596#c3802404282383962595" target="_blank">4.7810528e+20</a>, <a href="http://johnwhigham.blogspot.com/2013/06/goodbye-octrees.html?showComment=1372173229936#c7550988150103801861" target="_blank">9.8782084e+17</a> and <a href="http://johnwhigham.blogspot.co.uk/2013/06/goodbye-octrees.html?showComment=1372810807230#c4497482537376023830" target="_blank">10^21</a> which at first glance seem absurdly large but in fact they were pretty close to the mark. You see the thing about voxel data is it's big. Very big.<br />
<br />
Apart from the inherent inefficiencies of using GPUs to render voxels with raycasting when they would generally be much happier doing what they were designed for and be rasterising triangles, it's the amount of memory voxel data sets consume that prohibit their use in many scenarios and while that's pretty much accepted wisdom and hardly newsworthy - I thought it might be interesting to just take a moment to put out there some cold hard numbers.<br />
<br />
So, how many voxels and therefore of course bytes of storage <i>do</i> you need to render a planet? The answer as you would expect depends on the fidelity of your representation and your method of storage, but these are my numbers what what they're worth:<br />
<br />
To encompass a maximum data set of 25,000 Km cubed I am using 22 levels of detail each offering eight times the number of voxels of the previous one (i.e. twice the number along each axis). The lowest detail level uses a 19^3 grid of voxel bricks each of which is 14^3 voxels in size so the entire lowest detail level provides 266^3 voxels. Dividing the 25,000 Km region by this equates to each voxel encompassing a cubic region of space 93.98 Km on a side (830,185 Km^3). That's pretty big!<br />
<br />
So obviously the lowest level of detail is pretty coarse but even at that low detail level there are 266*266*266 voxels making up the space which equates to 18,821,096 individual voxels. So that's nearly <b>Twenty Million</b> voxels for just one very crude detail level!<br />
<br />
The next level down gives us 532^3 voxels each 46.99 Km on a side so we end up with over 150 Million of them there - things are scaling up pretty quickly. Continuing this down to the maximum detail 22nd level gives us the following results:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKYL33DLPABLTH-wW0y3zyz2j1nahWiWX_ajzyEBWQ7L0sZrm1HmZLSg5XwI6XYs_3dNIJNc0qHCs6Y5NFnx0-bN4hj8PyyuXLdKI-wFe0kZDI9Y49p-oioq3Khk-T0J8a00jH_DSI6rw/s648/DataSetSizes1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="247" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKYL33DLPABLTH-wW0y3zyz2j1nahWiWX_ajzyEBWQ7L0sZrm1HmZLSg5XwI6XYs_3dNIJNc0qHCs6Y5NFnx0-bN4hj8PyyuXLdKI-wFe0kZDI9Y49p-oioq3Khk-T0J8a00jH_DSI6rw/s320/DataSetSizes1.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Voxel counts, sizes and storage requirements at each level of detail</i></td></tr>
</tbody></table>
As you can see the numbers get silly very quickly, and this is storing just a single distance field value per voxel in half precision floating point (i.e. two bytes per voxel) without any thought to storing normals or occlusion for lighting or any kind of texturing information<br />
<br />
So to put it another way, storing the full 25,000 Km sized cube of space at a maximum detail level of 4.5 cm per voxel would take 173,593,970,549,359,000,000,000,000 (173 million billion billion) voxels taking a mind boggling 294 Zetabytes of storage just for the distance field! Putting that astronomical number into real world terms, if you burnt it all to dual layer DVD disks and stacked them on top of each other the pile would be nearly five times as high as the distance from the Earth to the edge of the Solar System!<br />
<br />
I know the cost of hard drive storage continues to decrease but I'm pretty sure I can neither afford that sort of storage nor fit it in my PC!<br />
<br />
Let's think about it a bit more though, firstly 25,000 Km is the maximum size my system can store - let's for the sake of argument say that I'm only interested in storing something Earth sized for now. It's not a perfect sphere but The Earth has a generally agreed approximate radius of 6371 Km which when plugged in to my formula reduces the numbers quite substantially:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhh-PHddBbDpqrnGs33MMdzMd3qPOT54PpiORpLxCa-DPRbMzZJFRF4b_cHjlt9Y3crFmn3Ts8XH6nXZex_FAN7j1I987DL2Z9AghZZ111Xyd9m94pYk5WaYt9uE1UY-3VOoqp811Pf8y4/s1600/DataSetSizes2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhh-PHddBbDpqrnGs33MMdzMd3qPOT54PpiORpLxCa-DPRbMzZJFRF4b_cHjlt9Y3crFmn3Ts8XH6nXZex_FAN7j1I987DL2Z9AghZZ111Xyd9m94pYk5WaYt9uE1UY-3VOoqp811Pf8y4/s320/DataSetSizes2.png" width="267" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Same voxel statistics but for an Earth sized cube of space</i></td></tr>
</tbody></table>
Which while better is still pretty extreme at a cool 39 Zetabytes - but let's keep going anyway. These numbers are for the entire cubic space but planets are conventionally round so with the volume of a sphere being (4/3)*PI*(Radius^3) our Earth sized planet has a volume of 1,083,206,916,845 KM^3, just 52.4% of the entire cube volume.<br />
<br />
Before proceeding further lets define the planet a bit more accurately though; while they are basically spheres a perfectly smooth spherical surface isn't very interesting so we need some amount of surface detail. Conversely though we don't typically want to be able to travel to the planet's core so data below a certain depth underground probably isn't needed. Combine these two and you end up with a spherical shell of some determined thickness that defines the area of interest for rendering. My chosen thickness is currently 18Km (about 59,000 feet) which is approximately twice the height of Everest; I am anticipating that having roughly 10 Km available for the tallest mountains and 8 Km available for the deepest caves or underwater abysses ought to be sufficient<br />
<br />
Making this assumption that we are only interested in a hollow shell of data is a great optimisation both of storage and rendering performance because instead of tracing the ray through the entire cubic data set we can first intersect it with the outer sphere of the planet's data shell and start tracing from there removing the need to store a great many of the distance samples. Assuming there aren't any holes in the shell you can also ignore any space inside the shell as you're bound to hit something solid before getting there.<br />
<br />
Some basic calculations shows that for an Earth sized planet an 18 Km thick shell takes up just 0.23% of the planet's bounding cube and has a volume of about 9 billion Km^3 requiring something like 1.017E+23 voxels taking 172 ZB at two bytes per voxel. Damn, that's still pretty large.<br />
<br />
This estimation is pretty crude as it doesn't take account of the topology of the actual terrain which will allow many more bricks to be discarded and there are basic storage optimisations such as compressing the brick data but no matter which way you look at it you simply can't store brick data for a whole planet at this kind of detail.<br />
<br />
There is also a major downside to all these assumptions of course - we are restricted to sensible planet shapes with sensible features which is a shame as being able to create crazy shaped planets you can travel through the core of sounds like a lot of fun. Even though my efforts at the moment are on conventional planet shapes therefore I'm taking care to make sure there are no artificial restrictions in place that would preclude more radical geology. These limitations are simply optimisations to make the data set more manageable and easier to generate (i.e. faster) while I develop the system, pushing the boundaries of what's possible is a large part of this project's raison d'être.<br />
<br />
Although all these crazy numbers make this project sound like an exercise in futility, remember that these are for the highest 4.5cm per voxel detail level. Each level lower takes only 1/8th the amount and of course you only need high detail data for the immediate vicinity of the viewpoint; the key therefore is to have a system that can generate brick data on demand for any given position in the clipmap hierarchy. Combine this with a multi-tier memory and disk caching strategy and you get something usable.<br />
<br />
Remember also that one of the benefits of clipmaps is that the memory overhead for rendering is fixed regardless of detail level. I support ten renderable levels from the set of 22 so it makes no difference whether I'm rendering levels 0 to 9 or level 12 to 21 the overhead is the same.<br />
<br />
Finally, you're maybe wondering why bother to store the bricks at all when there are some pretty cool projects out there showing what can be achieved with noise based functions directly on the GPU such as <a href="https://www.shadertoy.com/view/XsX3RB" target="_blank">iq's Volcanic</a>. The main reason I'm sticking with them is that I want to be able to add all sorts of voxely infrastructure onto my planets such as roads, cities and bridges which are hard to do dynamically in real-time but also because I want to experiment more with creating truly heterogeneous terrains rather than the fairly homogeneous looking constructs real time noise tends to favour. That's the goal anyway.<br />
<br />
I realise this has been a pretty dry number-heavy post so +1 if you're still with me, hopefully the next one will be a bit more visual.John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com4tag:blogger.com,1999:blog-3862349614860735414.post-25778715546742946192013-07-03T14:17:00.000+01:002013-07-03T14:17:20.958+01:00Interplay of LightThis is just a quick shout-out for the blog of a colleague here at Blitz Games Studios:<br />
<br />
<a href="http://interplayoflight.wordpress.com/" target="_blank">"Interplay of Light"</a> is the work of talented graphics programmer Kostas Anagnostou and is a recommended addition to the feed list of anyone interested in the field.John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0tag:blogger.com,1999:blog-3862349614860735414.post-49245400664898926522013-06-18T21:49:00.000+01:002013-06-18T21:49:29.209+01:00Goodbye OctreesIt's been a while since I posted an update on the Voxelus project but it has been advancing slowly as time allows. As with most hobby projects there never seems to be enough time but it's certainly not defunct and I enjoy fiddling with it when I can pull myself away from the <a href="https://kerbalspaceprogram.com/" target="_blank">Kerbal Space Program</a>!<br />
<br />
<span style="font-size: large;"><b>Framework Switch</b></span><br />
<br />
Most of the changes however are fairly invisible from the outside, one of the largest for example was changing C++ framework from the commercial one that I use and help develop at work to an entirely new one of my own creation. Although a considerable amount of work, I was finding the constantly moving goalposts of using a framework being actively developed for other purposes frustrating, as was the ever increasing complexity it brought.<br />
<br />
By creating my own framework I am now entirely in control and can create one that works exactly as I want and is only as complex as necessary to achieve my goals. Another benefit is that because it's now 100% my own code should I ever wish to distribute source for any of my projects I can do so freely without worrying about copyright or IP ownership issues.<br />
<br />
<span style="font-size: large;"><b>Goodbye Octrees</b></span><br />
<br />
Apart from changes to the underlying code structure, the biggest change to the project and definitely the largest consumer of development effort is my move away from sparse voxel octrees. I had started out using SVOs as they have been receiving quite a lot of coverage recently and they felt like a good way to experiment with raycasting voxels. While they work well for relatively small bounded areas I was having trouble working out a way to make them scale sufficiently to encompass an entire planet - my stated goal for this project.<br />
<br />
The main problem with the SVO approach was the number of levels of data necessary to represent a planet to a sufficient fidelity. Using some basic arithmetic I worked out that I would need 22 levels of voxel data to represent a cube of space 25,000 km on a side (roughly twice the size of the Earth) down to a resolution of 4.5 cm per voxel which I felt would be sufficient for walking around on the surface. Ignoring the issues of storage, having to recurse down up to 22 levels of octree every step along every pixel ray reading index and data textures at every iteration felt like asking too much of the GPU.<br />
<br />
The problem can be simplified somewhat by restricting the number of levels actually being used in any given frame to a sensible range based on the distance of the viewpoint from the surface - my guesstimate at the moment is using ten of the 22 levels - but you either need to still recurse down from the root to get to the first of the ten levels of interest or you need some sort of strategy allowing you to jump in to the octree structure at any given level and point in space. Neither of these felt like problems I wanted to tackle.<br />
<br />
I decided instead to ditch the octree structure altogether and move instead to a clipmap based system. I had used clipmaps previously on other terrain based projects for rendering heightfields where they provide a relatively simple and effective solution for storing, streaming and rendering multi-resolution data so I thought I would be able to drop them in fairly easily to this current project. Well...sort of.<br />
<br />
<span style="font-size: large;"><b>Clipmap Background</b></span><br />
<br />
Clipmaps have been around for a long time, there is a <a href="http://techpubs.sgi.com/library/tpl/cgi-bin/getdoc.cgi?coll=0650&db=bks&srch=&fname=/SGI_Developer/Perf_PG/sgi_html/ch15.html" target="_blank">SGI paper on them from 2004</a> for example or more recently an <a href="http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter02.html" target="_blank">article on using them for terrain rendering in GPU Gems 2</a>, Miguel Cepero also appears to <a href="http://procworld.blogspot.co.uk/2011/10/clipmaps.html" target="_blank">use them in 3D in his Voxel Farm Engine project</a>. Essentially they provide a way to scroll a potentially infinite data field through a fixed size multi-resolution cache. If you are familiar with mip-maps you can think of the 2D case as being somewhat like a reverse mip-map, instead of each level of data representing the same area of space but at half the resolution, in a clipmap each level of data is the same resolution and instead represents an area of space twice the size of the preceding level.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjV3HaQutJkUPGL9i_JzZeS7xGv-m0f1swg0vnSabPo3pVfGmAtWJLblbj0nl01G1CPf3P2pk6XFuJrNSRFLR-Nu6LnrfYwiDxo6-qwvzgG22Y78Ou_aAKC2M_pqizyxChDxYy4kvYEKXE/s1600/ClipMaps2D.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjV3HaQutJkUPGL9i_JzZeS7xGv-m0f1swg0vnSabPo3pVfGmAtWJLblbj0nl01G1CPf3P2pk6XFuJrNSRFLR-Nu6LnrfYwiDxo6-qwvzgG22Y78Ou_aAKC2M_pqizyxChDxYy4kvYEKXE/s1600/ClipMaps2D.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Three levels of a 4x4 clipmap illustrating how each level is the same resolution but covers four times the area of the preceding level at quarter the data density</i></td></tr>
</tbody></table>
So for example, if the first level of the clipmap stores a 1 km square of terrain in a 512x512 grid, the second level would store a 2 km square also in a 512x512 grid while the third level would store a 4 km square once again in a 512x512 grid. It can be easily seen here that each level is storing four times the area of the preceding one, a geometric progression that allows huge areas to be covered with a relatively low number of levels.<br />
<br />
When rendering with clipmaps you centre each level around the viewpoint so the highest fidelity data from the first level represents the terrain closest to the camera, the next level the data a little further away and so on. As perspective is reducing the size of features on-screen anyway the drop in resolution with distance provides a natural level of detail scheme.<br />
<br />
A key trick for efficiency when moving clipmaps around your data set is to think of the mapping of terrain to the clipmap buffers in memory as toroidal so instead of having to copy all the data around as the viewpoint moves you simply write the new data that's just moved into range over the old data that's just moved out of range. In this way the minimum amount of memory is rewritten and because the clipmaps never actually change size or move away from the viewpoint you don't get any numerical precision issues during rendering facilitating an effectively infinite data set.<br />
<br />
For a system like Voxelus where creating or loading the data for a region can take a number of frames and it's possible to move the viewpoint rapidly around you do get inevitable flicker if the data for the new region can't be copied in fast enough but unnecessary flickering can be minimised by keeping a border around the edge of the clipmap that isn't normally rendered. When the viewpoint moves at a more measured pace this extra data gives you something to render while the new data is paged in. Of course the faster your viewpoint moves the wider this band of pre-prepared data must be to avoid flickering so it's a trade-off of memory versus visual artifacts.<br />
<br />
<span style="font-size: large;"><b>Adding Dimensions...</b></span><br />
<br />
As I said I had <a href="http://jwhigham.wordpress.com/tag/isis/" target="_blank">used 2D clipmaps before in a fairly traditional heightfield</a> renderer but to use them for rendering a volumetric planet it would appear that I had to extend them to work in 3D, but as I was looking to render a set of ten levels from my full planet sized 22 level data set I actually had to implement a four dimensional clipmap arrangement as the renderable data set scrolls not just in the X, Y and Z spacial axes but also along the W axis representing which level of data to use as the viewpoint moves closer to and further away from the surface.<br />
<br />
This isn't conceptually that much more difficult, but it did give me some head scratching moments trying to work out the shape of dirty data regions as the viewpoint moved around!<br />
<br />
It's well known that voxels can produce vast amounts of data which can be difficult to manage efficiently in a real-time environment, trying to manage them individually makes this even worse so although I dropped the octree data structure I decided to keep the brick concept from the earlier version. In my currently implementation each of the ten clipmap levels stores a 19^3 grid of bricks each of which is a 14^3 array of voxels.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4j4_WmHGgjWEeVnndcG4w8tltngx7ulTkGyVw382jU2w7ZmGHGYjVqrM3DaLNOg5fLp6EG8rC-bHB69WrRjjYp8RnjtyOAAoElKhEmbgSEjd4_Q1m0_uWN4mCxSww6Ni2juENcNOxsCI/s1600/MapLevelBounds.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4j4_WmHGgjWEeVnndcG4w8tltngx7ulTkGyVw382jU2w7ZmGHGYjVqrM3DaLNOg5fLp6EG8rC-bHB69WrRjjYp8RnjtyOAAoElKhEmbgSEjd4_Q1m0_uWN4mCxSww6Ni2juENcNOxsCI/s320/MapLevelBounds.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Wireframe bounds of each of the 10 clipmap levels when the viewpoint is located close to the planet surface on the left hand side</i></td></tr>
</tbody></table>
As can be seen here, each level encloses 1/8th of the volume of the preceding level, normally they are centred around the viewpoint but that makes them hard to visualise so for the purposes of this screenshot I moved the viewpoint close to the surface on the left hand side of the planet then locked their positions before flying back out for illustrative purposes.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvHGXnQgZq34ADiwF2Q1Ll6I_86MH3HWPwnA-Fns8_VLC0Nm_0i-7qPlqN_XlP9fEtVY43UUiqIpYhBbRCMUv6SeNDPzy__-H4zjWxFxq_ph9yNo2ZLVABEatri2HJ2ATA-yei520Mojg/s1600/MapLevelColours.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvHGXnQgZq34ADiwF2Q1Ll6I_86MH3HWPwnA-Fns8_VLC0Nm_0i-7qPlqN_XlP9fEtVY43UUiqIpYhBbRCMUv6SeNDPzy__-H4zjWxFxq_ph9yNo2ZLVABEatri2HJ2ATA-yei520Mojg/s320/MapLevelColours.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Clipmap levels coloured by level</i></td></tr>
</tbody></table>
This second image shows the view from the original camera position near the surface, but here the voxels from each clipmap level are being rendered in a different colour. Note the smooth blending between voxels of different levels achieved by sampling adjacent levels and blending the distance values. This is one of the main benefits of raycasting voxels rather than triangulating them - seamless per-pixel level of detail blending - which I plan to expand on in another post.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixhSwBkX9lpiwoVJ8uDJqECqAva7nEAUkUVYM2EdACmqQ6Oh9anjkgQKIUQUGxWA5RaZyynFq7nZwOLwGyMAjOhRMhMB_yegPjfmdYQ3kpvUbKXRDb23Ugha19RikPyfEeqHMuo4FILxE/s1600/MapLevelBricks.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEixhSwBkX9lpiwoVJ8uDJqECqAva7nEAUkUVYM2EdACmqQ6Oh9anjkgQKIUQUGxWA5RaZyynFq7nZwOLwGyMAjOhRMhMB_yegPjfmdYQ3kpvUbKXRDb23Ugha19RikPyfEeqHMuo4FILxE/s320/MapLevelBricks.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Voxel brick visualisation</i></td></tr>
</tbody></table>
Finally this image shows the same style of colour coded level of detail view but also illustrates the size of the voxel bricks. Each of those patches is a 14^3 brick of voxels which is the smallest unit of data I generate, store and transfer. Only the higher detail brick size is shown here so each pixel is in fact sampling from the illustrated brick size and the equivalent one from the next level down (i.e. double the width, height and depth). Note how the bricks get larger automatically with distance - a simple metric of pixel size over distance is used to pick the best pair of levels at each point along each pixel's ray.<br />
<br />
As the viewpoint moves bricks are pulled from a CPU side cache and copied into the dirty regions. By transferring voxel data around in brick sized units it's possible to use the hardware far more efficiently than trying to do so individually.<br />
<br />
<span style="font-size: large;"><b>The Devil's in the Details</b></span><br />
<br />
As anyone who's ever tried to implement complicated real time graphics will know even conceptually simple systems can end up taking plenty of effort to get working well, but to help stave off <b>TL:DR</b> syndrome I'll save further detail for another post.<br />
<br />
Until then, anyone care to guess how many voxels I'll need to make up a planet?John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com5tag:blogger.com,1999:blog-3862349614860735414.post-8045659220402228772013-02-16T10:39:00.000+00:002013-02-16T10:39:15.869+00:00UNiGiNE Valley BenchmarkAlthough it's been a quiet few months as I've not been able to devote much time to my voxel worlds, I thought I'd share a link I found interesting.<br />
<div>
<br /></div>
<div>
The guys over at UNiGiNE have created a new benchmark/demo "Valley" showing a very nice real-time terrain:</div>
<div>
<br /></div>
<div>
<iframe allowfullscreen="" frameborder="0" height="360" src="http://www.youtube.com/embed/-y4bJvFEtHI?feature=player_detailpage" width="640"></iframe></div>
<div>
<br />
They've been a little heavy handed with the depth of field for my liking; something I've noticed on many demos trying to show off this feature but that's just my personal taste. It doesn't really detract from what is still a very impressive demo.<br />
<br />
I would like to know where the heightfield data was sourced from - whether it was a DEM set or created procedurally in an offline tool for example - I doubt it's created real-time as that's not really the point here. They do a good job of making it look nice though.<br />
<br />
Here's their website link: <a href="http://unigine.com/products/valley/" target="_blank">UNiGiNE Valley Benchmark</a></div>
John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0tag:blogger.com,1999:blog-3862349614860735414.post-93408633458561622012-12-06T17:48:00.002+00:002012-12-06T17:48:32.621+00:00Procedural Castle<div class="separator" style="clear: both; text-align: left;">
<br /></div>
I've been taking a break from trying to synthesise natural terrain features and instead been working on a system to create buildings in a procedural manner. I've looked a little at this before in my previous Osiris project (see <a href="http://johnwhigham.blogspot.co.uk/2011/08/building-placement-revisited.html" target="_blank">Building Placement Revisited</a>) but didn't take it much further than producing some roughly textured boxes:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7T5AwVZN1mNpD9-vZfUATjxQA0SQ4xZo847ZAMUV84k3c5iowtuZMqn2hyd5ptVQIdBTZjOJf15HpHVObSdUyzxsOetogSVTS_oxtSrb9_mW9dflb_qYe2ZjDdyBSqjufwuaLDj2ygGU/s1600/Lonposikyo_1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7T5AwVZN1mNpD9-vZfUATjxQA0SQ4xZo847ZAMUV84k3c5iowtuZMqn2hyd5ptVQIdBTZjOJf15HpHVObSdUyzxsOetogSVTS_oxtSrb9_mW9dflb_qYe2ZjDdyBSqjufwuaLDj2ygGU/s320/Lonposikyo_1.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Simple skyscraper buildings produced procedurally</i></td></tr>
</tbody></table>
I thought it was about time I pushed it a bit further to produce structures to make my new voxel worlds a bit more interesting. I've taken the basic system I was developing before but reworked and extended it to make it more powerful and capable of producing more varied output:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC7-3vSIohOnore_QuJA9AiPnhLstUbVuyHeNb-8AH6AtKphv7JN19QsGJ-vxuh9FEb13d9rY6znkni-yaOcE-3W_Fcm_bs9m1Ek5BvDlw4Gm_PRHT0fjdiXfxyz_sAuJgVhrNbZt5xys/s1600/Castle1.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgC7-3vSIohOnore_QuJA9AiPnhLstUbVuyHeNb-8AH6AtKphv7JN19QsGJ-vxuh9FEb13d9rY6znkni-yaOcE-3W_Fcm_bs9m1Ek5BvDlw4Gm_PRHT0fjdiXfxyz_sAuJgVhrNbZt5xys/s400/Castle1.jpg" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Voxel castle produced by the new procedural building system</i></td></tr>
</tbody></table>
It's not exactly going to win any modelling awards but I'm hoping the mechanism behind it's production will give me the power to create all manner of man made structures and buildings reasonably simply.<br />
<br />
The system is inspired primarily by the work of Müller, Wonka, Haegler, Ulmer and Van Gool in their paper <a href="http://peterwonka.net/Publications/mueller.procedural%20modeling%20of%20buildings.SG2006.final-web.pdf" target="_blank">Procedural Modeling of Buildings</a> where they present a grammar based system for the specification of buildings (See <a href="http://procworld.blogspot.co.uk/search/label/Grammar" target="_blank">Miguel Cepero's Procedural World blog</a> for another interpretation of this work), this essentially enables a set of rules to be written which when interpreted by a parser results in geometry being created representing the building for rendering.<br />
<br />
<b><span style="font-size: large;">Language Choice</span></b><br />
<br />
I wanted the system to be easy to play with so it was essential that the runtime be able to reload and parse these rules dynamically without having to recompile or even re-run the program so coding them statically into the C++ source was out, but rather than write my own parser I decided to make use of the <a href="http://www.lua.org/about.html" target="_blank">Lua</a> language and integrated that into my project instead. This gives me an out of the box solution for runtime loading and parsing and provides a far more powerful language feature set than I could hope to achieve in any home grown system. <br />
<br />
Lua also has the advantage of being a model of simplicity to integrate with C++, syntactically similar enough to be quick to learn and well supported with documentation and tutorials on the internet. There are even various editing environments with support such as <a href="http://notepad-plus-plus.org/" target="_blank">Notepad++</a> and <a href="http://www.sublimetext.com/" target="_blank">Sublime </a>providing out of the box syntax highlighting (there is also a Visual Studio extension to provide Lua syntax highlighting although at this time it appears to not support the 2012 edition I am using).<br />
<br />
<b><span style="font-size: large;">The Fundamentals</span></b><br />
<br />
In essence, I am representing each type of building or structure with a Lua file, when a building of that type needs to be generated the Lua file is loaded and an entry point function called "<i>Axiom</i>" executed. From here on the Lua environment has control making it's entire feature set available to richly describe the shape and construction of the building. The output of this process is a Signed Distance Field (SDF, i.e. a 3D array of signed distance values) that can then be plugged in to the sparse voxel octree builder to produce a data structure the GPU ray caster can process to render images such as the castle above.<br />
<br />
I expose a number of C++ functions to the Lua environment (22 at this time) that the script can call to perform useful intermediate processing operations and to output the final primitives that the SDF is constructed from.<br />
<br />
The fundamental concept the script operates on at any one time is a 3D bounding box called a Scope, when the Axiom function of the script is called there are globals called <i>xPos</i>, <i>yPos</i>, <i>zPos</i> and <i>xSize</i>, <i>ySize</i>, <i>zSize</i> pre-defined with the 3D position and size of the current scope respectively which can be used to either emit an output shape or create additional scopes for further processing. Each additional scope created is given a name which is the name of the Lua function to call to produce it's contents. When that function is called it will in turn have the position and size globals pre-set to represent it's own position and size thus allowing each function to act on it's own area of 3D space without having to worry about where it came from or what transforms it's parents have been through.<br />
<br />
<span style="font-size: large;"><b>Learning by Example</b></span><br />
<br />
If that sounds confusing, a simple example might help:<br />
<pre class="prettyprint lang-lua">function Axiom()
Scope(0, 4, 0, xSize,ySize-4,zSize, "Castle");
Scope(0,0,0, xSize, 4, zSize, "Ground");
end</pre>
here the main <i>Axiom</i> function is taking the original scope it's been given (which encompasses the entire 3D bounds of the building to start with) and creating two further scopes for later processing. The first is called "Castle" and is at position (0, 4, 0) with the same size as the global scope in the X and Z axis but four metres shorter than the global scope in the Y axis.<br />
<br />
The second scope it creates is called "Ground" and is at the origin of the current global scope (the origin is in the minimum corner of the box in (x,y,z) space) with the global scope's size in X and Z but is four metres tall in Y (the height). Essentially this is dividing the total space of the building into two horizontal slices, a 4m tall one at the base which will become the ground and a second sitting on top of that into which the building proper will be generated. The global scope is automatically destroyed when the function exits as it's not needed any more.<br />
<br />
As it stands this script won't actually output anything, we need to add more code so it knows what to do with those two new scopes:<br />
<pre class="prettyprint lang-lua">function Castle()
Box(5, 0, 5, xSize-10, ySize, zSize-10);
end
function Ground()
Box();
end</pre>
note here how the name of the scope is simply the name of the Lua function to call - a great advantage of a dynamic language like Lua that would be impossible in C++. In this simple example I've made the <i>Castle</i> function output a box 10m smaller than but centred within the scope in the X and Z axes and the full height of the scope in Y while the <i>Ground</i> function simple creates a box that fills the scope.<br />
<br />
Note the handy shortcut for the ground's box - having a shape fill the current scope is a common requirement so overloadings of many functions such as <i>Box</i> are provided that assume the origin and scope size as defaults.<br />
<br />
Running this simple script now produces this:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuuunwmZlb8B208X_9kDAFmmBgrltfciJ8_Ho1dhlTBQp5fN5hR1gBOwqh7nx9juxL5rmI9c8DE_xoB8LfQljDaPFb25M1BnYs9F9L_AJuE-DNWhqip0h3riEWlnpeb0yTk0MRJDGcOxc/s1600/ScriptExample1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuuunwmZlb8B208X_9kDAFmmBgrltfciJ8_Ho1dhlTBQp5fN5hR1gBOwqh7nx9juxL5rmI9c8DE_xoB8LfQljDaPFb25M1BnYs9F9L_AJuE-DNWhqip0h3riEWlnpeb0yTk0MRJDGcOxc/s320/ScriptExample1.jpg" width="320" /></a></div>
<br />
not perhaps the most convincing of castles but hopefully you can see how the script produces what you see here. It's important to remember though that it's not geometry that's being generated - there are no vertices and triangles being created by that <i>Box</i> function; instead for each point in the 256x256x256 SDF grid I'm using in the viewer it's calculating how far that point is from the surface of the boxes and storing that.<br />
<br />
The brick and stone texturing is a result of the default appearance set when scripts are run, appearances are essentially a pair of textures one of which is used for flat surfaces and another for vertical ones - surface in between receive a blend of the two textures depending on their slope. It is possible to specify different appearances when creating shapes but I'll let these examples stick to the default for now.<br />
<br />
<b><span style="font-size: large;">A Slightly More Complex Example</span></b><br />
<br />
I don't want to turn this post into a reference manual for the construction system as that would be quite verbose and probably not terribly interesting so I'll jump straight forward to a more advanced example, making a tower with a crenelated top:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgei-eH4I-a8E_hgmpyRPBgvGrDQTPlqsNjiHxl1w_iV7tNbpABQsFomC54iBgy1pgJcip8LTscZMamk3lK5Ghqy2nhcObRHMZgZfvo9vdarCvXs5U9cgUAtEUoL5-9k5GxppVux5WXJvM/s1600/ScriptExample2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgei-eH4I-a8E_hgmpyRPBgvGrDQTPlqsNjiHxl1w_iV7tNbpABQsFomC54iBgy1pgJcip8LTscZMamk3lK5Ghqy2nhcObRHMZgZfvo9vdarCvXs5U9cgUAtEUoL5-9k5GxppVux5WXJvM/s320/ScriptExample2.jpg" width="320" /></a></div>
<br />
and here is the code that produced it:<br />
<pre class="prettyprint lang-lua codescroll">function Axiom()
Scope(xSize/4, 4, zSize/4, xSize/2,ySize-4,zSize/2, "SquareTower");
Scope(0,0,0, xSize, 4, zSize, "Ground");
end
-- Produces a square tower with a crenelated top
function SquareTower()
Box(xSize, ySize-3, zSize); -- Body of square tower
SquareBoundary(merlonDepth, ySize-3, 3, xSize, zSize, "WallRampart");
end
-- Produces four long thin scopes forming a square boundary edge
function SquareBoundary(thickness, yPos, height, xSize, zSize, scopeName)
Size(thickness, height, zSize);
Scope(0, yPos, 0, scopeName);
RotateToY(180);
Scope(xSize, yPos, zSize, scopeName);
Size(thickness, height, xSize);
RotateToY(90);
Scope(xSize, yPos, 0, scopeName);
RotateToY(270);
Scope(0, yPos, zSize, scopeName);
RotateToY(0);
end
-- Produces a low wall topped by crenelations
function WallRampart()
Box(xSize, ySize*0.5, zSize);
Size(xSize, ySize*0.5, zSize+crenelWidth);
MoveTo(0, ySize*0.5, 0);
Appearance("StoneWall");
SubDivZ({ "rep", merlonWidth, "Merlon" });
end
-- Produces a single merlon
function Merlon()
Box(xSize, ySize, merlonWidth-crenelWidth);
end</pre>
<br />
hopefully it's length doesn't scare you off, assuming you're still with me you can see that the basic structure is the same; functions are often creating new scopes at a given position relative to their parents and based on the parent scope's size. Points that I think are worth noticing include:<br />
<ul>
<li>The <span style="font-family: Courier New, Courier, monospace;">SquareBoundary()</span> function shows how other Lua functions can be called just like normal to do additional work and allow code re-use without having to create intermediate scopes</li>
<li>The <span style="font-family: Courier New, Courier, monospace;">RotateToY()</span> function demonstrates rotating scopes, all translation and rotations are inherited automatically to child scopes</li>
<li>The <span style="font-family: Courier New, Courier, monospace;">Appearance()</span> function is used here to change the appearance for subsequent shapes, in this case changing to an appearance that doesn't have the rock floor texture so we don't get rock on the flat top surfaces of the merlons.</li>
<li>The <span style="font-family: Courier New, Courier, monospace;">SubDivZ()</span> function illustrates a more powerful way of subdividing space, in this case creating as many instances of the <i>Merlon</i> scope with a given width as will fit along the Z axis of the current scope.</li>
</ul>
<b><span style="font-size: large;">Constructive Solid Geometry</span></b><br />
<br />
At the moment the system is only capable of generating two basic geometric solids from it's scopes, either the boxes you've already seen or cylinders along the X, Y or Z axes. These basic shapes can be combined however to create far more interesting shapes using a technique called <a href="http://en.wikipedia.org/wiki/Constructive_solid_geometry" target="_blank">Constructive Solid Geometry</a> (CSG) where the results are combined using one of a range of boolean operators.<br />
<br />
There are three primary operators available:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPSyE2fW4r54-LRd_vgDBbuK6d2fjwYnEMdKF-D-GKxDQ5ICABOzFB-KowIIPEWUXQSBHKjCRbhg9yZBbgiNgA7CikwhvImVxedRyOncQ4WbOsNYKFOSl3g-0_nNJmKysr82Z03JUtQQ0/s1600/CSG_Union.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPSyE2fW4r54-LRd_vgDBbuK6d2fjwYnEMdKF-D-GKxDQ5ICABOzFB-KowIIPEWUXQSBHKjCRbhg9yZBbgiNgA7CikwhvImVxedRyOncQ4WbOsNYKFOSl3g-0_nNJmKysr82Z03JUtQQ0/s320/CSG_Union.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Union</i> is the default which merges the shapes together</td></tr>
</tbody></table>
<pre class="prettyprint lang-lua codescroll">function Axiom()
Box(0, 0, zSize/4, xSize/2, ySize/2, zSize/2);
CylinderZ(xSize/4, 0, 0, xSize/2, ySize, zSize);
end</pre>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjElzCbsFqcweO9tMbPZ5iqJ4ZefVeSXHWimwN27LYqo9QWV8LPCChJyGjNQCpU5va0pqV6pr9zT7yxJaUObvdLs_SPFGLJOpNxFCq-ZLr2yFMzXQ4a2El1wxKx81Swk3mP55LqFvA9oqs/s1600/CSG_Subtract.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjElzCbsFqcweO9tMbPZ5iqJ4ZefVeSXHWimwN27LYqo9QWV8LPCChJyGjNQCpU5va0pqV6pr9zT7yxJaUObvdLs_SPFGLJOpNxFCq-ZLr2yFMzXQ4a2El1wxKx81Swk3mP55LqFvA9oqs/s320/CSG_Subtract.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Subtract</i> removes the second shape from the first</td></tr>
</tbody></table>
<pre class="prettyprint lang-lua codescroll">function Axiom()
Box(0, 0, zSize/4, xSize/2, ySize/2, zSize/2);
CylinderZ(xSize/4, 0, 0, xSize/2, ySize, zSize, CSG_Subtract);
end</pre>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbz_o6hsiIQzhjO1st38ylA-TsJs97lIMjz6-0acMurl5XFxxDQvmjiqClDoPaVwaofu217vF2PrglAn1wUa5P4cwh0IpQSRACYC0bJQO9nzwPC0lgD0Y-ES6x0URYcQnRam8AyL3FRCs/s1600/CSG_Intersect.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbz_o6hsiIQzhjO1st38ylA-TsJs97lIMjz6-0acMurl5XFxxDQvmjiqClDoPaVwaofu217vF2PrglAn1wUa5P4cwh0IpQSRACYC0bJQO9nzwPC0lgD0Y-ES6x0URYcQnRam8AyL3FRCs/s320/CSG_Intersect.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Intersect</i> produces the portion of solid that is common to both</td></tr>
</tbody></table>
<pre class="prettyprint lang-lua codescroll">function Axiom()
Box(0, 0, zSize/4, xSize/2, ySize/2, zSize/2);
CylinderZ(xSize/4, 0, 0, xSize/2, ySize, zSize, CSG_Intersect);
end</pre>
<br />
CSG has been around for a long time and I've used it before on CPU based ray tracer projects where it's relatively straightforward to implement operating on the intersecting spans of rays, but it's a whole lot more complicated when operating on geometry as the finite numerical precision of floating point numbers often leads to problematic fragments and slivers of geometry, problems with non-sealed hulls and issues with coincident surfaces.<br />
<br />
Dealing with SDFs however eliminates these problems as the sample space is regular with no problems of open or closed surfaces. (See Iñigo Quilez's page on <a href="http://www.iquilezles.org/www/articles/distfunctions/distfunctions.htm" target="_blank">Modeling with Distance Functions</a> if you are interested in this)<br />
<br />
In addition to the regular boolean operators, working with SDFs provides some other interesting options:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitf384mDKaMrfJgOrHgsTuMg0UfP3QVycdTBAF-JdD8XLxCUnJ31VYmO7-424upWN5hl-FEHEGoov2-x-yBO9HMO7eqe3OV1QQ-p8F4GhGnqT-HxapomBAskVEuLmsBdsu6CW5gJVSWbg/s1600/CSG_Add.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitf384mDKaMrfJgOrHgsTuMg0UfP3QVycdTBAF-JdD8XLxCUnJ31VYmO7-424upWN5hl-FEHEGoov2-x-yBO9HMO7eqe3OV1QQ-p8F4GhGnqT-HxapomBAskVEuLmsBdsu6CW5gJVSWbg/s320/CSG_Add.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Adding signed distances together to create a blend</td></tr>
</tbody></table>
<pre class="prettyprint lang-lua codescroll">function Axiom()
Box(0, 0, zSize/4, xSize/2, ySize/2, zSize/2);
CylinderZ(xSize/4, 0, 0, xSize/2, ySize, zSize, CSG_Add);
end</pre>
<br />
here a basic addition is being applied resulting in a blend between the box and the cylinder shapes, likewise subtraction and other arithmetic operations can be used.<br />
<br />
<b><span style="font-size: large;">Advanced Parameters</span></b><br />
<br />
The flexibility of Lua also makes it simple to add additional optional parameters to function calls. By allowing a table to be passed at scope and shape creation time context specific values can be given:<br />
<br />
<pre class="prettyprint lang-lua codescroll">CylinderY(0, ySize-10, 0, xSize, 10, zSize, { topRadius=0; appearance="RoofTilesBrown" });</pre>
<br />
here the cylinder has been given a zero top radius factor to turn it into a cone and the appearance for the shape changed to a brown roof tile effect producing the top of the castle's turret:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpYcI0IKu6hjlUfCRjDMsEyGsrEH6ear0M3Qk6YhH5pn5VI5RxVsD-X80p5CdoQiEzfw7vvFOrYGxzy1cOasYyAw91yIdusTCzLnq-UE4oVzzE3M80kUFg84ekLsT7_31ievsCx7hpYM0/s1600/TurretRoof.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpYcI0IKu6hjlUfCRjDMsEyGsrEH6ear0M3Qk6YhH5pn5VI5RxVsD-X80p5CdoQiEzfw7vvFOrYGxzy1cOasYyAw91yIdusTCzLnq-UE4oVzzE3M80kUFg84ekLsT7_31ievsCx7hpYM0/s320/TurretRoof.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Turret roof formed by a cylinder with zero top radius</td></tr>
</tbody></table>
<b><span style="font-size: large;">Space Modifiers</span></b><br />
<br />
Finally it's also perfectly possible to apply arbitrary functions to areas of the SDF in much the same way as the geometric primitives do, but instead of generating values we can modify the values already present to produce interesting or bizarre effects that would be difficult to achieve with geometry directly (or at least without massively tessellated geometry)<br />
<br />
<pre class="prettyprint lang-lua codescroll">NoiseShape(-4, -4, -4, xSize+8, ySize+8, zSize+8, CSG_Add, { noiseScale=0.5, noiseAmplitude=1.5 } );</pre>
<br />
here a Noise modifier has been applied to an area of the scope to warp the values already generated for the keep:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn2DlgABnaoFOh8nW5ogIXtx2x1wFcN0MdrHmAJ__ogOYbTMibuFXK9rKLT5ZuCiqi7lunhvgbfHvxIzkTBiZj4DRAG5btUVCZbTi2epi-ZkM75B86wKEI-IgnIUr_JFJGfAQrM8kW7Ws/s1600/RegularKeep.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn2DlgABnaoFOh8nW5ogIXtx2x1wFcN0MdrHmAJ__ogOYbTMibuFXK9rKLT5ZuCiqi7lunhvgbfHvxIzkTBiZj4DRAG5btUVCZbTi2epi-ZkM75B86wKEI-IgnIUr_JFJGfAQrM8kW7Ws/s320/RegularKeep.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Regular keep without the modifier applied</td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6KhoeKWnyJ6xJmeY5ot9Akqwb1iblfFxhKPVIBY-XyCXy_qmePHuIhXShVzGRKzT7vWhFgfTY20NFqGStHJvsqQ-tZG69cqeyqNOq0oV3o3OnicsFLs0SFeez6pvu3GrVHx7PnxO18wM/s1600/WarpedKeep.jpg" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6KhoeKWnyJ6xJmeY5ot9Akqwb1iblfFxhKPVIBY-XyCXy_qmePHuIhXShVzGRKzT7vWhFgfTY20NFqGStHJvsqQ-tZG69cqeyqNOq0oV3o3OnicsFLs0SFeez6pvu3GrVHx7PnxO18wM/s320/WarpedKeep.jpg" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Same keep with the Noise() modifier applied just for fun</td></tr>
</tbody></table>
although this is something of a contrived example (unless you were planning on making a castle out of jelly) it shows the power of the effect, other effects such as twists, warps and even softening blurs are also possible.<br />
<br />
<span style="font-size: large;"><b>Variations on a Theme</b></span><br />
<br />
<br />
So far everything presented here has been undeniably procedural being based on rules and parameters but it has also been entirely deterministic. If I feed one of these Lua scripts into the system five times I will get exactly the same building out each and every time which is going to make it somewhat tedious to produce the wide variety of buildings and structures with which I hope to populate my voxel worlds.<br />
<br />
The real power of procedural systems though of course is their ability to produce endless variations of something by supplying different input parameters or seeds. For my building system there are only two primary inputs that dictate how the final architecturee looks: the first is the bounding box for the building comprised of the size of the plot onto which the building must fit and the height it's allowed to reach. These would be defined by the higher level town or city planning systems driven by factors such as population density, building type and the distance from the centre of the conurbation, the bounds are simply passed to the Axiom function as the size of the root scope. By writing the Lua script properly it can be made to work with a wide variety of overall building sizes, functions such as SubDiv() are really useful here to enable repetition along unknown length regions without undue scaling or stretching.<br />
<br />
The second factor that has a more significant impact on the form of the building is the seed value that is used to initialise the random number generator. This is calculated from the position of the building on the planet so the same building in the same spot will always look the same but the same script file used to generate a building at a different location may look completely different. The Rand() function is used in the script to make decisions that can change the architecture: deciding whether to put a square tower, a round turret or nothing at all on a corner of the castle for example or whether there is a gateway on a given wall. Rand() can also be used to vary the size of scopes and shapes, so for example once the choice of tower or turret is made it's width and height can be further randomised within sensible limits leading to a wide variety of final apperances.<br />
<br />
<br />
<span style="font-size: large;"><b>Wrap Up</b></span><br />
<br />
Hopefully this has been an interesting glimpse into what I hope will be a powerful system for synthetic formations, I've certainly learnt a lot about Lua and it's integration with C++ along the way which has been rewarding in itself, but feeling like the system I've created is open and flexible enough to produce interesting, realistic(ish) or even fantastical structures is satisfying.<br />
<br />
There is even scope here to release a stand alone version of the viewer tool these screenshots are from so you too could experiment with creating architecture from Lua - as long as people can accept it's limitations of course<br />
<br />
Anyone interested?John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com4tag:blogger.com,1999:blog-3862349614860735414.post-21203456401548687472012-11-08T19:10:00.001+00:002012-11-08T19:11:33.188+00:00Impossible WorldsAlthough I was pretty happy with the DEM terrain mapping described in my last post, I was aware that it didn't exactly show off the incredible flexibility that moving to voxels provides over the more traditional height-field grid techniques I employed in some of my previous projects. You pay a massive cost in complexity, computational cost and rendering performance moving to voxels so it seemed a bit poor to not show off their strengths a bit more.<br />
<br />
To this end, and just to have a bit of fun, I've been playing about with the underlying distance field functions my planets are based upon. All the images in the previous post used a simple spherical distance function to give a basically realistic planet shape, but there's absolutely no reason (discounting physics) not to use any arbitrary function to produce the underlying shape upon which the DEM mapping works.<br />
<br />
Here are a few of the toy-worlds I came up with:<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuSB0qO2lyqFykLtwiMHDxEsln4oa-fSZYBUg4z-oInGzAPXR1AfqUN2ut29IBbTiPrsnqZm_PJPBrXXctAov7rZG9K5b9aWIzJmZgODUfy694Uc7AyJ0TQI6kjXnH-eGfecehFFEvDNk/s1600/CubeWorld1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuSB0qO2lyqFykLtwiMHDxEsln4oa-fSZYBUg4z-oInGzAPXR1AfqUN2ut29IBbTiPrsnqZm_PJPBrXXctAov7rZG9K5b9aWIzJmZgODUfy694Uc7AyJ0TQI6kjXnH-eGfecehFFEvDNk/s400/CubeWorld1.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Why not have a cube world...<br /> (maybe the birthplace of the Borg?)</i></td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhif_QIo_qTU3zWYWcFwCbcnVHIllCvxpC1KronWl3PbGO5CztC2iytzczTNMdHt3nu8Hq7QGSprK0XoyLEfYziOuJqsFuzEPbMBjOwiLw8-OhRwsuoFGouhHVOyUBJoZzxtEr7d-zW0tQ/s1600/TorusWorld1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhif_QIo_qTU3zWYWcFwCbcnVHIllCvxpC1KronWl3PbGO5CztC2iytzczTNMdHt3nu8Hq7QGSprK0XoyLEfYziOuJqsFuzEPbMBjOwiLw8-OhRwsuoFGouhHVOyUBJoZzxtEr7d-zW0tQ/s400/TorusWorld1.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Or a torus?</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJwKpBD8lrOpVPcXidAwMs4STEl-Q0u9EiTyxZXZKwc0Zzy_HBzPNkNtk7jqlWjjub4ylo2yLXE9TyHQjs7ddA-l6YFJov5BQSvzCdPicYnHQD73BfeNpUpTMhXi1-oZ_pV4z_Aipuroc/s1600/CrossWorld1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJwKpBD8lrOpVPcXidAwMs4STEl-Q0u9EiTyxZXZKwc0Zzy_HBzPNkNtk7jqlWjjub4ylo2yLXE9TyHQjs7ddA-l6YFJov5BQSvzCdPicYnHQD73BfeNpUpTMhXi1-oZ_pV4z_Aipuroc/s400/CrossWorld1.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>A union of three cylinders with a bit of -ve blending at the intersection</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXe1u3z1K4mHMfg-wjZAZSddL9rnI5REkRBbdCmwldkdBjSsIdm6SjLpNOrkcNg9_WeNyrF3_1bJPk3Tn-ks3dbFMPWZZJoWGdTbIrTcJ8yq97DS0kVYOM9_xLKG8ZqdIIeZWP6MrwEws/s1600/HaloWorld.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXe1u3z1K4mHMfg-wjZAZSddL9rnI5REkRBbdCmwldkdBjSsIdm6SjLpNOrkcNg9_WeNyrF3_1bJPk3Tn-ks3dbFMPWZZJoWGdTbIrTcJ8yq97DS0kVYOM9_xLKG8ZqdIIeZWP6MrwEws/s400/HaloWorld.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>...and my timely tribute to Halo 4...</i></td></tr>
</tbody></table>
Obviously the terrain system copes with varying degrees of success when applied to these unconventional shapes but considering that it was designed for spheres it's surprisingly decent. A proper parametric mapping would work better for the shapes that have one but these experiments are just toys after all.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht8ygSSpln0Va8HDsFYcfz65qvT03zQsjVlTXbgyR8ezYDCEuEvHILij9wK46R8Af78p5Tk_Y5S1e7FARO1W5j0J0fA-1XxYtozO77LNSoG_rbodJFCHxDKE5nbOUnoTsewT-n5jlbdNw/s1600/BlobWorld1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEht8ygSSpln0Va8HDsFYcfz65qvT03zQsjVlTXbgyR8ezYDCEuEvHILij9wK46R8Af78p5Tk_Y5S1e7FARO1W5j0J0fA-1XxYtozO77LNSoG_rbodJFCHxDKE5nbOUnoTsewT-n5jlbdNw/s400/BlobWorld1.png" width="400" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Blobby world based on a low frequency noise function</i></td></tr>
</tbody></table>
Although the plan is for planets in my universe to be in general constrained by the laws of physics and nothing like these, this generality of form opens the door for having blasted husks of planets with huge chunks missing, cracked open meteorite ravaged planetoids and maybe just the odd eccentric alien world...John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com1tag:blogger.com,1999:blog-3862349614860735414.post-37648780278039599412012-11-06T22:32:00.000+00:002012-11-06T22:35:59.789+00:00The Birth of Voxel PlanetsAfter getting some basic data sets up and rendering with my GPU based sparse voxel octree ray-caster I wanted to get stuck in to actually using it to render planets. Now as I've discussed before planets are pretty big and dealing with them can throw up many numerical precision issues for algorithms. Having dealt with these in past projects however it seemed daft to reinvent the wheel so I spent some time porting the general solar system simulation code from Osiris into Voxelus. This was made more complicated and time consuming as I started a whole new C++ framework for Voxelus rather than use what was becoming a very old and messy utility library - it was time well spent though as I believe it's healthy to have a clean slate now and again.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh52Y5Ltv1ePW9G9j5njt9P1LGftlyirqjRe0nEGa91BNaUqjgxaz_yNpqrIliVBUW-tpSNtlcrexbg9UB9q5SU9kOWMoI2Fr-us_hN0HFPiYzXkkHyBy-gFeVfD_-2fYEFhPoTsDCiGqE/s1600/FirstPlanet.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh52Y5Ltv1ePW9G9j5njt9P1LGftlyirqjRe0nEGa91BNaUqjgxaz_yNpqrIliVBUW-tpSNtlcrexbg9UB9q5SU9kOWMoI2Fr-us_hN0HFPiYzXkkHyBy-gFeVfD_-2fYEFhPoTsDCiGqE/s320/FirstPlanet.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Very first voxel planet (256^3 grid)</td></tr>
</tbody></table>
With that done though I could bring across the procedural nebula backdrop generator which with a couple of colour tweaks just for fun at least gave me a nice cubemap backdrop. Above is my very first voxel planet, rendered using a 256^3 data set as source for the SVO with tri-planar texture mapping to give it some interest. The distance field from which this is generated is really just "distance from surface of sphere" so it's neither particularly interesting nor showing the power of voxels, but it's a start.<br />
<br />
Modelled as an earth side planet, the 256^3 data makes each voxel almost 50 Km on a side!<br />
<br />
Now perfect spheres can only be so interesting, so adding some relief detail was my next job. Although completely arbitrary shapes are possible with voxels I wanted to get started with some fairly traditional planet forms so the initial focus was on producing something reasonably realistic. I've used a variety of fractal functions in the past to simulate terrain, ridged multi-fractals being a popular choice but such terrains tend to suffer from a large degree of homogeneity - they tend to have a large degree of self-similarity especially across large areas even when combining numerous frequencies.<br />
<br />
In the spirit of trying new things, rather than tread that worn path again I wanted to try something different for Voxelus. Rather than generate heightfields from fractals, I wanted to see if I could generate something suitable using real-world Digital Elevation Model (DEM) data. A little searching turned up the <a href="http://en.wikipedia.org/wiki/Shuttle_Radar_Topography_Mission" target="_blank">Shuttle Radar Topography Mission (SRTM)</a> data which is available in a variety of resolutions. Downloading this and writing a little command line tool to load and cut out interesting sections of it enabled me to produce a library of DEM tiles that I could use.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYR58899_LAXSw8K9YpDPnYTwmpm84flYcIqjKitpesY_uI33AmDNynAPHzInJwcqz2nQWPccaIgPUrS3_tgcg-hsfFMbEcAxPJ349N2mBSEAg7fGOC-B99XNX7HKyT64Q3zU9TGs2z38/s1600/patch_0077.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhYR58899_LAXSw8K9YpDPnYTwmpm84flYcIqjKitpesY_uI33AmDNynAPHzInJwcqz2nQWPccaIgPUrS3_tgcg-hsfFMbEcAxPJ349N2mBSEAg7fGOC-B99XNX7HKyT64Q3zU9TGs2z38/s1600/patch_0077.png" /></a></div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8_xM3kH2azVNijqnvxAvIomguPj2b8gSeZS6UfHqhVt3Tyxq5R699q7TVZ8CQdBwz8JRQCdMCb7x0PR2k72l8zh2M6RIHdtXi6FPLxJMG4mhAAfvuHb-XtYyt9VTaYmKV_FXwOyaDiDo/s1600/patch_0106.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8_xM3kH2azVNijqnvxAvIomguPj2b8gSeZS6UfHqhVt3Tyxq5R699q7TVZ8CQdBwz8JRQCdMCb7x0PR2k72l8zh2M6RIHdtXi6FPLxJMG4mhAAfvuHb-XtYyt9VTaYmKV_FXwOyaDiDo/s1600/patch_0106.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Two examples of DEM patches my tool cut out of the massive SRTM 500m data set</i></td></tr>
</tbody></table>
I used a simple height histogram to choose areas where there was a lot of variety in elevation in an effort to make the final result more interesting - although the SRTM 500m data set is hundreds of MB in size, a surprising amount of the Earth's surface is in fact pretty damn flat!<br />
<br />
Armed with my library of DEM tiles I needed a way to map them onto my planet. Mapping 2D squares onto a sphere is however a classically impossible task (ask any cartographer) so rather than being able to make some perfect mapping I instead needed to quilt the surface with overlapping tiles, using as few as possible while ensuring that there were no gaps in between. I also wanted to be able to quilt at various resolutions so I could combine several "octaves" of DEM data in a traditional fractal-esque fashion.<br />
<br />
To do this, I based my mapping on an <a href="http://en.wikipedia.org/wiki/Icosahedron" target="_blank">Icosahedron</a>, a regular polyhedron made up of 20 triangles.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="http://upload.wikimedia.org/wikipedia/commons/e/e2/Icosahedron.gif" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="http://upload.wikimedia.org/wikipedia/commons/e/e2/Icosahedron.gif" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Icosahedron (image from Wikipedia)</i></td></tr>
</tbody></table>
The key attraction of the icosahedron is that each side is an equilateral triangle producing an even distribution of surface area, and that each side can be recursively subdivided into four smaller equilateral triangles to produce higher and higher detail meshes with the same characteristic. By normalising the position of the vertices introduced by these subdivisions an increasingly high quality approximation to a sphere is achieved.<br />
<br />
To map my DEM tiles onto the sphere I treat each triangle of the icosahedron as representing one tile. The centre of the tile lies on the centroid of the triangle and extends outwards to cover the triangle completely. Each tile also has a random rotation around the centroid so to ensure that there are no gaps between the tiles from adjacent faces, the tile has to be twice the radius of the triangles circumscribed circle in size. There will be considerable overlap around the edges between adjacent tiles but that's all the better to avoid seams.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQ8FMsy80CkQKlDokLh8rrJZ6kScipXqi764j4QvccRGcKJfz8-SH61RaaICiYPOJQpmu0A6iSurNmA8VYHJBYuwddpnEYrgSIvTGo1pmvROXtFdtI2G8eOJlKjSgh_PUilmXD1d485A8/s1600/DEMPatches_Gen1_12.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjQ8FMsy80CkQKlDokLh8rrJZ6kScipXqi764j4QvccRGcKJfz8-SH61RaaICiYPOJQpmu0A6iSurNmA8VYHJBYuwddpnEYrgSIvTGo1pmvROXtFdtI2G8eOJlKjSgh_PUilmXD1d485A8/s320/DEMPatches_Gen1_12.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>First "octave" of DEm tiles at primary icosahedron vertices, there are 12 tiles here</i></td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6_eIp0xNqXJIaQQrlSvY1plCfXrjnb44Mnlkt7fqOkWqeoNVq0tIy864IrhAPos8JY8WReCaXpvwJlHnRH-MyP8YnwqQjS8By851uUsuyN2GKpMaJVvK2AwWIb0Gsag6jwz_RSHYrtoU/s1600/DEMPatches_Gen2_42.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6_eIp0xNqXJIaQQrlSvY1plCfXrjnb44Mnlkt7fqOkWqeoNVq0tIy864IrhAPos8JY8WReCaXpvwJlHnRH-MyP8YnwqQjS8By851uUsuyN2GKpMaJVvK2AwWIb0Gsag6jwz_RSHYrtoU/s320/DEMPatches_Gen2_42.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>One further level of subdivision produces 42 smaller tiles</i></td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_cEeL1y_mJtbcy_4ZWT9sHDFzKX9pHNn_PDZGLK_Ou3RE1q7zuHMJY1CX2lN2UvIp8na0Sf3u21RqW4ToWThuXZwR3m8Itd_KnRVyv4woXLlJWF442dY-hQa0tKP3zu2UPR-j5vXXKwE/s1600/DEMPatches_Gen3_162.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_cEeL1y_mJtbcy_4ZWT9sHDFzKX9pHNn_PDZGLK_Ou3RE1q7zuHMJY1CX2lN2UvIp8na0Sf3u21RqW4ToWThuXZwR3m8Itd_KnRVyv4woXLlJWF442dY-hQa0tKP3zu2UPR-j5vXXKwE/s320/DEMPatches_Gen3_162.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Subdividing again produces 162 even smaller tiles</i></td></tr>
</tbody></table>
These three images are to visualise how the tiles are distributed on the sphere. These examples actually have the tiles at the vertices rather than the triangle centroids but I later switched to the centroids as I thought it gave a better result. Centroidal tiles produce 20, 80 and 320 tiles at each level respectively. The tiles here are artificially smaller than final size simply to improve the visualisation.<br />
<br />
Expanding the tiles to their correct sizes and repositioning them at the triangle centroids rather than the vertices produces a more accurate result:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUEq2ilKf5FOyJ4_WX-Qn2dH2vjponGWGI-wG9X02g_UmtAVr5qobfjuVWnFiGrlDRp6zYmQFn6juaDvCrsMVid5knUVj6MOQNbn6RbaXs-YWkJiV8_pslCs_xJ0RLxjjJSof0LQuMgyc/s1600/DEMQuilt_Gen1.png" imageanchor="1"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiUEq2ilKf5FOyJ4_WX-Qn2dH2vjponGWGI-wG9X02g_UmtAVr5qobfjuVWnFiGrlDRp6zYmQFn6juaDvCrsMVid5knUVj6MOQNbn6RbaXs-YWkJiV8_pslCs_xJ0RLxjjJSof0LQuMgyc/s320/DEMQuilt_Gen1.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7pmprPeaZcS4a7QYzcSPeq5oQ2tXXt9gzOn6b8lT8hIobMzWs7vb3xK3kRsJ9Wo3cb2_w9WwPnPjyEpsaV3CfIDhBD-xCCcTT2MMUTFQXOBuYRKVzIsml6_fJ9aqeZI4wA6ltWXnO2lw/s1600/DEMQuilt_Gen2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7pmprPeaZcS4a7QYzcSPeq5oQ2tXXt9gzOn6b8lT8hIobMzWs7vb3xK3kRsJ9Wo3cb2_w9WwPnPjyEpsaV3CfIDhBD-xCCcTT2MMUTFQXOBuYRKVzIsml6_fJ9aqeZI4wA6ltWXnO2lw/s320/DEMQuilt_Gen2.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWyCfeN5JscG8Mw-uCCU6IZOiMcwe8fDowtdPVdcM-wdIE46OQXmAE2I6oHVUSFjoK7zC6ZkscA5PdHfwQ0fyRlUBkZkVViVQib53jbb8N0uqROdqgW0LWq_Ua9lOj4dQi5uieL1VqZfg/s1600/DEMQuilt_Gen3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWyCfeN5JscG8Mw-uCCU6IZOiMcwe8fDowtdPVdcM-wdIE46OQXmAE2I6oHVUSFjoK7zC6ZkscA5PdHfwQ0fyRlUBkZkVViVQib53jbb8N0uqROdqgW0LWq_Ua9lOj4dQi5uieL1VqZfg/s320/DEMQuilt_Gen3.png" width="320" /></a></div>
<br />
Here the individual tiles have been tinted to aid identification and the edges shaded green based upon the weight of their influence at that point. As you can see the weight of each tile fades off towards it's edges to avoid hard seams and there is no space between them regardless of their orientation to ensure a complete mapping of the sphere. I've added a simple directional light so the relief from the DEM data can be seen.<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_y96u4XHb4rGUPKt9rGK6S6IoiFfWpn4MlyVmJf8gs_2LmeNZ3d9ViqFWh4wiQViQMeR2wEZ3O2wIZjRwjVnZ5jh4Za-X8DiZs7Co4zVLn4bUce74j0ylEmQn1hc0s_Nx3ckDa3-V2NY/s1600/DEMQuilt_Gen1_Normals.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_y96u4XHb4rGUPKt9rGK6S6IoiFfWpn4MlyVmJf8gs_2LmeNZ3d9ViqFWh4wiQViQMeR2wEZ3O2wIZjRwjVnZ5jh4Za-X8DiZs7Co4zVLn4bUce74j0ylEmQn1hc0s_Nx3ckDa3-V2NY/s320/DEMQuilt_Gen1_Normals.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Normals from just the basic level #0 data</i></td></tr>
</tbody></table>
Here the voxel normals can be seen, note that this is using just the 20 tiles from the level #0 quilting but with a mountain height of about 500 Km to make detail obvious from such a distance - by comparison Mt. Everest on Earth is just 8.8 Km tall.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxqBXW4BCC_DBce1fmh05T1HQdA3Nj7hWVfY_9tat-NEPzvgkUH5T2DSTbRyh5zYLddjqlwWMEYV4JQn3dWj3q1_bvGJB0G9defI_f9b8mClBaQlQAx5rQAWPSttk-xevRVSlpQ9eOCQY/s1600/DEMQuilt_Gen1_Terrain.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxqBXW4BCC_DBce1fmh05T1HQdA3Nj7hWVfY_9tat-NEPzvgkUH5T2DSTbRyh5zYLddjqlwWMEYV4JQn3dWj3q1_bvGJB0G9defI_f9b8mClBaQlQAx5rQAWPSttk-xevRVSlpQ9eOCQY/s400/DEMQuilt_Gen1_Terrain.png" width="400" /></a></div>
<br />
Finally here is the same level #0 data with some basic elevation/slope based texturing applied. Even with just one level of DEM data I am quite pleased with the appearance of the terrain, particularly with the variety of landscape forms in evidence which encourages me that this technique might just be viable.<br />
<br />
The coarse 256^3 nature of the data can be quite clearly seen in this image even from this distance so I think starting work on the dynamic resolution subdivision system for closer views might be next on the list of things to tackle.John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com4tag:blogger.com,1999:blog-3862349614860735414.post-29850652979912985612012-10-07T15:53:00.002+01:002012-10-07T15:53:54.309+01:00Sparse Voxel Octrees: A New Beginning<br />
There’s been quite a lot of interest lately in the realtime graphics world to do with Sparse Voxel Octrees (SVOs), so I thought it was about time I had a look at them. Although I’ve experimented with voxels before, in particular in the Geo project I worked on a few years ago (<a href="http://jwhigham.wordpress.com/category/geo">http://jwhigham.wordpress.com/category/geo</a>) I didn’t really make use of octrees in those cases and more significantly I converted the voxel data to geometry prior to rendering using the classic Marching Cubes algorithm - now generally superseded by better techniques such as Dual Contouring.<br />
<br />
Having been down that route before I wanted to try something new so this time I’m endeavouring to render by ray-casting directly against the SVO rather than converting to geometry and rasterising. I don’t believe that ray-casting (or ray-tracing if you want to consider more than one bounce) is going to replace rasterisation any time soon as the genesis of GPUs has left them understandably specialised for that particular problem and therefore very efficient at it. I do believe however that when used in conjunction with rasterisation ray casting or tracing is a tool too powerful to ignore and can be an ideal solution for effects such as reflection, refraction, shadows and global illumination that are difficult to simulate with rasterisation, especially at a global scale.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJkcWCUwPV8U1MClyJGDnqApEzTUlF4r8j3gNUJkFXcgcc9qmvB3HieGJZWH-y47IMqIs_jL5Bi8biAxyvOEyumOR_0M9BRvJwDeMXfL6Aybxos8UpGm2da1LascayC41F6ui8OnqdQ1Y/s1600/1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJkcWCUwPV8U1MClyJGDnqApEzTUlF4r8j3gNUJkFXcgcc9qmvB3HieGJZWH-y47IMqIs_jL5Bi8biAxyvOEyumOR_0M9BRvJwDeMXfL6Aybxos8UpGm2da1LascayC41F6ui8OnqdQ1Y/s320/1.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>128x128x128 SVO ray-cast on the GPU with simple dot-product lighting</i></td></tr>
</tbody></table>
There have been numerous articles published in recent years postulating that rasterisation is dead and raytracing is the next big thing and just as many countenancing the opposite, the truth of course as I see it is somewhere in the middle – both have compelling strengths and weaknesses so surely the best solution is to combine them, using each where best suited to maximise their strengths.<br />
<br />
So while it’s technically perfectly possible to convert the voxels to geometry and then intersect rays against the resulting polygonal soup it flies in the face of this belief and doesn’t strike me as a particularly sensible thing to do. I also want to see if ray casting against the voxel structures directly can help alleviate some of the more challenging problems with geometry based voxelisation, particularly transitions between multiple levels of detail which I struggled with on Geo. Also like I say, it’s not something I’ve done before which is really reason enough.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU2c8YxCerlPjEq9Z6weFOV_H0LTWSbzCvVNOeC2ZRP7T0oarOwT7HHxVCGygb0WyofbzZpHqqXzQQgufm0ilXV6-FCJ4UOFvV-vUiX3v2R8gAe3oOt0UQdMREPhwjDsAsG4J1a8ESMl0/s1600/5.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU2c8YxCerlPjEq9Z6weFOV_H0LTWSbzCvVNOeC2ZRP7T0oarOwT7HHxVCGygb0WyofbzZpHqqXzQQgufm0ilXV6-FCJ4UOFvV-vUiX3v2R8gAe3oOt0UQdMREPhwjDsAsG4J1a8ESMl0/s320/5.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>128x128x128 Sphere rendered with linear distance filtering</i></td></tr>
</tbody></table>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVdNWD6Q2CNRlbDMyjPYnSZJKjCA8zJVTplsdjgX8BXo8rze8duVCtFsSJ0GytJkStEZ39nTXvGHXZW7O66fwrFhhygrMcGgTC9qBOFghyphenhyphenIKnF9soSrzKA5XEyCRQZuNCAWUDn5Oo4N5U/s1600/6.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVdNWD6Q2CNRlbDMyjPYnSZJKjCA8zJVTplsdjgX8BXo8rze8duVCtFsSJ0GytJkStEZ39nTXvGHXZW7O66fwrFhhygrMcGgTC9qBOFghyphenhyphenIKnF9soSrzKA5XEyCRQZuNCAWUDn5Oo4N5U/s320/6.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The same sphere rendered with point distance sampling to make the individual voxels clearer</i></td></tr>
</tbody></table>
<b><span style="font-size: large;">
The Aim</span></b><br />
<br />
Ultimately I would like to recreate my virtual planet out of landscape blocks of varying resolutions right down from global scale viewed from orbit to standing on the surface with reasonable fidelity. I’ll also need to have a think about how to procedurally generate the data as the flexibility to have full 3D makes the problem far more interesting than just the heightfield used by Isis (<a href="http://jwhigham.wordpress.com/tag/isis/">http://jwhigham.wordpress.com/tag/isis/</a>) and Osiris (<a href="http://johnwhigham.blogspot.co.uk/search/label/Osiris">http://johnwhigham.blogspot.co.uk/search/label/Osiris</a>). I want to avoid if possible having the floating blobs of landscape that plagued Geo’s simplistic 3D noise based solution while still having features that make the 3D nature of the data obvious such as realistic overhangs and caves.<br />
<br />
<span style="font-size: large;"><b>
First Steps</b></span><br />
<br />
As with most new experiments the first thing to do is investigate the existing body of work on the subject, which as I mentioned earlier is quite active at the moment with the dramatic increase in GPU performance and especially their flexibility in recent years. See the references section at the end for a few links to papers and videos I discovered in my internet SVO safari, it’s by no means an exhaustive list but they are some of the more interesting items I found.<br />
<br />
Once I felt I had a sufficient understanding of current techniques I jumped in with both feet and embarked on a bit of coding. While the aim is to produce a real time GPU based solution my experience of the somewhat patchy tools available for GPU debugging steered me towards producing a reference implementation in C++ on the CPU first. While it would run far more slowly this would let me debug the code effectively and help me verify that my algorithm and data structures were correct before sending them down to the GPU.<br />
This turned out to be a really useful process and with modern CPUs being pretty powerful themselves (and ray casting being a parallel friendly technique) it was possible to produce a C++ implementation that ran acceptably quickly even in debug builds. Having it render a quarter resolution version during camera movements and a full resolution one once the camera stopped also helped.<br />
<br />
The first technique I attempted was the Laine & Karras one. While there was sample source code available for this I am of the belief that to properly understand a technique you have to at least have a go at coding it up from first principles so that’s what I did. After a while though my spider sense started to tell me that storing down to the individual voxel level wasn’t going to be best suited to planetary scales so I switched to the Cyril Crassin paper where he stores an octree down to a certain level then stores bricks of voxels rather than individual ones for the final few levels.<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAeCLES_vXDLijSHhg3uXzMXF9uzZiz4TElzDBbJ4Kxe7VvrY3rKdxAHUCxNMKyNRDHwqB_q7NqBdtNbmAu9SPYPVHmqM5ib04UD4XtlkFIS6wxUTdlgFAspI6wSoFFrXbninbLQN8Vt4/s1600/2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAeCLES_vXDLijSHhg3uXzMXF9uzZiz4TElzDBbJ4Kxe7VvrY3rKdxAHUCxNMKyNRDHwqB_q7NqBdtNbmAu9SPYPVHmqM5ib04UD4XtlkFIS6wxUTdlgFAspI6wSoFFrXbninbLQN8Vt4/s320/2.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The 16x16x16 grid of voxel "bricks" that make up the actual Octree</i></td></tr>
</tbody></table>
The algorithm traverses the octree until it arrives at a suitable level of detail or a leaf node then ray-marches through the chosen brick (8x8x8 in my implementation) to find the accurate intersection. Written well it's trivial to alter the size of the bricks to compare performance and memory use in different configurations.<br />
<br />
This system felt more scalable and better suited to GPU application, plus there were advantages to do with cone tracing through the structure for shadows and global illumination applications that felt worth having available for the future.<br />
<br />
<span style="font-size: large;"><b>
Storage</b></span><br />
<br />
It’s pretty clear that a voxel is a cube shaped blob of space, games such as MineCraft have made the voxel pretty famous these days, but there are a couple of key considerations when rendering them. Primarily is what each voxel represents, in it’s most simplisitic form a voxel could be considered to be either solid or empty which is easy to understand but limits rendering to producing blocky effects dependent entirely on the size of the voxels in use. Rather than storing a flag indicating solid or empty with a voxel a better way is to store a distance value representing how far the centre of the voxel is from the surface that is to be rendered.<br />
<br />
Under this representation for a landscape voxels above the ground would have increasingly positive values while those underground would have increasingly negative ones. Rendering the isosurface where the distance is zero would produce the intended surface. The primary benefit of this scheme is that the distance value can be interpolated accurately between voxels to produce a better approximation of the surface. The quality of the output is still highly dependent upon the resolution of the data but undersampling artifacts show up as lumpy blobs rather than hard square edges which I think look generally better.<br />
<br />
The images below show the progression from highest detail to lowest through the four levels of octree produced by my 128x128x128 noise-blob data. Note that I've turned off the linear filtering of distance here in all but the final image to make the individual voxels clearer:<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMX0VROZXnkvCxfTb2LSI-KxRrJ1tqqb8l_vwOp8iHRNNRl2xyIat32f6Ujcjcv0ufUPImmfcmFdXgL8Tg6VIChgmwjElDb-B6vovD_UOCltoYb79VgeAlKtIrdAg5BqW-IlAvzsycdns/s1600/Level4.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMX0VROZXnkvCxfTb2LSI-KxRrJ1tqqb8l_vwOp8iHRNNRl2xyIat32f6Ujcjcv0ufUPImmfcmFdXgL8Tg6VIChgmwjElDb-B6vovD_UOCltoYb79VgeAlKtIrdAg5BqW-IlAvzsycdns/s320/Level4.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The 128x128x128 noise-blob at octree level 4 (highest detail)</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOuo9fuKbGkLf7-E9I4WXn9aFX3opD4dyen3Kk5cLamX9aGMR9UKSBHMnqv7HZwpD66qq4lUZDBC5aVH9_B0uCUsJWFmSDta8Ep2L-MvpxFmKSitKNO4q8l1tA0hY3TT3cWjraQ6eU7fE/s1600/Level3.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOuo9fuKbGkLf7-E9I4WXn9aFX3opD4dyen3Kk5cLamX9aGMR9UKSBHMnqv7HZwpD66qq4lUZDBC5aVH9_B0uCUsJWFmSDta8Ep2L-MvpxFmKSitKNO4q8l1tA0hY3TT3cWjraQ6eU7fE/s320/Level3.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The same 128x128x128 blob at octree level 3, each voxel is effectively twice the size</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKDZf0b_mRN2RM5r8AaXw2G-o9R03CrGqKGXyQHEMgomkv2eWn0ErliRGf8Mn6OfWz-3Lo-jDFs_kGpgORx4Yv-1inHW9CV3EOfHl7EAnvNfE4pIWk3r3uxvVwR4_ttB7l2XS6EN6gBY0/s1600/level2.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKDZf0b_mRN2RM5r8AaXw2G-o9R03CrGqKGXyQHEMgomkv2eWn0ErliRGf8Mn6OfWz-3Lo-jDFs_kGpgORx4Yv-1inHW9CV3EOfHl7EAnvNfE4pIWk3r3uxvVwR4_ttB7l2XS6EN6gBY0/s320/level2.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Down to octree level 2 and while the shape remains the detail is largely gone</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7nuw56iS0Yj7x_QbqAMVoprbUHxX1iJxeXBjgckM2HmucAt7-DQbdvNAUEytcLJFhJww8hBN01JZKBnuo3736Qt-iGSfPXcnJk-bPc5ffVssUVQwNjT7KfGFKEQA0mhIrpOHqRJQX6Wc/s1600/level1.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi7nuw56iS0Yj7x_QbqAMVoprbUHxX1iJxeXBjgckM2HmucAt7-DQbdvNAUEytcLJFhJww8hBN01JZKBnuo3736Qt-iGSfPXcnJk-bPc5ffVssUVQwNjT7KfGFKEQA0mhIrpOHqRJQX6Wc/s320/level1.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Finally at the lowest detail level on the crude outline remains. The 128x128x128 original data is now effectively being rendered at 16x16x16 resolution.</i></td></tr>
</tbody></table>
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRDTCaT9l7xRYFnfRPvfntzMMzg8C2KcYtWPTJ1K-a6IGyGp08j83rO-bMu_CeFTmOSuiWa1Jwg8IRAUsCvVhDu794GG3-1smVR-fjkr_tkwnEBauZipjgTJTLrZ1r6BigZ0mZ9btkvCA/s1600/level1Filtered.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRDTCaT9l7xRYFnfRPvfntzMMzg8C2KcYtWPTJ1K-a6IGyGp08j83rO-bMu_CeFTmOSuiWa1Jwg8IRAUsCvVhDu794GG3-1smVR-fjkr_tkwnEBauZipjgTJTLrZ1r6BigZ0mZ9btkvCA/s320/level1Filtered.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>The same lowest detail level but with linear distance filtering - not a terrible approximation of the blob considering it's only using 1/512th of the data</i></td></tr>
</tbody></table>
Normals for shading and procedural texturing can either be pre-calculated and stored in the voxels or calculated at runtime from the gradient of the distance field – empirical testing will show which is faster in practice although my current thoughts are that it's probably better to store higher precision distance values and calculate the normal when needed. Although many steps are taken through the distance field only the normal at the closest intersection is needed so it only needs to be calculated once before shading.<br />
<br />
On top of the distance value there are also other required fields such as material types which I have some ideas for but I’ll get in to that in later posts.<br />
<br />
<span style="font-size: large;"><b>
From CPU to GPU</b></span><br />
<br />
Once I was happy that everything seemed to be working on the CPU I set about converting the code to run on the GPU using a pixel shader. I toyed briefly with using a compute shader instead as I've heard there are some benefits to converting some traditionally pixel based tasks to the more flexible compute pipeline but I'll save that for a rainy day. Ray casting seems a natural fit for per-pixel so I'm sticking with a pixel shader for now.<br />
<br />
Thankfully as it had always been my intent to run it on the GPU I had written it with that in mind so storing the data structures in textures and moving the code to HLSL was fairly straight forwards. It did take a few hours to work out the inevitable teething problems compounded by the typically unstable nature of GPU debugging tools but considering what it's doing it didn't take too long.<br />
<br />
The benefit of course is that instead of taking a couple of hundred milliseconds to render it now rendered in just a few milliseconds giving me true real-time frame rates. Being able to dynamically reload the HLSL code and GPU states also meant I could make changes and see the results virtually instantly without having to re-run the program, saving a considerably amount of time. If you are creating a graphics framework for your own projects I strongly recommend adding this ability!<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2hQ6f6Mv4pUB32JAUN14irYyyjxOsTFZK1KFgQvs4R2-_KiezzmVFkTa2k6EZgc6osy6AOgK7Zdo6lWOAQushdL3UHhkJbQPjjdKHNm-j0w4Rw1e5Mm5ugY8wgRhvgfd-5p76F_nC8wE/s1600/7.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="240" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2hQ6f6Mv4pUB32JAUN14irYyyjxOsTFZK1KFgQvs4R2-_KiezzmVFkTa2k6EZgc6osy6AOgK7Zdo6lWOAQushdL3UHhkJbQPjjdKHNm-j0w4Rw1e5Mm5ugY8wgRhvgfd-5p76F_nC8wE/s320/7.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;"><i>Two-sphere distance field showing the geometry that is actually sent to the GPU (i.e. 12 triangles). The back-faces of the cube are rendered instead of the front to allow the viewpoint to move inside the cube and still render</i></td></tr>
</tbody></table>
<span style="font-size: large;"><b>Next Steps</b></span><br />
<br />
So I have a single sparse voxel octree I can render via ray casting on the GPU complete with surface normals, what's next? Well the first thing to do is to be able to render multiple SVOs at different points in space, also to have them abut neatly with continuous surface normals so there are no shading seams.<br />
<br />
After this I want to add some automatic level of detail control so the tree is traversed less deeply as the voxel bricks become smaller on screen, having this controllable from the user interface would also be useful to allow performance to be measured against visual fidelity. Next I want to add a new distance generation function to produce blocks that are a bit more landscape-like plus having the materials to go with them.<br />
<br />
So, plenty to do then!<br />
<br />
<span style="font-size: large;"><b>
References:</b></span><br />
<br />
As mentioned above, this is a fairly arbitrary collection of some of the links I came across while researching SVOs. I am sure there are many more so do feel free to report any good ones you find in the comments!<br />
<br />
<a href="http://www.tml.tkk.fi/~samuli/publications/laine2010tr1_paper.pdf">http://www.tml.tkk.fi/~samuli/publications/laine2010tr1_paper.pdf</a><br />
Efficient Sparse Voxel Octrees – Analysis, Extensions, and Implementation, Samuli Laine Tero Karras, NVIDIA Research<br />
<br />
<a href="http://en.wikipedia.org/wiki/Sparse_voxel_octree">http://en.wikipedia.org/wiki/Sparse_voxel_octree</a><br />
Sparse voxel octree<br />
<br />
<a href="http://www.daimi.au.dk/~aquak/MasterThesisKristofRoemisch.pdf">http://www.daimi.au.dk/~aquak/MasterThesisKristofRoemisch.pdf</a><br />
Sparse Voxel Octree Ray Tracing on the GPU, Kristof Römisch, Masters Thesis<br />
<br />
<a href="http://maverick.inria.fr/Publications/2011/Cra11/">http://maverick.inria.fr/Publications/2011/Cra11/</a><br />
GigaVoxels: A Voxel-Based Rendering Pipeline For Efficient Exploration Of Large And Detailed Scenes, Cyril Crassin, PhD thesis<br />
<br />
<a href="http://www.youtube.com/watch?v=VpEpAFGplnI">http://www.youtube.com/watch?v=VpEpAFGplnI</a><br />
Sparse Voxel Octree (SVO) Demo by Jon Olick<br />
<br />
<a href="http://procworld.blogspot.co.uk/">http://procworld.blogspot.co.uk/</a><br />
Procedural World<br />
<div>
<br /></div>
John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com10tag:blogger.com,1999:blog-3862349614860735414.post-68244225151030164242012-05-02T17:41:00.000+01:002012-05-02T17:43:07.913+01:00Links, old and newI thought people with similar interests to myself might find these recent presentations interesting:<br />
<div>
<br /></div>
<div>
<a href="http://publications.dice.se/attachments/GDC12_Terrain_in_Battlefield3.pdf" target="_blank">Terrain in Battlefield 3 (GDC 2012)</a></div>
<div>
<a href="http://engineroom.ubi.com/wp-content/bigfiles/farcry3_drtv_lowres.pdf" target="_blank">Deferred Radiance Transfer Volumes in Far Cry 3 (GDC 2012)</a></div>
<div>
<br /></div>
<div>
I also came across a commercial terrain tool I wasn't aware of:</div>
<div>
<br /></div>
<div>
<a href="http://www.geocontrol2.com/e_index.htm" target="_blank">GeoControl 2</a></div>
<div>
<br /></div>
<div>
and finally just for fun here are some retro links to some information on "<a href="http://www.acornelectron.co.uk/info/4thdimension/i-whit.html" target="_blank">White Magic 1</a>" & "<a href="http://www.acornelectron.co.uk/info/4thdimension/i-whi2.html" target="_blank">White Magic 2</a>" - the very first games I had commercially published:</div>
<div>
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCNGjpXtBPn7fsSGanvyrGOEAnrAqWd-CbpE24p_FPzkKgoBBehqR90rV3Se4drAQ_ZcKQ9_oBwpGBz6YDaqObYJjnOzU923L77891zOKSq_dJqrfoP4tux5AQZenCFNdk6CdP9Bdn4Dk/s1600/WhiteMagic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="112" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjCNGjpXtBPn7fsSGanvyrGOEAnrAqWd-CbpE24p_FPzkKgoBBehqR90rV3Se4drAQ_ZcKQ9_oBwpGBz6YDaqObYJjnOzU923L77891zOKSq_dJqrfoP4tux5AQZenCFNdk6CdP9Bdn4Dk/s320/WhiteMagic.png" width="320" /></a></div>
<div>
<br /></div>
<div>
This was on the Acorn Electron back in 1990, 1 whole Mhz of 8bit power and 32K of total RAM driving a 160x256 screen resolution with four colours and a tape drive!</div>
<div>
<br /></div>
<div>
I couldn't afford a floppy disc drive at the time so had to assemble the code to the screen memory, save the assembly source to tape then copy the machine code from the screen memory into normal memory to run it before loading the graphics from tape each time I wanted to test anything...happy days.</div>John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0tag:blogger.com,1999:blog-3862349614860735414.post-39950751798311964342012-04-05T17:35:00.002+01:002012-04-05T17:35:59.594+01:0024 Hours is not enough<br />
It's been a while since I posted any progress updates about the Osiris project, what spare time I have has been fairly thoroughly sabotaged this year so far by the double whammy of receiving Skyrim for Christmas then just as my interest in that was waning Mass Effect 3 coming out. Instead of working on shaders and infrastructure generation I have therefore instead been largely sneaking up on bandits & dragons and battling the Reapers in deepest space.<br />
<br />
I love playing games and I love working on interesting software projects...but it would seem that there just aren't enough hours in the day to do both. Having said that I have managed to devote some time here and there to Osiris and have been primarily working in two areas: working on implementing a new and more flexible texturing solution for the terrain and adding oceans and other bodies of water. I plan to produce more detailed posts describing my experiments in these areas in due course but for now I thought I would whet the appetite a little with a couple of screens from the water experiments.<br />
<br />
Although I've done deep water simulation on previous projects (Isis) using a 64x64 FFT grid processed on the CPU, I wanted to try a more modern approach with Osiris so took inspiration from an NVIDIA demo and have implemented a 512x512 FFT using Compute Shaders to do the heavy lifting. This produces a height field, gradient and folding maps that are then fed into the vertex and pixel shaders. The effect is best suited to deep water so works well for oceans - rivers and smaller pools will need something else.<br />
<br />
Like I say I'll go in to more detail later, but for now here are the screens - I'm particularly happy with the way the effect scales into the distance which requires a little work to stop the FFT grid visibly tiling:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgi3zL5yoSz07ek9TDHHgcRCO5he8PDtEvL74v2bMbxBrI1caEA3dZkbIuci0OFpalb0YSSkCkenT_DdugVxwP2gVSA1qVd12Z-ff_VoOuHq_egBB4sD92dpxvMO6OzmNypeUbrg1Vs144/s1600/170112_01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgi3zL5yoSz07ek9TDHHgcRCO5he8PDtEvL74v2bMbxBrI1caEA3dZkbIuci0OFpalb0YSSkCkenT_DdugVxwP2gVSA1qVd12Z-ff_VoOuHq_egBB4sD92dpxvMO6OzmNypeUbrg1Vs144/s400/170112_01.png" width="400" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRRIft-U38MI8NvNDerZuN-Ej67wOKL25uxozw7-6OLRarGETvQMOvIgm26sMDHe_Vvh31zHfdiEs2CCQcl9nnVrkPI5bWlOgFjWIdIQ3KfBV3lx4H1kX19BUMcqgPBVh37JII5mFfjAg/s1600/190112_01.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="207" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgRRIft-U38MI8NvNDerZuN-Ej67wOKL25uxozw7-6OLRarGETvQMOvIgm26sMDHe_Vvh31zHfdiEs2CCQcl9nnVrkPI5bWlOgFjWIdIQ3KfBV3lx4H1kX19BUMcqgPBVh37JII5mFfjAg/s400/190112_01.png" width="400" /></a></div>
<br />John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com4tag:blogger.com,1999:blog-3862349614860735414.post-78323730324409799942012-03-07T09:18:00.000+00:002012-03-07T09:18:51.832+00:00Outerra Tech Demo DownloadI recently found out that the impressive <a href="http://outerra.com/" target="_blank">Outerra project now has a technology demo</a> that can be downloaded to interactively play around with their system.<br />
<br />
I've been following the Outerra project for a while now and the guys there are doing some really inspirational work. I strongly recommend checking it out if you are at all interested in real-time virtual worlds - even though it does make my own humble efforts look somewhat less stellar :-)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.outerra.com/shots/alpha/c1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="72" src="http://www.outerra.com/shots/alpha/c1.jpg" width="400" /></a></div>
<br />John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0tag:blogger.com,1999:blog-3862349614860735414.post-69683212088067965802012-02-27T17:38:00.001+00:002012-02-27T17:38:25.327+00:00The Scale of the UniverseJust a short note this one to point out a link I find very interesting. Entitled "The Scale of the Universe" by Cary Huang it's a fascinating interactive exploration of the relative sizes of all manner of entities in the Universe.<br />
<br />
<a href="http://htwins.net/scale2/" target="_blank">The Scale of the Universe</a><br />
<br />
Maybe it's just me, but I find zooming in and out and clicking on the different entities for extra information strangely compulsive...John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com3tag:blogger.com,1999:blog-3862349614860735414.post-74205813410643182182012-02-22T13:18:00.002+00:002012-02-22T13:18:51.262+00:00Understanding BCn Texture Compression FormatsTexture compression can be something of a black box with many people happy to just "turn it on" to save memory or increase performance and think little else about it, but in practice the different DXT compression formats that have been available in DirectX for years have significant behavioural characteristics that can make a radical difference to the visual quality of a project.<br />
<br />
More recently DX10 and DX11 have brought in even more choice with the replacement of the DXT formats with no less than seven flavours of block compression, conveniently known as BC1 to BC7 making the choice of texture storage format even more significant to achieve best quality visual results.<br />
<br />
While the DirectX documentation on these formats is technically rich, it's not the clearest introduction to the formats and doesn't always make it clear which is best for what purpose and why - fortunately though graphics programmer Nathan Reed has kindly taken the time on his blog recently to fill that gap with a clear explanation of the different BC formats.<br />
<br />
So if you are interested in texture compression or just want to make sure you're making the most of your carefully crafted DX10/11 project's visuals I suggest giving his excellent article a read:<br />
<br />
<a href="http://www.reedbeta.com/blog/2012/02/12/understanding-bcn-texture-compression-formats/" target="_blank">Understanding BCn Texture Compression Formats</a>John Whighamhttp://www.blogger.com/profile/10862563515312224241noreply@blogger.com0