Colour and image format types for the N64
Home » N64 Homebrew » N64 SDK » Adding textures to 3D models on the N64 » Colour and image format types for the N64
The N64 has many different texture image formats, and this page will go through each and every one of them to learn a bit more about how N64 texels work.
When rendering the final image, the N64 converts the colour to 32-bit RGBA as an output format. This just means that the colour vales are extrapolated from 4/8/16-bit to 32-bit in order to maintain consistency.
It is important to consider that TMEM contains 4096 bytes (4kb) so textures need to be limited to fit in that space. Here is a quick reference data table containing info on texture information density per data type:
Texel format | Maximum Texel Count | Max Best fit Resolution |
32-bit RGBA | 1,024 | 32×32 px |
16-bit RGBA | 2,048 | 44×44 px |
16-bit YUV | 2,048 Y’s, 1,024 UV pairs | 44×44 px |
16-bit IA | 2,048 | 44×44 px |
8-bit Color Index | 2,048 plus 256-entry LUT | 43×43 px |
8-bit (I, IA) | 4,096 | 64×64 px |
4-bit Color Index | 4,096 plus 16 palettes | 64×64 px |
4-bit (I, IA) | 8,192 | 64×128 px |
For the sake of this tutorial, We’ll be using this 16×16 spiral image as a source for the texture arrays:
RGBA
RGBA is short for Red, Green, Blue and Alpha. Colours in this format have a value for each of the RGB values and mix them together to output a first colour. Then the alpha (transparency) value is applied which mixes the colour with whatever is rendered behind it. The values are typically represented as hexadecimal numbers.
Of course, 32-bit RGBA is going to be the most versatile out of all the different image format types that the Nintendo 64 uses. However it is much larger than the others which would mean lower resolution images, and more hardware stress from calculating semi-transperent pixels. It should only be used for anti-aliasing and specifically semi-transparent textures like water/mud.
16-bit is half in size, so it allows for more texels to be stores in the texture memory at once. It is still very versatile in that it allows for any colour within its range to be used at any time, and it still has some transparency.
32-bit
The only 32-bit image format used by the N64 is RGBA. It also is the most straightforward to explain. Each of the RGBA values are 8 bits in length, making it 8/8/8/8. This means that the values range from 0 to 255 (0x00 to 0xFF in hexadecimal) for each colour.
So for example, pure white would be written as 0xFFFFFFFF, black is 0x000000FF, green is 0x00FF00FF and semi-transparent blue would be 0x0000FF80.
This is how the bits are distributed throughout the value for white:
Spiral image in 32-bit RGBA colour
unsigned int spiral[] = {
0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF,
0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x00FF00FF, 0x000000FF,
0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF, 0x000000FF,
};
16-bit
Writing colours in 16 bit RGBA is a bit different to 32-bit RGBA. Rather than have it simply be a compressed version as you’d expect such as 4/4/4/4 bits per value, it is instead split by 5/5/5/1. This means that the values for RGB range from 0 to 31 and the alpha value only has an on/off switch of 0 to 1.
If you want to alter a particular colour, you’re going to move across hex values so it’s best to see how they look in binary. Here are some examples:
Colour | Hexadecimal | Binary |
White | 0xFFFF | 1111 1111 1111 1111 |
Black | 0x0001 | 0000 0000 0000 0001 |
Red | 0xF801 | 1111 1000 0000 0001 |
Green | 0x07C1 | 0000 0111 1100 0001 |
Blue | 0x003F | 0000 0000 0011 1111 |
Yellow | 0xFFC1 | 1111 1111 1100 0001 |
Transparent white | 0xFFFE | 1111 1111 1111 1110 |
Spiral image in 16-bit RGBA colour
unsigned short spiral[] = {
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x0001, 0x0001, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x07c1, 0x0001,
0x0001, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x07c1, 0x0001,
0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001, 0x0001,
};
YUV
YUV is an image format that is comprised of three values – Luminance (Y) and two chrominance (UV) values. Luminance stands for the brightness of each pixel, and Chrominance refers to the colour. This image shows colours for the entire UV range at half luminance:
Source: Wikipedia
There are several types of YUV image formats, but the N64 only uses 16-bit YUV textures.
16-bit
Much like 32-bit RGBA, each value of YUV is 8 bits wide. However, this YUV format (called 422) has two Y values for each U and V value. This means that it is formatted somewhat into a ‘chunk’ that is 2px wide, but is 32 bits in size. Therefore it averages out to 16 bits per pixel. This is done because the Y value is more significant to the human eye, so UV doesn’t need to be changed as often, saving some space.
This makes the 32-bit chunk look like YYUV.
I haven’t seen this format be used in any demos, so it requires a bit of experimentation to figure out how these textures are stored and rendered. However, this is low-priority since 16-bit RGBA works out quite well in its stead and has the same density plus an alpha channel.
CI
Colour Index (CI) works differently to all the other image format types. Instead of directly holding the colour value in the texel itself, the texel value points to a TLUT/palette which holds the target 16-bit colour (RGBA or IA).
This image format is best used when the texture or the TLUT are used several times over. For example, you can convert a ground texture to a grass texture by swapping palettes from brown to green, or you can use the same palette but a different texture to colour different areas of a model.
8-bit & 4-bit
Both 8-bit and 4-bit CI image format textures have half of the TMEM dedicated to the texture data and the other half for the TLUT. They both look identical in terms of code because they are represented by the 8-bit char
data type such as 0x8C
. The 8-bit CI value uses both characters in the 0x8C
, making each comma-separated value a different texel.
However the 4-bit CI value is split between the two characters, where in 0x96
has the first texel look up the colour at 9
and the second texel looks up the value at position 6
such as the image below shows.
Spiral image in 8-bit CI colour
unsigned int spiral[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00,
0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
unsigned short spiral_lut[] = {
0x0001, 0x07c1,
};
Spiral image in 4-bit CI colour
/*This is the 4-bit Colour Index texture.*/
unsigned char spiral[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
0x01, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,
0x01, 0x01, 0x11, 0x11, 0x11, 0x10, 0x10, 0x10,
0x01, 0x01, 0x00, 0x00, 0x00, 0x10, 0x10, 0x10,
0x01, 0x01, 0x01, 0x11, 0x10, 0x10, 0x10, 0x10,
0x01, 0x01, 0x01, 0x00, 0x10, 0x10, 0x10, 0x10,
0x01, 0x01, 0x01, 0x00, 0x00, 0x10, 0x10, 0x10,
0x01, 0x01, 0x01, 0x11, 0x11, 0x10, 0x10, 0x10,
0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x10,
0x01, 0x01, 0x11, 0x11, 0x11, 0x11, 0x10, 0x10,
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
0x01, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
/*The colour index for the above texture.*/
unsigned short spiral_lut[] = {
0x0001, 0x07c1,
};
I and IA
These two image format types are quite similar, so I’ll cover them in the same section. Technically they aren’t colour types since they carry no colour value, only intensity and alpha/transparency. As the name implies, I contains 4 or 8-bit values for Intensity, and IA contains 4 or 8-bit values for Intensity and Alpha.
The following diagram shows how the bits are distributed among each datatype and size:
This might sound a bit useless at first, but it does have a place in some situations. The first thing to consider is that these aren’t greyscale colours (though they can be if you want). You can assign any colour you want to be represented by the intensity by using the colour combiner to mix the texture colour with the vertex/polygon colour. For example, if you’re making a grassy texture, you can use a green base and then use different intensities to apply a pattern. This diagram shows how we can use this principle to make our green spiral:
16-bit IA
This is the highest-density for this colour mode. It’s very easy to read since you have one whole byte for intensity and another one for alpha.
I don’t have a sample for this, but think of it like a higher-res version of the 8-bit IA format shown below.
8-bit I
The simplest out of all of these is the 8-bit Intensity (I) texture image format. It uses all 8 bits (range 0-255) of the char type to determine the intensity of the colour.
In the example below, I use vertex colours to determine the colour (more about that here), and then the Intensity value determines the darkness. Halfway through the spiral, the gradient seems to stop. This is because I stopped varying the first hex value, and switched to using the 2nd so it still varies a little bit. Look at the texture code below the image to understand what I mean.
8-bit IA texture for the above spiral
unsigned char spiral[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xEF, 0xDF, 0xCF, 0xBF, 0xAF, 0x9F, 0x8F, 0x7F, 0x6F, 0x5F, 0x4F, 0x3F, 0x2F, 0x1F, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00,
0x00, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF, 0xFE, 0xFD, 0x00, 0x1F, 0x00,
0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x2F, 0x00,
0x00, 0x4F, 0x00, 0xFC, 0xFD, 0xFE, 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0x00, 0xFB, 0x00, 0x3F, 0x00,
0x00, 0x3F, 0x00, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x4F, 0x00,
0x00, 0x2F, 0x00, 0xFA, 0x00, 0xF2, 0xF3, 0xF4, 0xF5, 0x00, 0xF9, 0x00, 0xF9, 0x00, 0x5F, 0x00,
0x00, 0x1F, 0x00, 0xF9, 0x00, 0xF1, 0x00, 0x00, 0xF6, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x6F, 0x00,
0x00, 0x0F, 0x00, 0xF8, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00, 0xF7, 0x00, 0x7F, 0x00,
0x00, 0x1F, 0x00, 0xF7, 0x00, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x00, 0xF6, 0x00, 0x8F, 0x00,
0x00, 0x2F, 0x00, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x9F, 0x00,
0x00, 0x3F, 0x00, 0xF5, 0xF4, 0xF3, 0xF2, 0xF1, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0x00, 0xAF, 0x00,
0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x00,
0x00, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF, 0xEF, 0xDF, 0xCF, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
8-bit IA
This works similarly to 8-bit I, except that the first half of the byte is dedicated to intensity and the second is dedicated to alpha. The below image uses the same texture data as the above 8-bit I texture, but as you can see, renders differently.
Note: at the 2nd top right corner (orange brick colour), the spiral starts varying the alpha, rather than the intensity; notice how the darkest point is no longer black, but a grey that blends into the background.
8-bit IA texture for the above spiral
When you look at each item in the array (eg 0x9F), the first hex character (9) refers to the intensity, and the second (F) refers to the alpha.
unsigned char spiral[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xEF, 0xDF, 0xCF, 0xBF, 0xAF, 0x9F, 0x8F, 0x7F, 0x6F, 0x5F, 0x4F, 0x3F, 0x2F, 0x1F, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00,
0x00, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF, 0xFE, 0xFD, 0x00, 0x1F, 0x00,
0x00, 0x5F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x2F, 0x00,
0x00, 0x4F, 0x00, 0xFC, 0xFD, 0xFE, 0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0x00, 0xFB, 0x00, 0x3F, 0x00,
0x00, 0x3F, 0x00, 0xFB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0x00, 0xFA, 0x00, 0x4F, 0x00,
0x00, 0x2F, 0x00, 0xFA, 0x00, 0xF2, 0xF3, 0xF4, 0xF5, 0x00, 0xF9, 0x00, 0xF9, 0x00, 0x5F, 0x00,
0x00, 0x1F, 0x00, 0xF9, 0x00, 0xF1, 0x00, 0x00, 0xF6, 0x00, 0xF8, 0x00, 0xF8, 0x00, 0x6F, 0x00,
0x00, 0x0F, 0x00, 0xF8, 0x00, 0xF0, 0x00, 0x00, 0x00, 0x00, 0xF7, 0x00, 0xF7, 0x00, 0x7F, 0x00,
0x00, 0x1F, 0x00, 0xF7, 0x00, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0x00, 0xF6, 0x00, 0x8F, 0x00,
0x00, 0x2F, 0x00, 0xF6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF5, 0x00, 0x9F, 0x00,
0x00, 0x3F, 0x00, 0xF5, 0xF4, 0xF3, 0xF2, 0xF1, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0x00, 0xAF, 0x00,
0x00, 0x4F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBF, 0x00,
0x00, 0x5F, 0x6F, 0x7F, 0x8F, 0x9F, 0xAF, 0xBF, 0xCF, 0xDF, 0xEF, 0xFF, 0xEF, 0xDF, 0xCF, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
4-bit I
As with the other 4-bit structures, the 4-bit I image format has each character in the hexadecimal value represent one texel. For example, in 0xF8 the F would be a value of 15 for the first texel and 8 for the second.
Here is an example where I made a gradient in the values along the top and right of the spiral. Look at the code below to see how the values relate to each other.
4-bit I texture for the above spiral
unsigned char spiral[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x10,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x20,
0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0x30,
0x0F, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x40,
0x0F, 0x0F, 0x0F, 0xFF, 0xF0, 0xF0, 0xF0, 0x50,
0x0F, 0x0F, 0x0F, 0x00, 0xF0, 0xF0, 0xF0, 0x60,
0x0F, 0x0F, 0x0F, 0x00, 0x00, 0xF0, 0xF0, 0x70,
0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xF0, 0xF0, 0x80,
0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x90,
0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xA0,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0,
0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0xC0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
4-bit IA
The following example doesn’t have gradients like the above, but it’s still the output of a 4-bit IA texture.
When you look at each item in the texture array (eg 0xFF), each hex character (F and F) refer to one texel. Each texel has for bits and they are split between I/A at a ratio of 3/1. This means that the first 3 bits of the texel represent the intensity and the last one represents the alpha (on or off only). This also means that you have to divide the hex into binary to find out what the value is.
For example, a hex value of B (11 in decimal) is represented in binary as 1011. Divide this into two sections (101) and (1), and you have the values for I (5) and A (1) respectively.
If we use the same texture array as for the above 4-bit I texture, we get the result in the image below. Notice how we still get the gradient (in steps of 1/8 rather than 1/16) but every other texel is transparent. This is because the even numbered values have an alpha value of 0 and the odd ones have a value of 1.
4-bit IA texture for the above spiral
unsigned char spiral[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0x10,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x20,
0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xF0, 0xF0, 0x30,
0x0F, 0x0F, 0x00, 0x00, 0x00, 0xF0, 0xF0, 0x40,
0x0F, 0x0F, 0x0F, 0xFF, 0xF0, 0xF0, 0xF0, 0x50,
0x0F, 0x0F, 0x0F, 0x00, 0xF0, 0xF0, 0xF0, 0x60,
0x0F, 0x0F, 0x0F, 0x00, 0x00, 0xF0, 0xF0, 0x70,
0x0F, 0x0F, 0x0F, 0xFF, 0xFF, 0xF0, 0xF0, 0x80,
0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x90,
0x0F, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xF0, 0xA0,
0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB0,
0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xED, 0xC0,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};