Creating a procedurally generated universe, one algorithm at a time.

Beachballs in space

Posted on Jun 25, 2012 graphics proceduralcontent

Heres a quick post on how I elaborated on a technique I first saw used by Paul Whigham to simulate whirling vortices on gas planets. I wont go into huge depth explaining the technique as he has already done a great job of doing so, but essentially the technique involves creating a number of cones which protrude from the center of a sphere outward. Each of these cones represents a conical surface detail. One then renders a voronoi diagram on the surface of the sphere mapping out for each pixel which cone is closest and encoding this into the textures RGBA data (as shown below).

image

Once this map is rendered, when rendering the object in the pixel shader one can then determine the strength of the nearest cone by sampling the voronoi map texture, and use this to offset the texture lookup to produce a whirl effect.

image

Whigham doesn’t go into detail as to how to calculate this offset, so in the interest of helping others out, here’s the relevant pieces of HLSL that I came up with.

//rotates a vector about an arbitrary axis

float3 RotateAboutAxis(in float3 v,in float angle,in float3 axis)
{
    //http://en.wikipedia.org/wiki/Rodrigues%27_rotation_formula
    return (v * cos(angle)) + (cross(axis,v)*sin(angle)) + (axis * dot(axis,v)*(1-cos(angle)));
}

...

//sample the voronoi map in object space
float4 vornoiSample = texCUBE(vornoiSampler,input.LocalPosition);

//decode the properties from the voronoi map
float radius = vornoiSample.a;
float3 whirlPosition = vornoiSample * 2 - 1.0;
float strength = abs(whirlPosition.z);

//recreate the z value for the cone axis
whirlPosition.z = sqrt(1.0f - whirlPosition.y*whirlPosition.y - whirlPosition.x*whirlPosition.x) * sign(whirlPosition.z);

//find the distance between the current pixel and the intersection point of the cone axis and sphere    
float3 difference = whirlPosition-input.LocalPosition;
float distance = length(difference);

//calculate the strength of the rotation at the given distance from the cone axis.
//the strength diminishes by distance squared from the axis outward
float attenuation = saturate(1.0 - (distance/radius));
float theta = (strength * MAX_ROTATION) * (attenuation * attenuation);

input.Normal = normalize(input.Normal);
//adjust the final cubemap texture lookup by rotating the lookup by theta about the cones axis.

float3 adjustedPosition = whirlPosition - (theta>0 ? RotateAboutAxis(difference,theta,whirlPosition) : difference);

I also found that this technique can be used for any roughly circular surface details. One such is case is generating impact craters on the surface of rocky planets. The general process is the same, except that instead of rotating texture lookups, one uses the voronoi map to adjust the heightmap data to produce circular surface indentations.

image

There are a bunch of other uses I can see this technique being useful for such as adjusting the strength of night time lightmaps on planetary surfaces to create cities, or varying diffuse colors on procedural planetary textures to create ‘biome’ type effects.

Constants are changing

Posted on Jun 25, 2012 design storytelling

As you may have noticed, I finally got around to updating the site to reflect the new aims for the Junkship project. Junkship is no longer an attempt to create a hybrid JRPG space opera game. While these types of games will always remain dear to me, they do so in a mostly nostalgic sense as I have found my tastes and interests moving away from these types of games for a number of years now. I think most of the problems I now have with heavily linear ‘cinematic’ experiences can be summed up by this quote from the suspicious developments manifesto

You can make a movie where people have to press the right buttons to see the next scene, but it’s hard, expensive, and spectacularly missing the point. These things count as ‘games’ in the same way that a wheel on a stick once counted as a ‘toy’, and we’ll look back on them with same tragicomic pity.

Maybe I’m just harder to impress these days, but ‘Epic’ storylines and cool cut scenes just don’t do it for me like they did when I was a teenager. Instead these days I’m floored by games that put you inside amazing dynamic worlds and don’t try too hard to tell you a story, instead they allow you to uncover the story as you interact with the world. So in keeping with this Junkship is now about putting you in the shoes of a freelance interplanetary arms dealer in a large procedurally generated solar system full of various planets, asteroids and rival political factions. The arms dealer aspect of it makes it interesting to me for two reasons, first – unlike most space trader type games you’re not a pirate, the goal of the game isn’t to purchase the best ship and fly about blowing people up. Instead you’re the guy designing, testing, and building the weapons, and choosing which pirates you want to sell them to. For me this emphasis on creativity and ingenuity is more compelling than a combat based treadmill to buy better and better stuff so you can blow up more stuff. Think more Tony Stark, and less doom guy.

image

he second reason is that being an arms dealer brings with it all sorts of interesting moral conundrums. When you are a space pirate, you don’t have much choice in your actions – its kill or be killed. An arms dealer however is in a completely different situation; who you sell your weapons to and why is a personal decision that reflects who you are and what you believe in. It also provides an interesting dilemma in that something you enjoy doing generates misery for many people, so how do you go about justifying those actions?

Obviously things are in the very early stages at the moment, and exactly how I’m going to execute on this vision is still very much unknown. With that said, I’ve largely completed the initial prototyping for the procedural solar system generation, so now its on to designing the weapon design workbench. downloads box2d

Asteroid blues

Posted on Jun 17, 2012 graphics proceduralcontent

Its been a while since the last blog post, however it doesn’t mean that nothing has been happening - quite the opposite, the amount of ongoing work has left no time to keep the blog up to date. I really should try and keep the posts coming on a regular basis or I find myself in my current situation with a massive backlog of things to write up, to help with this I’ve started to use trello to keep my work organized – so we’ll have to see how that works out .

So anyway, as promised here’s a dev diary on generating and rendering procedural asteroid fields. The basic idea for this was to use similar techniques I’ve used in the past to generate planetary textures using perlin noise to generate color and normal maps, but instead of applying it to a sphere applying it to some arbitrary (or seemingly arbitrary) geometry.

In my case I decided to use a sphere as the base for the asteroids geometry as opposed to using some combination of 3d noise and marching cubes. The reasons for this were twofold, firstly the sphere based approach was much simpler, and secondly most asteroids are vaguely spherical in shape anyway. Given a sphere its a relatively simple matter to use a 3d perlin noise function to move the spheres vertices up or down their normals in order to generate smooth deformations in the sphere surface.

I ran into some initial problems with this approach however as I was using the built in D3DXSphere primitive which uses a series of triangle strips to build a sphere. The problem with this is that the vertices are not evenly distributed as the smaller strips near the poles have much higher density than those near the equator, this leads to the areas near the poles appearing ‘crinkled’. The solution is to use an icosphere which is generated by continually subdividing an icosahedron to approximate a sphere. An icosphere has an even distribution of vertices which fixes the issue.

image

On the left, an icosphere – On the right, a sphere built up using strips.

image

An initial render of the asteroid geometry using the skin from an earthlike planet.

Rendering a single asteroid is one thing, but in order to create a convincing asteroid FIELD, its necessary to render hundreds or thousands of asteroids. In order to make this performant I utilized hardware instancing to re-use the same asteroid object, but to apply thousands of different rotation/scale/translation matrices in order to give the appearance of many different asteroids without the overheads of having thousands of unique textures and models. In practice I could use a handful of models and textures rather than just one to increase the variety a bit, but even as is the results are pretty good.

image

image

Let men burn stars

Posted on Dec 30, 2011

As promised last post, here’s a brief dev diary entry on creating random starfields and nebulas. After generating a passable looking planet the next step is to generate some cool looking  space for the planet to exist inside, and for that you need a couple of things.

Firstly you need a starfield. for this I created a spherical point cloud of star sprites centred about the camera. Getting a good even distribution of stars was a little trickier than I first thought it would be but I found a good algorithm for evenly distributing points about a sphere as shown below

StarParticle* p = 0;
HR(field->VertexBuffer->Lock(0, 0, (void**)&p, D3DLOCK_DISCARD));
for (int i=0;i<starCount;++i)
{
    StarParticle sp;
    float u = (((rand() % 10000)/(float)10000) - 0.5) * 2.0;
    float theta = ((rand() % 10000)/(float)10000) * 2 * D3DX_PI;

    float distance = (rand() % (int)(maxDistance-minDistance))+minDistance;
    //uniform distribution of points about a sphere http://mathworld.wolfram.com/SpherePointPicking.html
    sp.Position.x = (minDistance+distance) * sqrt(1-pow(u,2)) * cos(theta);
    sp.Position.y = (minDistance+distance) * sqrt(1-pow(u,2)) * sin(theta);
    sp.Position.z = (minDistance+distance) * u;
    sp.Size = (float)starDesc.Width * (1-(distance/maxDistance));
    p[i] = sp;
}
HR(field->VertexBuffer->Unlock());

I also generate the star sprites procedurally by setting a size and start color in the center of a square, then as distance from the center increases, exponentially decaying the opacity. This produces sprites that look like this

image

When put together you get something like this (Note: I render the starfield’s into a cubemap skybox so that there’s no need to regenerate the skybox every single frame)

image

Now this is all well and good, but it does look very dull, and its also very hard to orient yourself as there are no recognizable landmarks. To fix this I added a number of extra detail layers. The first of these was two smooth background noise layers to add some lightness and variety to the background.

image

image

Finally I added a few layers of brighter stars of different colors and extra background star layers that were masked such that they were most visible around the nebula clouds to give the appearance of greater star density in the nebulas.

image

The results aren’t going to fool anyone into thinking its a Hubble photo, but I think they do look quite good. One thing that I haven’t yet implemented is a random palette picker to pick varied, yet complimentary colors for the nebula clouds to further increase variety. The palette picker can wait however as the next entry will be on the subject of generating random asteroid fields :)

Building steam with a grain of salt

Posted on Nov 13, 2011 graphics design proceduralcontent

Its been an insanely long time since this website has had an update, but I guess its better late than never. Anyways, about 3 months ago Junkship underwent a significant design refresh that was prompted by a few key factors. The first of these was the realization that as a small indie team, the scope and in particular the content creation required for the original Junkship concept was not really feasible. The second factor was a general disillusionment with hand crafted narratives and stories in games, and the desire to try a novel approach to narrative, and the third and final factor was an awesome game concept demo by Luke Smith that signalled a new and interesting direction that we could take Junkship in.

While I’m not going to go into any detail as to the specifics of the new gameplay (much of it is in flux anyway), one new aspect is that the game world will now be much more of a sandbox with an emphasis on procedurally generated content. The advantage of this are twofold, a sandbox arrangement allows the player the freedom to create their own narratives and stories, and procedural content allows the experiences of the player somewhat unique as well as reducing the content creation workload for us :)

So in order to facilitate this vision I’ve started off down the path of generating procedural space environments, and in fact the purpose of this post is to show the progress I’ve made thus far. The end goal is to procedurally generate solar systems with tens of planets, hundreds of moons and many thousands of asteroids, each of which have settlements and trade routes etc between them. While this is a fair way off, this end goal is simply the aggregation of a number of base content generators (a starfield generator, an earthlike planet generator, a rocky planet generator, an asteroid field generator, etc.)

I decided to start with the task of writing a generator for procedural earthlike planets. The basic idea was to use improved Perlin noise to generate a heightmap, which combined with a color gradient and a known sealevel I could produce a decent approximation of a planet with water oceans and continents. Below is the first iteration of this idea. To produce the terrain I used two layers of blended noise, the first was for the base continent terrain, and the second was a layer of ridged noise to produce mountain range like features. I also settled on using HLSL shaders on the GPU to do the heavy lifting as far as generating the terrain as the CPU proved too slow (10 seconds + per planet on the CPU compared with milliseconds on the GPU).

09-09-2011

While the above screenshot is all well and good its got a long way before it looks properly earthlike. The next step was to calculate terrain normals from the generated heightmap to allow bumpmapping, as well as calculating specular terms for the water and terrain. Generating the normal map was relatively simple given that I already had all the height data and just had to record the height differences between adjacent texels to get what I wanted. The hardest part getting the bump mapping to actually work was due to me foolishly recording the bumpmap vectors in world space rather than the usual tangent space.

19-09-2011

After producing a bumpmap like the one above (I also included a specular map in the alpha channel of the bumpmap, which you can’t see in the above image) I could now produce planets that looked like the one below.

20-09-2011

The main problem I now had to overcome was to make sure the terrain had realistic variations in color. After staring at pictures of earth for hours I identified a few main techniques I could use in order to improve the terrain. The most obvious addition I could make was to ensure that the poles and areas with high altitude were snowy (and have a high specular value for more shininess). A more subtle variation on this is that forests and jungles are more common at lower altitudes and closer to the equator, with deserts being more common at higher altitudes and more extreme latitudes. Finally I could also vary terrain color based on the steepness of the terrain itself. By using two square color gradient textures and sampling/blending between them using the heightmap/normal map/texel latitude I was able to produce much better looking terrain.

22-09-2011

The main feature I was now missing was clouds. I implemented these using two noise fields which I would additively blend together using a varying offset to simulate changing clouds over time. Getting the noise to look right was relatively easy, however I found the clouds really didn’t look right until they cast shadows on the terrain below them, once I did this the results were really good (especially when you see them changing with time).

03-10-2011

One last thing I wanted to add was city lights on the night side of the planet (assuming of course that the planet is inhabited) to do this I used another noise map similar to the cloud layer and prioritized the intensity to areas more typically inhabited (i.e. near the coast and in the more comfortable latitudes).

04-10-2011

After putting it all together…

1-11-2011

Satisfied with the results so far, my next challenge was to generate random starfields and nebulas (of which the previous screenshot shows an early version), but that will have to wait for another post.