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 values 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 formatMaximum Texel CountMax Square Resolution
32-bit RGBA1,02432x32 px
16-bit RGBA2,04848x48 px
16-bit YUV2,048 Y’s, 1,024 UV pairs48x48 px
16-bit IA2,04848x48 px
8-bit Color Index2,048 plus 256-entry LUT48x48 px
8-bit (I, IA)4,09664x64 px
4-bit Color Index4,096 plus 16 palettes64x64 px
4-bit (I, IA)8,19290x90 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:

ColourHexadecimalBinary
White0xFFFF1111 1111 1111 1111
Black0x00010000 0000 0000 0001
Red0xF8011111 1000 0000 0001
Green0x07C10000 0111 1100 0001
Blue0x003F0000 0000 0011 1111
Yellow0xFFC11111 1111 1100 0001
Transparent white0xFFFE1111 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:

8-bit I

Th 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,
};

Search

Subscribe to the mailing list

Follow N64 Squid

  • RSS Feed
  • Tumblr

Popular posts