Large World Coordinates Rendering Overview.

An overview of Rendering Large World Coordinates.

LWC Rendering Overview

New HLSL types have been introduced along with Large World Coordinates and can be found in the LargeWorldCoordinates.ush file.

HLSL Type

Notes

FLWCScalar FLWCVector2/3/4

These types are similar to float1/2/3/4, however, they include an additional Tile coordinate. Therefore, a FLWCVector2 is made from a float2 Tile and a float2 Offset. The value represented is calculated by the formula: Tile * TileSize + Offset, where TileSize represents a constant value that is defined as 256k.

FLWCMatrix FLWCInverseMatrix

These types are similar to float4x4, however, they both include an additional float3 Tile coordinate.

FLWCMatrix

adds the Tile coordinate to the result after multiplying the matrix.

FLWCInverseMatrix

adds the Tile coordinate before multiplying the matrix.

FLWCMatrix

is used for transforms to world space (LocalToWorld).

FLWCInverseMatrix

is used for transforms from world space (WorldToLocal).

Operations such as LWCAdd or LWCRsqrt can be performed on these types. Operations that take LWC input values will return LWC outputs (LWCAdd), while others return regular float outputs (LWCRsqrt). This distinction is driven by your choice of implementation but operations that make coordinates smaller provide you with a return to regular floats.

Within the shader code, many types have been switched to LWC variants. Below is a list of some notable examples:

  • At a high level, LWC types are not included directly inside uniform buffers. Entities contain several pieces of data that can all use the same tile coordinate (WorldToLocal, LocalToWorld, AbsoluteWorldPosition). Therefore, the typical pattern is to store regular non-LWC values in the uniform buffer (RelativeWorldToLocal, LocalToRelativeWorld, RelativeWorldPosition) along with a single float3 TilePosition value.

Inside the shader, this uniform data is "unpacked" into a new struct type that includes the actual LWC values in which many values are initialized with the single TilePosition.

  • SceneData.ush, FPrimitiveSceneData, and FInstanceSceneData have various updated matrices and position vectors.

  • Global camera uniforms have various matrices and offsets changed (such as PreViewTranslation.) Previously, global camera uniforms were either accessed through ResolvedView when rendering meshes (which handles instanced stereo rendering), or View when rendering post-processing and other global passes.

In previous engine versions, View refers directly to the global uniform buffer, as a result we added a new alias PrimaryView which refers to the unpacked global view struct. Therefore, names such as View.PreViewTranslation changed to PrimaryView.PreViewTranslation. ResolvedView.PreViewTranslation continues to function where applicable and is valid to access non-LWC quantities from the regular View alias.

  • FNaniteView now matches updates to the camera uniforms.

  • Light positions and reflection capture positions have been switched over to the new LWC HLSL types.

Switching these global types provides a general indication of where your shader code needs to be refactored to support LWC. For example:

    float3 WorldPosition = mul(Input.LocalPosition, View.LocalToWorld);

    //This will no longer compile, and will need to be refactored to:

    FLWCVector3 WorldPosition = LWCMultiply(Input.LocalPosition, PrimaryView.LocalToWorld);

The initial LWC pass does not check to ensure that all shaders work correctly with LWC.

To accommodate them, you can use the global function LWCHackToFloat to convert a LWC type to a non-LWC type.

LWCHackToFloat behaves as a wrapper around the LWCToFloat function. The distinction between them is the LWCToFloat function is used when you know a conversion is safe at LWC-scale. LWCHackToFloat is a searchable marker. If you didn't have time to refactor your codebase from switching WorldPosition from float3 to FLWCVector3, you could instead use the following code:

    float3 WorldPosition = mul(Input.LocalPosition, LWCHackToFloat(PrimaryView.LocalToWorld));

TranslatedWorldSpace is an existing engine concept that is used in UE5 shaders. Previously, it was intended to increase precision by working relative to the Camera Origin. However, this behavior is useful for LWC because it is safe to use floats when working in TranslatedWorldSpace. Rather than convert WorldPosition to an LWC value, you can use a formula such as:

    float3 TranslatedWorldPosition = mul(Input.LocalPosition, PrimaryView.LocalToTranslatedWorld);

Quick Porting Guide

You can port your existing HLSL codebase into Unreal Engine 5. Below are some suggestions to ensure a successful conversion.

  • Change View to PrimaryView when accessing values/matrices in the world space (LocalToWorld, PreViewTranslation),

  • If your code performing in world space doesn't compile (if it is missing function overloads or type conversions), and you do not have the resources to refactor your codebase, you can use the LWCHackToFloat function where needed, for example:

    (LWCHackToFloat(PrimaryView.PreViewTranslation))

If your code still does not compile:

  • Consider changing the shader code to operate in TranslatedWorldSpace instead of WorldSpace.

  • World space values require LWC types (FLWCVector3 instead of float3). You may need to use the LWCAdd and LWCMin function.

Any new code you develop should follow the guidelines listed above to use TranslatedWorldSpace whenever possible, otherwise we recommend you use the LWC types.

Material Translator

The Material Translator has been updated to work with LWC values. Material nodes will output LWC values instead of floats when querying the AbsoluteWorldPosition and transforming it to WorldSpace.

Materials will work at the LWC scale, however, this may come at a performance cost. Materials that you port can become more expensive, due to LWC operations that add more process calls.

Tags