Sunday, January 25, 2015

Voxel Terrain Generation and Rendering

This time I'll present 3DWorld's voxel terrain system.

I'm generating a 3D array of noise values using different noise functions, computed either on the CPU or on the GPU using a custom fragment shader. The current set of noise functions supported are Perlin noise, Simplex noise, and sum-of-products-of-sines noise (of my own invention). They all seem to produce the same sort of lumpy terrain; in fact, the only real difference is computation time. Naturally, it's faster to generate noise values on the GPU, though reading back all that floating-point data takes around half the time of the CPU noise generation. This inefficiency is partly because I have to generate the volume in 2D slices, to match a 2D frame buffer. If anyone manages to invent a 3D frame buffer then let me know - or I suppose I could give in and use Nvidia-specific compute shaders or CUDA. In the end, it only takes 200ms to generate 16M noise values (64 slices) for a 512x512x64 scene, which comes to 64MB of data.

A big array of noise values is great, but it needs to be rendered. I chose to use the Marching Cubes algorithm because it's fairly simple (compared to the alternatives) and the crazy lookup tables needed for MC can be found online. There are some ambiguous cases in MC, but they tend to not show up often in the scenes I create and I prefer simplicity and efficiency over perfect quality, at least for now. So I can use MC to get triangles, then connect them together and compute the shared vertices so I can render with indexed triangles. This seems to be the most efficient way to render large triangle datasets using OpenGL, and is more compact than storing individual triangles. I divide the scene up into XY tiles (I use Z=up). 16x16 tiles seems to work well, and this division gives some nice benefits:
  • Each block can be generated independently, so I can use openmp to run separate threads on each core. 8 threads (4 core + hyperthreading) gives me about a 5.5x speedup.
  • View frustum culling can be used to drop invisible blocks when rendering
  • Individual blocks can be modified, which is fast enough for realtime voxel editing
Of course I skipped a few steps here, including handling of the mesh boundary, clipping the bottom of the volume with the heightmap floor, removal of disconnected "floaters", etc. This is all pretty standard stuff so I won't go into all of those details. One more important thing is lighting: these voxel scenes just don't look right with simple direct lighting. They need real ambient, and fortunately it's easy to compute ambient occlusion by ray marching through the 3D volume and tracking the visibility of each voxel (again using openmp). I also run path tracing on the triangle mesh like in my previous lighting post with the Sponza scene screenshots. This gives multiple bounce indirect lighting from the sun, moon, etc. for each voxel, which is recomputed (using openmp) when the light sources change.

Here is a screenshot of a 128x128x128 voxel rock/mountain that uses triplanar texturing with different textures + procedural texture selection to give it a variety of rock/grass features. I also placed some dynamic grass blades on the top surfaces of the rock. The shadows and ambient occlusion really bring out the dark crevasses in the rock and make it come to life.
Voxel mountain covered in grass, with indirect lighting, ambient occlusion, and shadows.

Here is another screenshot of a 512x512x64 voxel snowy/icy landscape. The normal maps on the surfaces add nice specular highlights to the snow. The ambient lighting and shadows give depth to the scene, and the indirect lighting is responsible for the blue vs. white coloring. The blue areas are mostly lit by the sky but the white areas include indirect reflections from the sun on the snowy ground and icy walls. I have the indirect term set higher than what is realistic to make the indirect lighting stand out.

If you look at the ridge line on the right, there are some strange cube shapes placed there. These are the results of my voxel editing tests on this scene. I have implemented realtime addition and removal of volumes using different brush shapes (sphere, cube, etc.), sizes, and weights. These brush strokes can be saved to files and re-applied when the scene is loaded after the procedural generation is finished. I decided to store the brushes instead of the raw volume data because it allows for tiny file sizes. It would take a huge amount of time for a user to accumulate 64MB of brush strokes to make the save file exceed the size of the floating-point volume data!

Procedurally generated ice/snow caves + user edits with indirect lighting, ambient occlusion, shadows, and normal mapping.

Here is a zoomed out view of the entire scene - there is a surprising amount of data here! You can easily get lost walking around in this cave system, which is almost entirely connected and walkable. This is 16M voxels, which is converted to around 2M triangles for rendering. I'm getting a nice 300 FPS here with all the effects turned on, even though the camera is too far away to see the details. Oh, and that round hole in the bottom left is a tunnel I made horizontally through the entire scene.

Zoomed out view of voxel ice cave scene.

And finally, that same scene with more interesting lighting: 100 moving, light emitting, colored spheres. This time the sun and moon are both below the horizon for a nice dark night, which makes the colorful light sources stand out. The normal mapping produces some nice specular reflections, though they're a little hard to see at this view distance. I'm still getting a high frame rate of 130 FPS here, even with all the lighting computations for 100 lights.

Ice caves scene with 100 dynamic colored lights.

If I was more of an artist, I would show you some cool screenshots of what I created with the voxel editing tools. However, I was never really into art, and the procedural terrain looks pretty good to me. I'm hoping that I can integrate voxel content into other 3DWorld scene types in the future. 3DWorld already supports the addition of non-voxel polygon objects into voxel-based scenes. For example, I can put the Sponza atrium model on top of these voxels or inside of a large cave. The first person shooter "smiley killer" game can be played in these caves (if you could ever find something to shoot at in the maze). [Yes, I know, I need to post something about that game here since it's not just an engine.]

Well, that's about it for this post. I'll try to put some more screenshots of cool stuff I created with voxels into a later post. I'm sure there's a lot I can do with 3DWorld's voxel framework.

Sunday, January 4, 2015

Procedural Universe and Planet Generation and Rendering

It's time to introduce the "universe" mode of 3DWorld, which is quite a bit different from the other "terrain" modes. This mode doesn't share too much of the high-level classes, functions, and control flow with the rest of 3DWorld, but it does share a lot of the low-level infrastructure. It can also be used to draw a real, animated nighttime starfield (with planets, moons, rings, etc.) in the background of terrain mode.

Universe Objects
There are two classes of objects in universe mode: procedural, natural universe objects and dynamic, manufactured objects such as space ships, their projectiles, and their particles. The procedural universe objects are generated in a hierarchy of cell -> galaxy -> solar system (with star) -> planet -> moon. Each cell contains several galaxies and is identified by 32-bit integers for {x, y, z} grid positions, so the universe is effectively a 3D grid of 2^32 cells, and contains some 2^98 galaxies. I call that universe infinite - in fact any procedural world where you can move at max speed in the same direction for a lifetime without reaching the end is more of less infinite in size. The objects in the universe have compressed scales so that you're not flying for hours in empty space to get from one planet to another, or one star to another. Even with the compression, there are still issues with floating-point precision, so I had to use custom float + int coordinates in some places, and double precision in others.

The universe contains the following physical objects at various levels of the hierarchy:
  • Stars (one per system, up to 500 per galaxy)
  • Nebulae (one or more per galaxy) - volumetric with 3D ridged perlin noise
  • Asteroid fields (multiple per galaxy, either inside or outside of a system)
  • Asteroid belts (in some systems and around some planets)
  • Planets (up to 15 per system)
  • Moons (up to 8 per planet)
  • Comets (spawned near the player, out of view)
Everything is procedurally generated, more or less from scratch. Since the player can travel between star systems in a fraction of a second in hyperspeed, everything needs to be generated within a single frame. In the end I had to move almost all the generation and rendering into large shaders up to 600 lines of GLSL code. So far I haven't been able to find any other online tool/product/demo that draws an entire planet from scratch using a single shader in a single pass. Well, it's not exactly a single shader - I have a shader generation framework that takes bits and pieces of GLSL code and combines them together to create a shader customized for the particular class of planet, moon, etc. being rendered. So it's one shader/pass per planet, but different shaders for each type of planet or moon. The types of planet that 3DWorld supports consist of:
  • Terran/Earth-like: Procedural ground/vegetation color, normal map, water, snow/ice coverage, clouds with shadows, and atmosphere
  • Alien: Colorful terrain, toxic clouds and atmosphere with shadows
  • Ocean/Water: Clouds and atmosphere
  • Ice: Gas clouds/atmosphere
  • Rocky: Procedural height generation in vertex shader, normal maps at multiple octaves, shadows
  • Hot/Lava: Lava instead of water, possible toxic atmosphere, normal maps, colored
  • Gas Giants: Multiple layers of procedural clouds, perturbed 1D color bands, animated procedural cyclone storms, soft cloud shadows
  • Moon: with procedural heightmap and craters generated within the shader using normal maps
Each planet can also have rings that cast and receive proper analytical soft shadows, asteroid belts, and one or more moons that revolve around it. All objects rotate and revolve according to the laws of planetary physics. In addition, each object is given a unique generated name.

In addition to creation and rendering, 3DWorld provides a framework for physics simulation, collision detection, and modification of the universe. There are query functions for gravity, temperature, and lighting at any point in the universe. 3DWorld supports efficient object line intersection tests, sphere intersection tests, nearest object queries, future collision queries, and other functions necessary for interacting with the universe. Objects can be destroyed and renamed by the player, and modifications can be saved to disk and reloaded in future sessions.

Here are some screenshots of planets and other universe objects, all procedurally generated and rendered with custom shaders. There are no predefined textures used, all planets are unique, and I'm getting framerates of hundreds of FPS. Note that the planets are closer together than they would be in reality, to make the scene look more interesting with multiple planets in view at the same time.

Volumetric procedural nebula that the player can fly through, using 3D ridged perlin noise. Each one is unique.

Star with asteroid belt and nearby planets, with stars, galaxies, and nebulae in the background.

Closeup of system asteroid belt with 10K large rotating asteroids (perturbed spheres with hardware instancing and LOD) and 1M small asteroids (point sprites). All asteroids rotate around the star, and can be collided with, selected, and destroyed.

Gas giant with swirling animated clouds, animated storm cyclones, cloud shadows, lightning, rings with animated asteroids and particles, soft shadows to/from planet <=> rings, and indirect moon lighting.

Terran planet with procedural terrain (8 octave 3D ridged noise multifractal), normal mapping, snow covered mountains, ice at poles, animated swirly clouds with shadows, specular snow, ice, and water, and atmosphere.
Icy planet near the asteroid belt with some snowy peaks and green forested areas. Note the moon in the back left.

Planet with clouds, toxic atmosphere, and emissive lava glow (black body radiation). You can also see a distant comet in the upper right corner.

Rocky ice planet with nearby asteroids. The vertices are perturbed by evaluating noise in the vertex shader. All planets of this type use a constant/shared VBO of initial sphere vertices.

In the other category we have artificially created objects consisting of:
  • Space Ships
  • Starbases
  • Planetary Colonies
  • Projectiles (missiles, etc.)
  • Particles and Particle Effects
  • Explosions
This is the space gameplay part of universe mode, where the player engages in colonization and fleet combat with one or more AI-controlled sides. All of these objects are either initially placed in the config file, randomly spawned, created by the player, or created by an AI. We start with the ships, which can create other ships, colonies, projectiles, and particles. All of these objects can interact with universe objects (stars, planets, moons, etc.) and with each other. I'll make a later post with more ship details and screenshots.

Video Update

Here is a procedural universe fly-through recorded by 3DWorld. I fly my ship away from the battle, past a planet, into the asteroid belt, past the star, to a ringed gas giant, back and forth through a nebula, and back into a system to view a planet up close.

YouTube Video Link

At one point I fly my ship into an asteroid and bounce off, which shows how physics and collision detection work with every solid object in the universe. I also get too close to the star at one point and start taking damage, which is why the screen turns red and shakes. (I have the health bar and other UI elements turned off to reduce screen clutter.) The nebula is procedural, which allows the player ship to fly though it. All planets, including the gas giant and Terran planets in the video, are entirely generated and drawn on the GPU. I'm only sending a low-resolution, untextured sphere to the GPU for tessellation and rendering. Everything is generated seamlessly with no visible LOD transitions and no loading screens.