Encode province ID in a texture

Hi All

I’m trying to create a system for handling provinces similar to the one Paradox use for games such as EU4.
For those that don’t know, this is done using a texture with each unique colour representing a unique province:

I’m struggling with colouring those provinces based off who owns them. My plan is to have a 1D shader that acts as a lookup, with each pixel along the X representing that provinces ID and the value of that pixel being the colour I want all provinces with that ID to render with.

To do this I need to encode the ID of the province into an ID texture that the shader can then read, how should I do this?

Since there’s more than 256 provinces, we can instead split our 4 byte (int/uint) province ID that would support millions of IDs, into its individual bytes, and then write those bytes as the RGBA of the texture.

//Get each byte of our 4 byte integer using bitshifts.
var r = (byte)(id);       //Cast to byte, which will return as the last 8 "most significant" bits.
var g = (byte)(id >> 8);  //Shift bits a whole byte (8 bits) right, replacing the most significant.
var b = (byte)(id >> 16); //Shift over 2 bytes...
var a = (byte)(id >> 24); //Shift over 3 bytes... leaving only the final byte of data.
//Use those bytes as the RGBA value of the pixel
var provinceColor = new Color32(r, g, b, a);

And then in your shader when you sample this texture, you can reconstruct the ID by doing the reverse and combining:

col *= 255u; //Adjust the 0-1 float range of our sampled color to 0-255 byte range
uint4 bytes = (uint4)col; //Cast our float4 version of the bytes to a uint4 to bitshift with
uint provinceID = (bytes.r | bytes.g << 8u | bytes.b << 16u | bytes.a << 24u);

Now you’ve got an ID to use as your UV offset or array index.

(make sure R, G, B, A are cast to int/uint after you sample it from the texture in your shader, otherwise the bitwise operations won’t work correctly due to the way floats work. For indexing you generally want uint since you don’t want negative values for an array index, not to mention it will give you double the positive values)
(if you’re wondering what the ‘u’ affixed to the end of those numbers is for, it’s a type literal to state “this is a uint number” instead of defaulting as float")

1 Like

Having some issues when sampling in the shader, using a test lookup table they seem to always be sampling the first pixel in the lookup, so it seems the ID reconstruction is resulting in 0.

This is how i’m getting the ID in the shader:

uint r = (uint)tex2D(_IDMap, i.uv).r;
uint g = (uint)tex2D(_IDMap, i.uv).g;
uint b = (uint)tex2D(_IDMap, i.uv).b;
uint a = (uint)tex2D(_IDMap, i.uv).a;
uint provinceID = (uint)(r | g << 8 | b << 16 | a << 24);

My apologies, I completely forgot about the fact the sampling will return a 0-1 range color instead of 0-255.
You just need to multiply the input color by 255 before doing the bitshifts. I’ve updated my example some more!

edit: Also, no need to sample the texture 4 times. Just sample it once and use the 4 components of that sampled color however you want, like in my example.

Like: float4 col = tex2D(_IDMap, i.uv) * 255u;
and then cast it to bytes like in my example.
Or you could one-liner that like so:
uint4 bytes = (uint4)(tex2D(_IDMap, i.uv) * 255u);

1 Like


It worked!

As a test I’ve generated the lookup table with a bunch of random colours and comparing that texture to the ID of each province shows the remapping works.

1 Like