Pixel Shaders: float & integer colors

Of course, this is the coda thet puts the watermark/message in the original texture:
Dx's HLSL code:
Code:
struct PS_INPUT {
    float2 uv : TEXCOORD0;
};

sampler image : register(s0);         // original image
sampler watermark :register(s1);   // texture containing the bit pattern of the watermark in the blue channel
     
float4 main (PS_INPUT In) : COLOR
{
  float4 pix1 = tex2D(image,In.uv);
  float4 pix2 = tex2D(watermark,In.uv); // read the bit
  float tmp = floor(pix1.b*255); // transform it to integer space
  if(pix2.b == 0)    // it is a 0?
  {
    pix1.b = ((int)tmp/(int)2)*2; // then put an even number in the output image's blue channel
  }
  else
  {
    pix1.b = ((int)tmp/(int)2)*2+1; // put an odd value
  }
  
  pix1.b = pix1.b/255; // un-transform to float
  return pix1;  //put value
};

this is the shaders that makes the detection of the watermark:

Code:
struct PS_INPUT {
    float2 uv : TEXCOORD0;
};

sampler image : register(s0);

float4 main (PS_INPUT In) : COLOR
{
  float4 pix1 = tex2D(image,In.uv);   // read texel
  pix1.b = floor(pix1.b*255);             // transform to integer
  float tmp = fmod(pix1.b,2);            // calculate module
  if(tmp == 0)                                 // if module was 0, the number was even
  {
    pix1 = 0;                                    // so put a {0,0,0,0} to the output 
  }
  else
  {
    pix1 = 1;                                    // it was a odd number: put white {1,1,1,1} to the output
  }
  return pix1;
};

It works perfectly when the device is the REF reference software rasterizer, however, fails when using HAL device. Sometimes it returns inverted bit patterns, and often completely fails and returns random bit patterns.

The watermark texture is a black A8R8G8B8 texture. His n-th texel has b = 255 if the n-th bit of the watermark is 1, or 0 otherwise.

With REF rasterizer, after the detection phase I should obtain a black texture with white texels where a even odd number (watermark = 1) was found. There sould be no clues of the original image, only a bit pattern.
However, with HAL device, after the dection phase the result texture is like this:

deswatermark.GIF


regards
 
I am guessing this is coming down to rounding rules etc. I would suggest a couple changes to your code to enhance your chances, and portabbility:

  1. Don't use any integer variables, DX allow these to be emulated as floats. I am not sure how much wiggle room it allows for the handling.
  2. Minimize your operations. Each one is adding error. You scale the numbers 4 times in your code. You only need to do it twice.

I would try this:

Code:
struct PS_INPUT { 
    float2 uv : TEXCOORD0; 
}; 

sampler image : register(s0);         // original image 
sampler watermark :register(s1);   // texture containing the bit pattern of the watermark in the blue channel 
      
float4 main (PS_INPUT In) : COLOR 
{ 
  float4 pix1 = tex2D(image,In.uv); 
  float4 pix2 = tex2D(watermark,In.uv); // read the bit 
  float tmp = floor(pix1.b*127.0); //clip the last bit

  pix1.b = tmp/127.0 + pix2.b/255.0; //add 8th bit based on watermark
  
  return pix1;  //put value 
};

Getting bit exact integer math is pretty hard with floats. Would you care if I stuck this in a piece of sample code?

Assuming it works, it is something that several developers would like to see.

-Evan
 
I'd suggest this:
Code:
struct PS_INPUT {
    float2 uv : TEXCOORD0;
};

sampler image : register(s0);         // original image
sampler watermark :register(s1);   // texture containing the bit pattern of the watermark in the blue channel
     
float4 main (PS_INPUT In) : COLOR
{
  float4 pix1 = tex2D(image,In.uv);
  float4 pix2 = tex2D(watermark,In.uv); // read the bit
  pix1.b = (floor(pix1.b * 127.9) * 2 + pix2.b) / 255.0;
  return pix1;  //put value
};

Code:
struct PS_INPUT {
    float2 uv : TEXCOORD0;
};

sampler image : register(s0);

float4 main (PS_INPUT In) : COLOR
{
  float4 pix1 = tex2D(image,In.uv);   // read texel
  float tmp = pix1.b * 255;
  float bit = tmp - (floor(pix1.b * 127.9) * 2);                 return float4(bit);
};
 
Evan, nice to see you here :D

I hate to say it, but your LSB clipping code won't work. When you insert 2/255 as pix1.b, you will get 0/255 as result. 255/255 will result in 255/255, but should be 254/255, so you can add 1/255 without overflowing.

The correct scaling factor is 127.5, but for numerical reasons it is better to use 127.9
 
Yeah, after I read your post, I realized it. I think I have been staring at too much code this weekend. :rolleyes:

-Evan
 
thanks Evan & Xmas.
The code now works perfectly with HAL device's.

I still don't completely get the trick of using 127.9
Yet I see that there is a lot to improve in my shaders , your solution is a lot more elegant.

ehart said:
Getting bit exact integer math is pretty hard with floats. Would you care if I stuck this in a piece of sample code?

Assuming it works, it is something that several developers would like to see.
Of course you can! In fact a lot of my work it's inspired your article at ShaderX 2.

Regards
 
asmatic said:
I still don't completely get the trick of using 127.9
Let's assume your texture contains integer values ranging from 0 to 255, which get mapped to float values ranging from 0.0 to 1.0 by dividing them by 255.
So
0 -> 0/255
1 -> 1/255
2 -> 2/255
etc.

Now you multiply those values by 127.5, use floor(), multiply by 2 and get this:
0 -> 0/255 -> 0 -> 0 -> 0
1 -> 1/255 -> 0.5 -> 0 -> 0
2 -> 2/255 -> 1 -> 1 -> 2
3 -> 3/255 -> 1.5 -> 1 -> 2
4 -> 4/255 -> 2 -> 2 -> 4
etc.

Seems to work... but only if x/255 can be represented perfectly accurate! Imagine the float representation of 42/255 is slightly lower than 42/255 itself. Then you get something like 20.99999 instead of 21, and the floor function gives you 20 instead of 21.
Using 127.9 makes sure the result is slightly above 21.
 
Back
Top