Virtual Textures
Gaia Sky supports Sparse Virtual Textures (SVT), which enable ultra-high resolution partially resident textures to be used to map planets and other objects. From the user’s perspective, virtual textures are transparent, meaning that the user does not even need to be aware they are being used.
Contents
Hint
The implementation of Sparse Virtual Textures in Gaia Sky is thoroughly explained in this external article.
Overview
Virtual Textures (VT), also known as Sparse Virtual Textures (SVT), MegaTextures, and Partially Resident Textures (PRT), have at their core the idea of splitting large textures into several tiles and only streaming the necessary ones (i.e. the ones required to render the current view) to graphics memory in order to optimize memory usage and enable the display of textures so large that they can’t be handled effectively by the graphics hardware. In this article we use VT and SVT interchangeably to refer to virtual textures.
This technique aims at drastically increasing the size of usable textures in real time rendering applications by splitting them up in tiles and streaming only the necessary ones to graphics memory. It was initially described in a primitive form by Chris Hall in 1993 and has subsequently been improved upon. My understanding is that most modern implementations are based on Sean Barret’s GDC 2008 talk on the topic.
Committed texture pages are kept in a texture, called cache, which is unique for all virtual textures. The size of the cache (in tiles) can be adjusted in the Graphics Settings, virtual textures section.
Creating Virtual Texture Datasets
An SVT is essentially a quadtree which contains a downsized version of the whole texture in the root node. Each level contains 4 times the amount of tiles as the level above, and each tile covers 4 times less area. The pixel count and resolution of all tiles in all levels is always the same.

An example of a virtual texture with 3 levels (0 to 2) for the Earth laid out as a quadtree. Note that the root (level 0, top), covers the whole area, while successive levels have equally-sized tiles that cover less and less area each. This VT has an aspect ratio of 2:1, so it has two root nodes at the top.
In Gaia Sky, SVTs can be packed into a dataset. To do so, we create a new directory for the dataset, preferably using the naming convention vt-[object]-[channel]-[source]. For example, vt-earth-diffuse-nasa is a good name for a VT for the Earth’s surface generated from a NASA dataset. Virtual Textures, like regular textures and cubemaps, can be applied to several material properties:
Diffuse – the color of the surface of a planet or moon, for shading.
Specular – the specular map, for shading.
Normal – the normal map, for shading.
Height – the elevation map, to be used by the tessellation shader or by the parallax mapping process, depending on the height representation chosen.
Metallic – the metallic map, for PBR shading.
Roughness – the roughness map, for PBR shading.
Clouds – the cloud layer.
Typically, we create a virtual texture dataset for a pre-existing object, like the Earth, the Moon or Mars. The Gaia Sky JSON format incorporates some syntax to update already loaded objects. For instance, we can add a diffuse virtual texture to the Earth with the following JSON descriptor in the file vt-earth-diffuse-nasa.json
:
{"updates" : [
{
"name" : "Earth",
"model": {
"material" : {
"diffuseSVT" : {
"location" : "$data/virtualtex-earth-diffuse/tex",
"tileSize" : 1024
}
}
}
}
]}
The "updates"
object name at the top marks the objects in the list as updates. Then, we define the name of the object and the properties we need to update, with the same structure as in the original description file. For instance, if diffuseSVT
is a property of material
, which is inside model
, the same structure must be maintained in the update file.
The following are the objects and attributes that can be updated:
material
– material and all its sub-attributes. In particular all, regular textures, cubemaps and virtual textures: -diffuse
,diffuseCubemap
,diffuseSVT
. -specular
,specularCubemap
,specularSVT
. -normal
,normalCubemap
,normalSVT
. -height
,heightCubemap
,heightSVT
. -emissive
,emissiveCubemap
,emissiveSVT
. -metallic
,metallicCubemap
,metallicSVT
. -roughness
,roughnessCubemap
,roughnessSVT
.cloud
– describes the cloud layer. Can also have a virtual texture. -diffuse
,diffuseCubemap
,diffuseSVT
.atmosphere
– all its direct attributes.rotation
– all its direct attributes.
Any SVT needs to specify a location
and a tileSize
. The location is the directory where the tiles for the different levels are located. The tile size is just the resolution of the tiles of this SVT.
Gaia Sky can work with multiple SVTs, but they all need to have the same tile size. Additionally, the tile size needs to be a power of two in [4, 1024].
Preparing the tiles
The dataset directory must contain a dataset descriptor file named dataset.json
, and the actual data descriptor seen in the previous section (vt-earth-diffuse-nasa.json
).
A VT dataset directory looks like this:
tex/
dataset.json
vt-earth-diffuse-nasa.json
Tiles for a certain level are grouped inside a directory. The directories for all tiles are all in the same place. By convention, we use the tex/
directory. Level directory name format is determined by the regular expression ^(level)?0{1,2}$
. For example, for level 5, these would be correct directory names:
tex/level05
tex/level5
tex/05
tex/5
If we use the format in (1), an SVT with 7 levels (0 to 6) would look like this on disk:
tex/level00
tex/level01
tex/level02
tex/level03
tex/level04
tex/level05
tex/level06
The first level typically contains two tiles (for virtual textures with a 2:1 aspect ratio), the second level contains 4 times that number, and so on (each tile is subdivided into 4 sub-tiles in the next level). Tile files are named tx_[col]_[row].ext
, where col
is the column, and row
is the row. Supported formats are JPG and PNG.
The tex/level01
directory looks like this:
tx_0_0.jpg
tx_0_1.jpg
tx_1_0.jpg
tx_1_1.jpg
tx_2_0.jpg
tx_2_1.jpg
tx_3_0.jpg
tx_3_1.jpg
When in doubt, look at the existing VT datasets.
It is important to know that levels (except level 0) do not need to be complete. Missing tiles will be queried at higher levels automatically.
Tools
We have created our own tools to create and prepare VTs for Gaia Sky. Find them in the virtual texture tools repository.
The repository contains scripts to split a large texture into several tiles, generate the lower levels given a matrix of tiles at a particular (higher) level, get information of tile coordinates, extent, and resolution, and pull data from the Sentinel-2 satellite. Refer to the readme file in the repository for more information.
Limitations
The limitations of our implementation are the following:
Due to the fact that all SVTs in the scene share the same cache, right now we can’t have SVTs with different tile sizes in the same scene.
Similarly, only square tiles are supported. Actually, I can’t think of a single good use case for non-square tiles.
Supported virtual texture aspect ratios are n:1, with
. This is due to the fact that VT quadtrees are square by definition (1:1), and we have an array of root quadtree nodes that stack horizontally in the tree object. It is currently not possible to have a VT with a greater height than width.Performance is not very good on integrated GPUs, especially with many SVTs running at once. This may be due to the shader mimpmap level lookups. This produces
texture lookups (mip levels) in the worst-case scenario when only the root node is available in the cache.All SVTs in the scene share the same tile detection pass. This means that for the same object, all VTs must have the same tile size.