Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 24 additions & 22 deletions docs/Guides/DDSFormatGuide/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

![DDS_Flowchart](./DDS_Flowchart.png)

*Note: Kopernicus doesn't support BC5 at the moment, use DXT5nm. Parallax Continued users should use Parallax Scaled as it properly supports BC5.*

You've probably heard of image formats before, .png, .jpg, .webp, maybe even .dds. An image format at the most basic level is simply an agreed upon structure (usually created by some very smart people) on how to encode image data in some specific way. Different image formats are often tailored to very specific tasks, and it's important to use the correct format in your work.

Sometimes, an image format contains a wider specification of subformats, with each subformat being even further tailored for a specific applications. These image formats are often called "container formats", and it's just as important to choose the right subformat that fits your texture's needs.
Expand All @@ -11,9 +13,9 @@ Sometimes, an image format contains a wider specification of subformats, with ea

### What is DDS?

The DirectDraw Surface format was developed by Microsoft in 1999, as a high performance alternative to jpg and png in regards to GPUs. Since then it's grown to be the industry standard for compressing textures, with a wide variety of subformats, support for special texture types like arrays and cubemaps, and various other nifty features to help the GPU such as mipmaps.
The DirectDraw Surface format was developed by Microsoft in 1999 as a way of storing textures with minimal overhead required to transfer them from disk to the GPU, making them a high performance alternative to .jpg and .png. Since then it's grown to be the industry standard for compressing textures intended to be used in real time rendering. DDS comes with a wide variety of subformats, support for special texture types like arrays and cubemaps, and various other nifty features to help the GPU such as mipmaps and signed/unsigned subtypes.

When a png or jpg texture is requested on the GPU, the CPU must first decompress this data into its raw bytes in RAM, next this raw byte array is then compressed into a format that the GPU can properly read (this is done by Unity), then this compressed texture is transferred into VRAM and bound to a texture slot, with the final result being a texture in VRAM that can then be sampled in the shader. This process is time consuming on both the CPU and GPU, and you're often giving up far more storage than you really need.
When a .png or .jpg texture is requested on the GPU, the CPU must first decompress this data into its raw bytes in RAM. Next this raw byte array is then compressed into a format that the GPU can properly read (this is done by Unity), then this compressed texture is transferred into VRAM and bound to a texture slot, with the final result being a texture in VRAM that can then be sampled in the shader. This process is time consuming on both the CPU and GPU, and you're often giving up far more storage than you really need.

In contrast, DDS acts a direct pipeline to the GPU, the texture is loaded into RAM, and then directly given to the GPU without any decompression or recompression required. Furthermore these textures are often "Block Compressed" (these are often denoted by BC*n*), meaning the image is chopped up in small 4x4 squares, which allow the GPU to load chunks of data at a time, improving sampling speed and memory usage.

Expand All @@ -26,22 +28,22 @@ In contrast, DDS acts a direct pipeline to the GPU, the texture is loaded into R

DDS textures (specifically BC*n* textures) undergo lossy compression when they are encoded, this sacrifices *ideally* minute, unnoticeable detail in exchange for much lower storage sizes.

A PNG texture loaded on the GPU will more than likely undergo a conversion to a RGBA32 format. The 32 means that for every pixel on the image, 32 bits are dedicated to representing it, or 4 bytes. Compare that to a BC1 texture which only used 0.5 bytes per pixel and you're looking at a 8x reduction in VRAM.
A PNG texture loaded on the GPU will more than likely undergo a conversion to the RGBA32 format. The 32 denotes the number of bits the format uses to represent pixel on the image. Compare that to a BC1 texture which only used 0.5 bytes (4 bits) per pixel and you're looking at a 8x reduction in VRAM.

It's clear why we'd use DDS here, but what's the downside?
1. You'll have to save a development copy of your planets.

Every time you make an edit to your world, you'll need to re-export it to save your changes. This will trigger another block recompression if you export as DDS, "crunching" your maps further.
Every time you make an edit to your world, you'll need to re-export it to save your changes. If you export in a lossy DDS format, this will block compress your images. If you then decide to edit this block compressed version, it'll go through another set of block compression, crunching your image further.

I'd personally advise you to save a .png or a .tif copy of your maps, edit that, then use the commands and the bottom of this guide to compress your data when you're ready to load the game.
I'd personally advise you to save a .png or a .tif copy of your maps, edit that, then use the commands at the bottom of this guide to compress your data when you're ready to load your textures into the game.

2. Sometimes you want lossless.

When working with heightmap data specifically, your options for DDS are pretty limited. BC4 is the standard but it can cause some nasty artifacting in scenarios where height accuracy is necessary.

I actually keep two heightmaps per planet for Sol, one is R16 and kept on the CPU/RAM side by only using it for the PQS, and one is BC4 and is only used in scaled space via Parallax Scaled. It's entirely possible you may only need a BC4 map though!

Another example is sometimes you want to store very specific types of data, this could a heightmap or a normal map or perhaps some sort of flow map. DDS is going to try its best to make the data "look" the same, it doesn't care about precision, it doesn't care about the fact you're encoding a vector, its only job is to make it "look" similar
Another example is sometimes you want to store very specific types of data, this could be a heightmap, normal map, emissive or scattering map, or perhaps some sort of flow map. DDS is going to try its best to make the data "look" the same using perceptual error metrics. The compression algorithm doesn't care about precision of each channel independently, making data where each channel is intended to store independent data prone to precision errors.

<br>

Expand All @@ -61,10 +63,10 @@ Example of data loss from different types of DDS compression :

Another major benefit of DDS textures is their native support for mipmaps.

While our textures on disk might be flat grids, when we project them onto a 3D plane we often get grazing angles or want to view our data from long distances. Without mipmapping this causes what's called "aliasing" and "moire patterning" due to the GPU overdrawing textures in a small space. Mipmapping seeks to prevent this by saving your original image at 1/2, 1/4, 1/8, 1/2^n resolutions. When the texture is taking up less screen space than a "mip level" requires, it'll drop down a level, lowering the number of samples needed and improving performance.
While our textures on disk might be flat 2D grids of pixels, when we project them onto a 3D plane we often see them at grazing angles or want to view our data from long distances. Without mipmapping this causes what's called "aliasing" and "moire patterning" due to the GPU overdrawing textures in a small space. Mipmapping seeks to prevent this by saving your original image at 1/2, 1/4, 1/8, 1/2^n resolutions. When the texture is taking up less screen space than a "mip level" requires, it'll drop down a level, lowering the number of samples needed and improving performance.

> [!NOTE]
> The process of mathematically controlling these lower miplevels is called filtering.
> The process of mathematically controlling these lower miplevels is called [filtering](https://en.wikipedia.org/wiki/Texture_filtering#Filtering_methods).
>
> This is where bilinear, trilinear, and ansiotropic filtering come from.

Expand Down Expand Up @@ -102,11 +104,11 @@ The formats most relevant to KSP are as follows:

### How to DDS?

Previously it was recommended to use Nvidia Texture Tools for loading and exporting of textures, however it struggles significantly with 16k textures and is generally just not very fast compared to modern alternatives.
Previously it was recommended to use Nvidia Texture Tools for loading and exporting of textures, however it struggles significantly with 16k textures and is generally just not very fast when compared to modern alternatives.

I personally recommend [texconv](https://github.com/microsoft/DirectXTex/releases) as it supports all of the latest formats, is being actively maintained, and is extremely fast with the caveat that it is a command line tool.

The following instructions are for Windows, if you're on linux, you should be capable of following along similarly but you'll need [this version](https://github.com/matyalatte/Texconv-Custom-DLL).
The following instructions are for Windows, if you're on Linux, you should be capable of following along similarly but you'll need [this version](https://github.com/matyalatte/Texconv-Custom-DLL).


To install
Expand All @@ -122,7 +124,7 @@ To use texconv, you simply type in `texconv` followed by your command arguments,

| Argument | Usage |
| :--- | :---- |
| -m | Mipmap levels. Texconv will automatically make mipmaps, to disable mipmaps do `-m 0` |
| -m | Mipmap levels. Texconv will automatically make mipmaps, to disable mipmaps do `-m 1` |
| -srgb | Ensures that the texture is treated as srgb on load and on export, use for colormaps
| -o | Specify an output directory, otherwise it'll write to the current location with the same filename except as .dds
| -y | Overwrite on disk, if this isn't set it'll halt if the texture already exists
Expand All @@ -136,17 +138,17 @@ To use texconv, you simply type in `texconv` followed by your command arguments,
Here is a list of all the relevant compression format options:


| Format | Usage | Code |
| :--- | :---- | ---- |
| BC1 (DXT1) | Legacy/Low Cost Colormaps | BC1_UNORM
| BC3 (DXT5) | Legacy Colormaps | BC3_UNORM
| BC3n (DXT5nm) | Legacy Normal Maps | BC3n or DXT5nm
| BC4 | Heightmaps (Lossy) | BC4_UNORM
| BC5 | Normal Maps | BC5_UNORM
| BC7 | Colormaps | BC7_UNORM
| R8 | Heightmaps (8b, Lossless) | R8_UNORM
| R16 | Heightmaps (16b, Lossless) | R16_UNORM

| Format | Usage | Code |
| :--- | :---- | ---- |
| BC1 (DXT1) | Legacy/Low Cost Colormaps | BC1_UNORM
| BC3 (DXT5) | Legacy Colormaps | BC3_UNORM
| BC3n (DXT5nm) | Legacy Normal Maps (Kopernicus) | BC3n or DXT5nm
| BC4 | Heightmaps (Lossy) | BC4_UNORM
| BC5 | Normal Maps (Parallax Scaled) | BC5_UNORM
| BC7 | Colormaps | BC7_UNORM
| R8 | Heightmaps (8b, Lossless) | R8_UNORM
| R16 | Heightmaps (16b, Lossless) | R16_UNORM
| Palette | Biomemaps | [See KSPTextureUtils](https://github.com/Phantomical/KSPTextureLoader/tree/main/src/KSPTextureUtils)



Expand Down
Loading