BitField in Shaders (SM 3 and before)


Sometimes we need to encode lots of boolean flags in a texture channel and recover it in a shader. I found that shader model 3 and before dont have bitwise operations (they only support floats … that dont have this …) !!!
Fortunately i found some help on the old good float math and i managed to solve this even on SM 2.

Here is how it is done:

1. Encoding some flags in a binary format:

Helper Class

public class ShaderUtils
    {
        public static int CreateSpecificBitField(bool DoNotIlluminate = false, bool isBackGround = false,bool NotEffectedByMotionBlur = false , bool useCubeMapAmbientLightining = false)
        {
            return CreateBitField(DoNotIlluminate, isBackGround, NotEffectedByMotionBlur, useCubeMapAmbientLightining);
        }

        public static int CreateBitField(bool op1 = false, bool op2 = false, bool op3 = false, bool op4 = false, bool op5 = false,bool op6 = false,bool op7 = false,bool op8 = false,bool op9 = false)
        {
            int flags = 
            (op1 ? 1 : 0) |
            (op2 ? 2 : 0) |
            (op3 ? 4 : 0) |
            (op4 ? 8 : 0) |
            (op5 ? 16 : 0) |
            (op6 ? 32 : 0) |
            (op7 ? 64 : 0) |
            (op8 ? 128 : 0) |
            (op9 ? 256 : 0) 

            ;
            return flags;
        }
    }

Creating the bitfield

var bitfield = ShaderUtils.CreateSpecificBitField(false, true,false , false)

2. Saving it to a texture channel (in some Pixel Shader, using render to texture)

float id;   /// recieve the bitfield value
....
PixelShaderOutput PShader( PS_INPUT pin ) : COLOR0   
{   
    PixelShaderOutput output;   
    ///bla bla bla   
    output.color.a = id / 255;   
    return output;   
}

3. Recovering it in another shader and testing the flags !!!:

      sampler extraSampler :register(s1);
....
        float4 extra =  tex2D(extraSampler,input.TexCoord).rgba;
        int procces = round(extra.a * 255 );
        bool DoNotIlluminate = fmod(procces, 2) == 1; 
	bool isBackGround = fmod(procces, 4) >= 2; 
	bool isAmbienteCubeMap = fmod(procces, 16) >= 8; 
 ....

         if(DoNotIlluminate  || isBackGround)
	{
		return float4(diffuseColor,1);
	}
....

There is no rounding problem with the float parameters (the explanation is a bit harder, you will need to know how the worst case scenario is encoded as a mantissa and a expoent).
Shader Model > 3 introduced real integers and Bitwise operations =P

, , ,