Skip to content

Backdrop Textures

  • 🌰 In a nutshell...

    • A BackdropTexture supplies both the sky drawn behind a scene and the ambient light that sky casts on everything in that scene. Backdrop Textures
    • Backdrops can be created from high-dynamic-range (.hdr/.exr) images. From HDR or EXR Files
    • Preprocess or bake your backdrops once ahead of time so they load quickly. Preprocessing / Baking

Backdrop Textures#

using var sunset = factory.AssetLoader.LoadBackdropTexture(@"Assets/sunset.hdr"); // (1)!
scene.SetBackdrop(sunset, backdropIntensity: 0.8f, rotation: 130f % Direction.Up); // (2)!
  1. Loads a backdrop texture from a high-dynamic-range image file. Note that this can take a long time; see Loading Backdrop Textures below for faster alternatives.

  2. Sets the scene's backdrop to the loaded texture, slightly dimmed and turned 130° around the up axis. An intensity of 1f shows the backdrop as authored; sunset.MeasuredLux gives the illuminance that represents, and a SceneBackdropBrightnessPreset can be passed instead to light the scene to a real-world brightness (see Exposure & Brightness).

A BackdropTexture describes the surroundings of a scene. It does two jobs at once:

  • It provides the skybox: The image drawn behind everything in the scene, filling every part of the view that no object covers.
  • It provides image-based lighting: Ambient light derived from the image, which lights every object in the scene from every direction (so, for example, a scene set at sunset picks up warm light from the horizon without you having to place any lights by hand).

The second part is what makes objects look as though they genuinely belong in their surroundings, and a scene with a backdrop tends to look markedly more natural than one lit only by its own lights. Because of these two roles, every backdrop is ultimately made of two parts; a skybox texture and an image-based lighting texture.

Other Ways to Set a Scene's Backdrop

As well as scene.SetBackdrop(), the following methods are available on a Scene:

  • SetBackdropWithoutIndirectLighting() draws the backdrop behind the scene but doesn't use it to light anything. This is useful when you want to light the scene entirely with your own lights, or when the backdrop is purely decorative.
  • SetBackdrop(ColorVect color) fills the background with a flat colour, and also lights the scene in that colour from every direction (a mid-grey gives soft, even lighting similar to an overcast sky). SetBackdropWithoutIndirectLighting(ColorVect color) fills the background without lighting anything.
  • RemoveBackdrop() removes the backdrop (and the light it was contributing) entirely.

See Scenes for more information.

Like every other resource, a BackdropTexture should be disposed when you're done with it; but every scene using it must have stopped doing so first.

Loading Backdrop Textures#

There are three ways to load a backdrop texture:

Source Load Speed Notes
An .hdr or .exr image Very slow (can take minutes) Converts the image every time; only available on a local factory
A preprocessed pair of .ktx files Fast Portable files that can be produced once from an .hdr/.exr image
A baked asset file Fastest Produced by the asset bakery

From HDR or EXR Files#

using var sunset = factory.AssetLoader.LoadBackdropTexture(
    @"Assets/sunset.hdr", 
    backdropTextureResolution: Quality.High // (1)!
);
  1. Specifies the resolution of the resultant backdrop. Higher resolutions look sharper but take proportionally longer to create (and use more video memory).

assetLoader.LoadBackdropTexture() creates a backdrop texture directly from a high-dynamic-range image in .hdr or .exr format. To do so, TinyFFR converts the image in to a skybox and image-based lighting texture using a tool (cmgen, from Filament) that's bundled with TinyFFR, run as a separate process on the local machine.

The backdropTextureResolution argument sets the size of each face of the backdrop's cubemap:

Quality Resolution (per cubemap face)
Quality.VeryLow 64x64
Quality.Low 256x256
Quality.Standard 512x512 (default)
Quality.High 2048x2048
Quality.VeryHigh 4096x4096
Converting HDR Images is Slow

Converting a high-dynamic-range image in to a backdrop is very slow; it can take minutes for a large image at a high resolution, and it allocates heavily while it runs. It's not something you should do while your application is meant to be responsive.

Unless you're only loading backdrops while developing your application, you should preprocess or bake your backdrops ahead of time instead (see below). If you must load from an .hdr/.exr file at runtime, use the asynchronous version of the method (see Asynchronous Loading).

If a conversion takes longer than 15 minutes it is aborted with an exception. You can change this time limit via the MaxHdrProcessingTime property of the LocalAssetLoaderConfig supplied when creating your factory:

using var factory = new LocalTinyFfrFactory(
    assetLoaderConfig: new LocalAssetLoaderConfig { MaxHdrProcessingTime = TimeSpan.FromMinutes(30d) }
);

Image Shapes#

using var nebula = factory.AssetLoader.LoadBackdropTexture(
    @"Assets/nebula.hdr", 
    aspectRatioCorrection: BackdropAspectRatioCorrection.MirrorTile // (1)!
);
  1. Specifies how to adjust the image if it isn't already a supported shape. If this argument is omitted, BackdropAspectRatioCorrection.Stretch is used.

Backdrops are best when created from images of certain shapes:

  • Equirectangular panoramas, which are exactly twice as wide as they are tall (2:1). This is the shape of almost all downloadable HDR skies.
  • Cross-shaped cubemaps, which are 4:3 (horizontal cross) or 3:4 (vertical cross), where the longer side is a power of two (e.g. 4096x3072).

Some .hdr images are not either of these shapes. In this case, TinyFFR adjusts these images to a 2:1 panorama before converting them, according to the aspectRatioCorrection argument:

Value Result
BackdropAspectRatioCorrection.Stretch The image is stretched to wrap all the way around the scene (default). Nothing is lost, but the image is distorted, and its left and right edges meet on the far side of the backdrop (which can show a seam if they differ).
BackdropAspectRatioCorrection.MirrorTile The image is placed next to a mirrored copy of itself, and the pair is stretched to wrap around the scene. The image appears twice, but there is no seam.
BackdropAspectRatioCorrection.PadWithAverageColor The image is placed, undistorted, in the middle of the panorama, and the rest is filled with the average color of the whole image. The fill lights the scene in keeping with the image's overall brightness, but there's usually a visible step where the image meets it.
BackdropAspectRatioCorrection.PadWithEdgeColor The image is placed, undistorted, in the middle of the panorama, and the rest is filled with the colors found along the image's edges. Beside the image, the fill fades from the color of its right edge to the color of its left edge, so it blends in to the image on both sides; above and below the image it matches the top and bottom edges.
BackdropAspectRatioCorrection.Throw An exception is thrown instead.

Images that are already a supported shape are never altered, whichever value you choose. PreprocessHdrOrExrTextureToBackdropTextureDirectory() and the asynchronous methods take the same argument.

EXR Images Are Never Adjusted

Only .hdr images can be adjusted. An .exr image that isn't a supported shape always fails to convert, with an exception that states its dimensions. Convert it to a 2:1 panorama yourself (or save it as an .hdr image) first.

Preprocessing#

factory.AssetLoader.PreprocessHdrOrExrTextureToBackdropTextureDirectory( // (1)!
    @"Assets/sunset.hdr", 
    @"Assets/Backdrops/Sunset", 
    backdropTextureResolution: Quality.High
);

using var sunset = factory.AssetLoader.LoadBackdropTextureFromPreprocessedDirectory(@"Assets/Backdrops/Sunset"); // (2)!
  1. Converts the .hdr image to a pair of .ktx files and writes them in to the Assets/Backdrops/Sunset directory (creating it if it doesn't exist). This is the slow part, but it only needs to be done once (e.g. while developing your application).

  2. Loads the backdrop texture from the preprocessed files. This is much faster than loading from the .hdr file, and can be done every time your application runs.

Rather than converting a high-dynamic-range image every time it's loaded, you can convert it once with PreprocessHdrOrExrTextureToBackdropTextureDirectory() and ship the converted files with your application.

The conversion writes two files to the destination directory: A skybox texture (named *_skybox.ktx) and an image-based lighting texture (named *_ibl.ktx). LoadBackdropTextureFromPreprocessedDirectory() then loads a backdrop from a directory containing one of each.

If you'd prefer to specify the two files individually (e.g. if you've renamed them, or you obtained them from somewhere else), use LoadPreprocessedBackdropTexture() instead:

using var sunset = factory.AssetLoader.LoadPreprocessedBackdropTexture(
    @"Assets/Backdrops/sunset_skybox.ktx", 
    @"Assets/Backdrops/sunset_ibl.ktx"
);

The .ktx files are in the standard format produced by Filament's cmgen tool, so they're portable: You can keep them alongside your other source assets, share them between projects, or produce them yourself with cmgen.

Baking#

factory.AssetBakery.Enabled = true; // (1)!
using (var sunset = factory.AssetLoader.LoadBackdropTextureFromPreprocessedDirectory(@"Assets/Backdrops/Sunset")) {
    factory.AssetBakery.Bake(sunset, @"Assets/sunset.tinyffr"); // (2)!
}
factory.AssetBakery.Enabled = false;

using var bakedSunset = factory.AssetLoader.LoadBakedBackdropTexture(@"Assets/sunset.tinyffr"); // (3)!
  1. The asset bakery must be enabled before the resources you want to bake are loaded.

  2. The backdrop texture can be loaded via any of the methods above, and then baked in to a single asset file.

  3. Later (e.g. in your released application), the baked backdrop texture is loaded. This is the fastest way to load a backdrop.

The fastest way to load a backdrop texture is to bake it with the asset bakery and then load it with LoadBakedBackdropTexture(). The only reasons to prefer the preprocessed .ktx files over a baked asset are if that's the format you were already given, or if you want to keep your converted backdrops in a portable format.

Asynchronous Loading#

using var sunset = await factory.AssetLoader.LoadBackdropTextureAsync(@"Assets/sunset.hdr", Quality.High); // (1)!
scene.SetBackdrop(sunset);
  1. Converts and loads the backdrop on a background thread. Once it's ready, execution resumes on the primary thread, so the backdrop can be used immediately.

Every method on this page has an asynchronous counterpart (e.g. LoadBackdropTextureAsync(), LoadBakedBackdropTextureAsync(), PreprocessHdrOrExrTextureToBackdropTextureDirectoryAsync()). These are especially worthwhile when converting .hdr/.exr images, as they let your application keep running (and rendering) while the slow conversion takes place. Asynchronous loading is explained in more detail on the Async asset loading page.

Comments