Home       About Me       Contact       Downloads       Part 17    Part 19   

Part 18: Procedural Landscapes

May 1, 2011

Well, I still have tons of issues, big and little, in the various ports of the project. At this point, I feel like it was a mistake trying to support multiple platforms, and switching to OpenGL with the variations in its shader language and driver support. One commenter two parts ago called it "batshit insane". It has used up a lot of the enthusiasm I had for this project.

To fix that, instead of doing things off the ToDo list, I went back to working on the world design. In particular, I wanted to implement my ring of asteroids.

If I wanted something sensible like 10,000 asteroids, I could just generate the coordinates using a random number generator with a fixed seed, or precaclulate the coordinates and store them in a file. All players would see the same set of asteroid positions. But I wanted a "nearly infinite" sea of asteroids. To do that, I need to create them procedurally.

What this means in practice is that I have a function that takes as input (x,y,z) positions. It produces as output some value I'm going to use to generate an asteroid at that point. This technique is very general. If we were doing a landscape, input might just be (x,y) and output would be terrain height. A second function might output terrain type (land, water, snow, etc.) at each point.

As the player moves, we are continually calling this function to generate new terrain. This function needs to produce the same output for each input (x,y,z) so that terrain will be the same for all players, and the same for a single player when he returns to a place he's already seen. I'm using Simplex noise again.

Fig 1: Some simplex noise
Default noise

I wrote a simple program to display my noise function. The left side is the view from above in (x,z). The right side is the view from the side (x,y). I added pan and zoom functions on the mouse, and put the current scale at top-left. In this view, we are looking at a region 40,000,000 units wide. The very large range of inputs is more than the simplex function can handle, so it repeats as you can see. In practice, this won't matter, since the scale is so large.

Next, I multiply this noise by two functions. The distance from the center is fed into another Simplex noise function to get rings. The distance on y is used to restrict the depth of the ring.

Fig 2: A ring of noise
ring of noise

Zooming in on this, we can see more detail in the rings, and get a better idea of the thickness.

Fig 3: Details of a ring
details of a ring

I threshold this to get a final set of asteroid positions.

Fig 4: Asteroid positions
Asteroid positions

A tighter closeup lets me judge whether the density is right. The red line at the bottom-left is 20,000 units. I had earlier decided that would be a good distance between asteroids.

Fig 5: A local group of asteroids
A local group of asteroids

Next, I take this function and write an "asteroid object" in the game world. As you move, I'm checking nearby points in space to see if they should have an asteroid. I do this by calling the noise function I've just built. I don't call it at every point. Not only would that be expensive, but I don't want asteroids to run into one another. If I limit their radius to 1200 units, and call the noise function only at spacing of 4000 units, two asteroids cannot intersect.

The result is shown in a video, here. Watch carefully and you can see the asteroids appear in the distance. I've painted them blue because the grey asteroids were harder to pick out against the stars.

Using the noise function, I've generated a solar system with millions of asteroids. All of these have stable positions and can be revisited or seen by multiple players in the same positions.

Next Step

In the video, all the asteroids are the same size and shape. My next step is to use a noise function to generate each asteroid, making them all different. The logic of that will be similar to what I did here, but instead of just getting a yes/no for whether an asteroid is at a point, I'll create a real terrain.

The final game has a set of objects it displays, based on distance, from near to far:

  1. The avatars, objects and buildings, made of cubes.

  2. The nearby terrain, which should be modifiable, so you can dig foundations for your buildings, etc. This will be polygons, to give it a more natural look than a cube-based landscape.

  3. The distant buildings, summarized with points instead of cubes.

  4. The distant terrain, drawn as low-resolution procedural shapes. Any user-created edits would be missing in that version.

  5. The nearby asteroids, procedurally generated with limited detail. Still, each nearby asteroid should be different. I can also generate a texture based on the current state of construction on that asteroid. The texture would be updated in the background, not in real time.

  6. The sky, which consists of the ring, sun, nebula and stars.

There's still a lot to do to implement all of this, and make it display smoothly. Aside from coding the objects themselves, there are some subtle issues.

For example, my "universe" is 40 million units across. I don't want coordinates this large in my transform matricies. I will have float overflows or loss of precision in odd places. So I need to separate "universe coordinates" (20,000,000) from local coordinates (2,000). I can do this by generating objects relative to the eye instead of in absolute coordinates.

Even then, the distances to the local asteroids are large (100,000 units or more). To avoid this, I can scale down asteroid coordinates and sizes to keep them in a reasonable range. Remember that we are still using the Z-buffer to keep distant objects obscured by nearer ones. There is a limit to the size of value I can put in the Z-buffer. Making a large coordinate space (high value for back plane distance) reduces the resolution of the buffer.

World Design

I still have an issue with the world design. As I mentioned in previous parts, you can't actually see a ring of asteroids from within the ring. If I do this accurately, I get something like this:

Fig 6: An accurate ring
An accurate ring

I can force the ring to always be below your eye, to give the asteroids some context. That's completely inaccurate and it kind of gripes me. I can't think of anything else to do though if I want this kind of solar system.

I'm also feeling kind of underwhelmed by the look of these asteroids. It's just not very interesting. I can make all the asteroids different, but it's still not a landscape I want to explore. Even original Minecraft, with its endless little bays and hills, seems more inviting.

I was thinking that any procedurally generated landscape or solar system is going to be a bit bland. There just isn't enough variation in these noise functions. But I have to show you one weird variation I got while playing with the code:

Fig 7: An interesting bug
An interesting bug

This wouldn't work as an asteroid field. For one thing, the scale is so huge that you can't see these details. In my current version, despite putting gaps between the rings, they are really hard to find when flying around. But this does make me think there are more interesting procedural landscapes out there to be created.

I may end up trying some completely different concepts (I have a list...) No matter what I design though, I'm going to be using these same techniques, and the same classes of objects and order of display.

Stay tuned.

Home       About Me       Contact       Downloads       Part 17    Part 19   

blog comments powered by Disqus