Height Sampling
Orbis supports both procedurally generated terrains based on noise samples, and heightmap terrains.
Procedural Generation Sampling
To set a terrain to use procedural generation, add a ProceduralHeightGeneratorSettings component to the terrain. This component has three properties:
- Generator ID: The id of the generator that is being used to generate the terrain. For spherical terrains, all 6 sides should use the same height generator to avoid cracks at the edges between the sides.
- Scale: The maximum height of the terrain
- Frequency: How high/low the noise frequency should be. Higher values result in more tighly packed mountains and features, while low values make the terrain feature appear more scaled down.
Adding a new generator
There are a few predefined sample procedural generators in the project, but most project most likely want to implement their own procedural generators. The generator that is being used is defined by the GeneratorID property. To add a new generator, some coding knowledge is required. New generators can be added in the ProceduralHeightGenerator.cs class. This is a static class that is being utilized by the mesh generation job, and is fully burst compiled. This means that all code that is added should be kep burst-compatible, otherwise the generation performance will be much lower.
To add a new generator, two new methods have to be added to this class, one to sample heights and one to sample biome values. They should conform to the following prototype:
private static float yourBiomeValue(in float3 pos, in ProceduralHeightGeneratorSettings settings);
private static float yourHeightValue(in float3 pos, in ProceduralHeightGeneratorSettings settings)
Next, add your two new methods to the switch statements located inside the getProceduralHeight() and getProceduralBiome() functions, with the case parameter being a new ID for your generator.
Inside the noise function you’ll want to use some kind of fractal noise in most projects. A sample, burst and job compatible fractal noise sampling function is provided inside the class:
private static float getFractalNoise(in float3 pos, int octaves, float frequency, float amplitude)
This function uses the simplex noise function provided by the Unity.Mathematics package. Note that this is quite performance intensive, and especially more octaves can result in longer mesh generations times and warnings in the editor.
Predefined generators
Orbis includes 3 procedural height generators as example implementations:
- Simple Generator (ID 0): Uses a plain fractal noise with 7 octaves. Results in a hilly kind of terrain without any special features.
- Advanced Planet Generator (ID 1): This generator uses a base elevation mask to define areas which are more flat or have more mountains. Also mixes in ridged noise inside the mountains.
- Warped Generator (ID 2): The warped height generator is using warped noise, where the sampling position is being offset by a noise value, resulting in more organic looking shapes. Also includes a mountain mask to make some areas higher and others lower
Heightmap Sampling
To have planets and flat terrains have more sophisticated terrain layouts, height maps can be used. They include heights for each part of the terrain and have to be prepared before use in external programs. This allows the terrain to have erosion algorithm and manually designed features included. Heightmaps are also faster than most procedural height generators, since the heavy calculations have been performed beforehand. However, they have to be kept in memory constantly, and their resolution is limited, resulting in very large terrains possible being low-res when the camera is close.
To make a terrain use a HeightMap, a HeightMapSettings component is required. This scale of the heightmap can be modified in this component, and the ID has to be set. The heightmap is assigned in the OrbisInterface component, and the ID in the OrbisInterface component has to be same as the ID in the HeightmapSettings component.
Note that the reading and converting the heightmaps into an internal format is done in a job at the begin of the script execution. Depending on the size of the heightmap, this might take a few frames. All other Orbis systems will wait until that process is done. You can check if the heightmap processing has been completed by using the following public static field of the OrbisInterface component:
public static bool initializationDone;
There also is the option to smooth heightmaps when importing them, allowing for lower-res heightmaps or heightmaps with less than 16-bit precision to still look smooth. When there are noticable terraces in your terrain, you might need to use a higher smoothWidth for the heightmap. Avoid using values higher than 9, as this can drastically increase the import and conversation times.
Heightmap formats
Orbis supports heightmaps of any size, as long as they are multiples of 2 and have the same width and height. Heightmaps are stored internally as an array of ushort, allowing for up to 16 bits of precision at each pixel. To fully utilize this precision, make sure to edit and store your heightmaps as grayscale textures with 16 a color depth of 16 bit.
When importing heightmaps, their texture type has to be set to single channel in the import settings, and the channel has to be set to red. The Read/Write property has to be enabled, and the format needs to be set to R 16 bit.
Note that expecially high-res heightmaps can take up a lot of memory. While a 1k resultion height map only needs 2 MB of memory (1024^2 * 2 bytes = 2097152 bytes), an 8k heightmap needs 128 MB RAM (8192^2 * 2 bytes = 134217728 bytes)
Sampling Algorithm
Heightsmap are used inside the mesh generation job to offset the vertices of the terrain. Since the heightmap resultion usually doesnt exactly match the vertex position, the heights for each vertex are sampled bilinear between the closest pixels to the vertex position.
Spherical Heightmap Alignment
For spherical terrains, six heightmaps are used, one for each side of the cubesphere. To avoid cracks between the sides, the heightmaps needs to precisely line up with each other. Note that simple tilable textures are not enough, they have to be carefully crafted beforehand so the correct sides of each texture line up. See the image below for how the heightmaps are aligned. In the demo scenes, there also is a scene called “CubeHeightmapLayout”, where you can test if your height maps all line up. However, not all sides are connected on a flat terrain, there are sides that are only connected when the heightmaps are assigned to a spherical terrain.
The heightmaps are applied to a cube, so for example the “front” and “left” tile are also connected, which is impossible to show on a 2D-Image.
Scattered heightmap sampling
Scattered heightmap sampling is a semi-procedural approach to heightmaps. It randomly scales, rotates and positions heightmaps over the terrain. Each point on the surface is influenced by 4 nearby “heightmap center points”. The area after which another random heightmap is used is based on the Frequence parameter of the configuration component.