Creating Landscapes - jgoffeney/Cesium4Unreal GitHub Wiki

Back

World Partition

NOTE 1: for the current instructions I recommend first creating the landscape with a height map file and then enabling world partition. There is a different expectation for the format when adding a landscape to a level with world partition already enabled.

NOTE 2: set the material for your landscape prior to converting the map. This will automatically apply the material to each partition. The material does not have to be completed but otherwise you will have to apply the material to each partition yourself.

To enable World Partition (as of UE5 Preview 2) for a current map:

  • Go to Tools->Convert Level
  • Browse to select the current map file and press Open
  • When you press Ok a command window will launch to run the conversion application.

ConvertSettings

Issues

  • When starting the level I get the message "Landscape Physical Material Needs to be Rebuilt".
    • Problem: no idea what issue this causes.
    • Solution: In the world partition window select and then load all the cells and then run Build->BuildAllLevels.
  • After running Build->BuildAllLevels I get the message "Static Lights are not supported"
    • Problem: Lumen is enabled by default and it does not support static lights.
    • Solution: this appears to be a bug as of Preview 2. Don't use static lights.

Creating a Landscape

Within Unreal change the mode to Landscape mode.

SelectLandscape

Importing Terrain

To generate a landscape based on a height map.

  • Select the Import From File tab.
  • Under Heightmap File use the browser button to find the source height map PNG file.
    • For a supported resolution it will automatically fill other fields with related values.
  • Set Scale
    • The default for Unreal is 100 cm resolution.
    • For scale X and Y set it to the actual resolution in centimeters. For this example I used 1/2 meter data so they are set to 50.
    • For Z scale you have to use the following formula for the correct value. It transforms the PNG values for the elevation to Unreal space.
      • When you create the heightmap it is scaling and storing the elevation values in 9 bits (or 512 values) so you have to convert them back by computing the elevation range and dividing by 512 to get the correct elevation scaling for the landscape.
      • range(cm) = maxHeight(cm) - minHeight(cm)
      • ZScale = range(cm) / 512.0
  • Press Import and return to Select Mode.

ImportLandscape

Multiple Heightmaps

Unreal supports automatic mosaicing of heightmap tiles with a single landscape or individual tiles in multiple landscapes or a mix of both.

Landscape Mosaics

  • When converting elevation data to heightmaps use the overall minimum and maximum elevation values to scale the data when creating heightmap PNG files.
  • End the files with xM_yN.png where M and N are the tile indices starting at the upper left of the region
  • Place the PNG files within their own directory. When Unreal finds multiple PNG files in a single directory it will attempt to load them together in a single landscape (which probably has the same resolution limitation). If the files do not have the xM_yN.png naming convention then it will cause an error.

Multiple Landscape

You can add multiple landscapes to a level. You will need to have each heightmap (or set of heightmaps to mosaic) within its own directory. If your material requires specific parameters for the landscape then you will need to create a material instance before creating the landscape.

When a new landscape is created it will be placed in the center of the scene and you will need to move it. I found the X and Y coordinated are flipped from what I expected based on the source image dimensions.

Landscape Materials

References

Material Sources

When you load your height map it will display the actual dimensions of the final landscape near the bottom of its details. If you use the same dimensions as your height map for source images (such as satellite or classification images) then it you will have not have enough to cover the entire surface. Use the correct value provided by Unreal.

LandscapeResolution

Texture Formats

Unreal textures derived from images can be RGB or greyscale. The data is automatically scales from 0 to 1.0 based on a max size byte value of 255. If you need the original values when scale it back up.

NOTE: if you are working with single band geotiffs and they are paletted then the values will be read based on the mapped colors and not the original greyscale values. The following code will open the geotiff and remove the color table.

ds = gdal.Open(sourceFilePath, gdal.GA_Update)
ct = gdal.ColorTable()
ds.GetRasterBand(1).SetRasterColorTable(ct)
ct = None
ds = None

LandscapeCoords

In your material when you use LandscapeCoords its Mapping Scale is set to the resolution of the heightmap by default. Set it to actual landscape resolution to sample textures over the entire landscape. For example the default for my landscape was 4033 which caused the textures to stop before completing the entire landscape. Setting it to 4081 corrected the issue.

LandscapeCoords

Resize Sections

Larger section sizes increase the density of the terrain near the camera and is done by:

  • Select Landscape Mode with an existing landscape.
  • Select the Manage tab
  • Select the Resize button
  • Under Change Component Size->Section Size select the option 255x255 Quads.

Mask On Slope

The inputs VertexNormalWS and PixelNormalWS finds the corresponding normals for a given position. By masking on the B channel you get the upward Z component. Flat areas will have larger values than upward slopes. One of its uses is to blend between different textures so something like a mountain path will be mainly dirt while the hill is rocky.

Mask On Height

  • Get an input Absolute World Position which provides the world coordinate of the pixel currently being processed.
    • Under UMaterial Expression World Position->Shader Offsets select Absolute World Position (Excluding Material Shader Offsets)
      • Prevents the position from being altered by any shader operations.
  • Get a Component Mask node and select the B channel to get the Z coordinate.
  • Add a SmoothStep node which transforms an input value from 0 to 1.0 based on a supplied range.
    • Connect the Z coordinate and the Unreal height range and you get a value useful for an interpolation alpha.

Procedural Vegetation

To automatically apply vegetation to a landscape first add a LandscapeGrassOutput node to your material. Contrary to its name it is not specifically for grass. Instead it can apply other vegetation types (and even just other meshes).

Adding Vegetation Types

Only a single LandscapeGrassOutput node can be added to a material but it can support multiple vegetation types. In the node's Details Grass Types is an array and you can add extra entries at adding elements to the array.

GrassTypes

The Grass Type for each element requires a Landscape Grass Type which is created by creating a new component as Foilage->Landscape Grass Type. Create a folder such as Vegetation and create a Landscape Grass Type component for each type. Opening the component shows that Grass Varieties is an array type so you can add an element for each plant you want to add to the mix. This allows you to create a heterogenous mix of plants nearby but also by playing with the culling distances you can set up lower resolution meshes for plants farther from the camera.

Some important parameters:

  • Grass Mesh: the mesh model
  • Grass Density: the density of spawned plants. Lower to improve performance.
  • Start Cull Distance: the distance in centimeters to start culling vegetation
  • End Cull Distance: the distance in centimeters to end culling
  • Scale: a cheap way to improve performance is to lower density while scaling up the plants.

LandscapeGrassType

Landscape Layers

Landscape layers provide a way to pass inputs to a landscape material from the landscape object. Internally you can use the layer inputs as weights for blending layers based on precomputation. For instance you can compute the slope within a shader or you can just generate a slope texture outside of Unreal and pass it in.

A couple caveats:

  • The LandscapeLayerSample needs to be included in the material prior to creating the landscape.
  • The inputs have to be in the heightmap format like PNG or RAW. I have TIFF inputs so that would be an extra conversion step.

References

Creating

Within a material you can add a LandscapeLayerSample node which will output a value from 0 to 1.0 for the current pixel location. Each node will appear in your landscape under the Paint tab. Beside each layer entry is a + symbol to add a Weight-Blended Layer or Non Weight-Blended Layer which will be created as components. Once a component is added for each layer you can right click on the layer and select Import from file to import the texture of interest for the layer.

The example usage of the layers is to use different textures that were created in Houdini such as water flow and curvature as alpha channels for blending materials.

Optimizing and Runtime Virtual Textures

References

Optimizing

To see your bottlenecks from the viewport menu enable Show FPS and then from the command line (press '~' to open) enter stat unit to display an expanded FPS display in the viewport.

For an even more detailed analysis from the viewport menu go to Stat and then pick a category such as GPU. For my project the main GPU bottleneck is Shadow Depths.

FPSStats

To visualize what is expensive to the viewport Lit menu and select Optimization Viewmodes->Shader Complexity. The scale at the bottom goes from good green to bad white. In my scene everything appears good but the ground itself is taking more time than the vegetation models.

ShaderComplexity

Runtime Virtual Textures

Note: Runtime virtual textures do not support updating based on distance such as when using a PixelDepth input to vary textures at a distance. For this reason I disabled it in my project as I wanted to use materials close to the player and satellite imagery in the distance.

A runtime virtual texture is used to take a material graph and rather than recomputing values at runtime every frame for every pixel, instead the values are rendered to the virtual texture and are only updated as needed. This is a tradeoff of computation vs. GPU memory.

  • Under Project Settings->Engine->Rendering go to Virtual Textures and check Enable Virtual Texture Support.
  • Restart Unreal. It will take time (up to hours) for the shaders to recompile.
  • Go to your landscape and under Details find Virtual Texture.
    • For the array parameter Draw in Virtual Texture add an element.
    • For the new element in the drop down menu select Create New Asset->Runtime Virtual Texture.
  • A new actor in the scene as type Runtime Virtual Texture Volume under Volumes. The placement does not matter.
    • Under its Details go to Transform from Bounds and set Bounds Align Actor to the scene landscape component.
      • In UE4 there are buttons to Copy Rotation and Copy Bounds which are missing in UE5. Instead press the Set Bounds button.
    • Under Virtual Texture->Virtual Texture set the Runtime Virtual Texture you previously created.
  • From the landscape open its Material Instance and then open the instance parent (or just open the parent directly).
    • Within the material rather than sending the final results to the material node they will be sent to a Runtime Virtual Texture Output node.
    • Add a Runtime Virtual Texture Output node.
      • Connect the outputs for BaseColor, Specular, Roughness and Normal.
      • For the WorldHeight output add a WorldPosition node followed by a ComponentMask node set to mask the B channel (which is the height Z).
      • Note: Ambient Occlusion and Pixel Depth Offset are not available as inputs to the node. They are not currently supported.
    • Add a Runtime Virtual Texture Sample node and connect its outputs (BaseColor, Specular, Roughness and Normal) to the material output nodes inputs. This reads from the virtual texture and the material works are normal.
      • Under Details Virtual Texture->Virtual Texture set the virtual texture you created.

VirtualTexture