2 channel diffuse textures

Certain types of diffuse textures have a wide range of lightness and saturation values but they vary little in terms of hue. We can exploit this property and pack the image into only two texture channel, complemented with a single color constant, allowing us to store arbitrary data in the remaining two channels of the image (normal XY, specular amount and roughness, etc).

Basic setup

A typical example for this kind of textures are those used for terrain: rock, dirt, grass, etc, much like these:

Ground texture 2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse01 150x150
Leaf mould texture 2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse02 150x150
Grass texture 2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse03 150x150

I used a Filter Forge filter to extract the “brightness” and “saturation” data in the HSB model and put it in the red and green channel of the final texture. (Blue and alpha was set up for normals but in this example they are just constant 1.)

Filter Forge HSB to RGB 2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse04 150x150
Ground texture brightness saturation 2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse01b 150x150
Leaf mould texture brightness saturation 2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse02b 150x150
Grass texture brightness saturation 2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse03b 150x150

The next step is importing the images to your chosen game engine. (Here I’ll use Unreal Engine 4.) Make sure that during import the texture is not converted to sRGB. That will cause a problem when it comes to the red channel which stores brightness aka “value”, but that can be fixed in the shader:

Using the packed texture 2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse05 150x150

Value and saturation is unpacked from the texture parameter and then processed: Gamma is applied to brightness before it gets multiplied by the color constant. The raw saturation gets boosted by the alpha of the Vector4 parameter. The amount of boost depends on the contents of the image but it’s usually around 2.

The results are as follows. The left side is the original while the right side is the 2 channel version. Drag the handle to compare them.

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuseComparisons1b
2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuseComparisons1a
2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuseComparisons2b
2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuseComparisons2a
2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuseComparisons3b
2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuseComparisons3a

The first one holds up quite well, the grass is reasonable too, but the leaves look weird. However it can be helped by using two color constants and modifying the shader like this:

Using the packed textures with two colors 2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse06 150x150

The two colors are blended based on brightness: the second color is in full effect on texels 50% bright or less. In this case this simple logic is enough to recover the general feeling of the original image:

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuseComparisons2c
2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuseComparisons2a

If necessary the blending logic could be augmented with additional parameters to allow finer adjustments.

Choosing channels

When deciding which channel stores what data it’s important to take the compression format into consideration. Probably DXT5 will be used most of the time so let’s take a closer look at it.

The DXT5/BC3 format has the data compression resolution of 5 bits on the red channel, 6 on green, 5 on blue and 8 on alpha. This means that one should place the more sensitive data on the green and alpha channels.

  • Normal maps, especially when used on glossy surfaces, can make compression artifacts look quite apparent. Try to use the Green and Alpha channels if possible.
  • The human eye is more sensitive to changes in lightness than in hue or saturation so try to store  “Brightness” data in either Green or Alpha.
  • Saturation could use the Green channel’s 6 bits but it’s not a disaster if only gets 5.
  • Specular amount and roughness values are only indirectly present on the final render so they are good candidates for the low bit depth channels, red and blue.

Artifact comparison

Here are a closeups of the compression artifacts of the final diffuse color in different data packing situations.

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse41a

DXT1

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse41b

R: Brightness, G:Normal X, B: Saturation, A: Normal Y

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse41c

R: Perlin noise, G: Saturation, B: Perlin noise, A: Brightness

  • Original
  • Packed #1
  • Packed #2
2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse42a

DXT1

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse42b

R: Brightness, G:Normal X, B: Saturation, A: Normal Y

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse42c

R: Perlin noise, G: Saturation, B: Perlin noise, A: Brightness

  • Original
  • Packed #1
  • Packed #2
2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse43a

DXT1

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse43b

R: Brightness, G:Normal X, B: Saturation, A: Normal Y

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse43c

R: Perlin noise, G: Saturation, B: Perlin noise, A: Brightness

  • Original
  • Packed #1
  • Packed #2

When a normalmap was stored then it looks like this against the a dedicated normal map texture:

2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse44b
2 channel diffuse textures 2 channel diffuse textures 2ChannelDiffuse44a

The vertical edges are clearly messed up by the unrelated data on the red and blue channels but the issue should be less apparent with a more organic normal map.