5
\$\begingroup\$

I am Rendering to a SurfaceFormat.Color. (R8G8B8A8) The alpha of this format has 8 bits and I want to be able to use those 8 bits as a bit field.

For example, in one pass of my render, I want to save a bit value like 1 or 4 passing => 00000001 = 1 or 00000010 = 4 ....

The problem is that the output data of my pixel shader need to be in the range of [0 - 1].

How can I encode, for example, the value 00100001 = 33 in my surface.

I am doing this:

PixelShaderOutput PShader( PS_INPUT pin ) : COLOR0   
{   
    PixelShaderOutput output;   
    ///bla bla bla   
    output.color.a = 33 / 255;   
    return output;   
}   

Is this the right way?

Will I be able to read it in the other side? I mean, in another shader, when I need to read this value, can I use float value = ReadTexture(...) * 255; where value will have 33? (or 00100001)

The GPU will make the right "convertion", cause 33 / 255 = 0,1294117647058824. Will it be encoded as 00100001 in the texture?

In this new shader I want to this value as a bitfield like:

bool xTextureRepeat = fmod(value , 64) >= 32; 
\$\endgroup\$

4 Answers 4

4
\$\begingroup\$

Your method will almost work. There is a rounding problem: if 32/255 is converted to a floating-point value, and multiplied by 255, it may become 31.99 and fmod(value, 64) >= 32 will fail.

This will work better:

float value = round(ReadTexture(...) * 255);

This will work, too, in case you care about performance and wish to avoid a call to round, as long as your tests are in the form fmod / >=. Be careful with other kinds of tests:

float value = ReadTexture(...) * 255 + 0.5;

There is, however, no precision problem.

Explanation

It's a bit complicated to explain why there is no precision issue here. In HLSL, the floating point format with the lowest precision has 11 bits of mantissa (10 encoded bits, plus an implicit leading bit).

Normally you would need either a) complex maths, or b) exhaustive testing of all the half precision range (not very complex, there aren't that many values) to really prove this. But I am really talking about the worst case, ie. half precision floating point encoding, which will never happen unless you specifically ask for it.

The most problematic value to send to the GPU would be 254/255, which is approximately 0.9960784313725490196.... Depending on rounding rules, this can become one of the following values when encoded as a half-precision float:

  • 0.99560546875h (encoded as 2^-1 * (1 + 1015 * 2^-10))
  • 0.99609375h (encoded as 2^-1 * (1 + 1016 * 2^-10))

Multiplying these by 255 yields either 253.87939453125 or 254.00390625 at full precision. But at half precision, these can become one of the following:

  • 253.75h (encoded as 2^7 * (1 + 1006 * 2^-10))
  • 253.875h (encoded as 2^7 * (1 + 1007 * 2^-10))
  • 254h (encoded as 2^7 * (1 + 1008 * 2^-10))
  • 254.125h (encoded as 2^7 * (1 + 1009 * 2^-10))

These will all be rounded to 254 even in the worst case. So you are pretty safe with using a floating point variable here.

\$\endgroup\$
0
\$\begingroup\$

Yes, by dividing and multiplying by 255 you should be able to make this work.

Just one note, better do 33.0 / 255.0 instead of 33 / 255 to ensure the compiler uses floating point division and not integer division.

\$\endgroup\$
0
\$\begingroup\$

just a point. you need to use: int value = round(ReadTexture(...) * 255); not float value = round(ReadTexture(...) * 255);

obs: clever way to avoid the round =P

\$\endgroup\$
0
\$\begingroup\$

You will also likely need to move the variables into uniform variables so the optimizer doesn't try merge too much of the math on you.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .