0
\$\begingroup\$

In HLSL I want to store a bitfield for each color in a texture. Specifically spanning 3 floats (RGB, not alpha).

Since each float has to be between 0 and 1 I can only use the significand/mantissa of each float (last 23 bits) which should give me 23*3 = 69 values for my bitfield.

How can I set and get specific bits? (without bitwise operators, obviously, since these are floats)

Remember to take floating point precision errors into consideration.

\$\endgroup\$
  • \$\begingroup\$ Why are you using Shader Model 2.0? Are you targeting Direct3D 11 9.x Feature Levels? \$\endgroup\$ – Chuck Walbourn Oct 20 '18 at 17:41
  • \$\begingroup\$ Not really sure what you mean by that exactly. I'm making a 2D game, so I just figured the lower Shader Model the better because I can target older hardware and 2.0 is good enough for all my other shaders. I guess I could use 3.0 without any losses nowadays, but even if this was for 4.0 where bitwise operators for integers are supported, what difference would it make? You can set and get bits for integers with arithmetic operators as well, right? \$\endgroup\$ – Martin Oct 20 '18 at 19:28
  • \$\begingroup\$ If you look at the Steam hardware survey you capture like 95% of the market with D3D_FEATURE_LEVEL_11_0 (Shader Model 2.0); and 99% with D3D_FEATURE_LEVEL_10_0 (Shader Model 4.0). Just don't worry about it and focusing on making your game fun and cool. See this blog post \$\endgroup\$ – Chuck Walbourn Oct 21 '18 at 0:05
  • \$\begingroup\$ D3D_FEATURE_LEVEL_11_0 is shader model 5.0 and I capture less of the market by using 2.0 than 4.0? That doesn't make any sense. \$\endgroup\$ – Martin Oct 21 '18 at 8:44
  • \$\begingroup\$ But yes I get your point. By using 3.0 instead of 2.0 I would only lose 0.01% of the people on Steam. Even if I used 1.0 I would only gain 0.94% and by using 4.0 instead of 1.0 I would only lose a total of 1.05% of the people on Steam. So using anything below 4.0 with the argument that it would expand the market is completely redundant. Thanks for the information. \$\endgroup\$ – Martin Oct 21 '18 at 8:52
1
\$\begingroup\$

Figured it out. The trick is you can clamp an integer between 0 and 1 by dividing 1 by it, to "unpack" it you divide 1 by the result and the way that floats work I think you could technically use all 32 bits in the 0-1 range. The problem is precision. If you're using Shader Model 4.0 you can use bitwise operators for the integer bitfield, otherwise, you can do this:

Read a bit from float:

fmod(1.0 / num, pow(2, bit + 1)) >= pow(2, bit);

Set bit that is 0 to 1 (when I set a bit to 1 I know it's 0 if that's not the case you could always read the bit to check first):

num = 1.0 / (round(1.0 / num) + pow(2, bit))

You can store a bitfield of 23 bits in a float with this method, but beyond 23 bits the precision is too bad for the round function to handle.

For RGB this gives you a bit field of 23*3 = 69 bits. You can access RGB component by integer division:

rgba[bit / 23]

Get bit in RGB component with modulus:

fmod(bit, 23)
\$\endgroup\$

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.