Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Bitwise masks and shifts

Discussion in 'Shaders' started by diablo, Nov 8, 2012.

  1. diablo

    diablo

    Joined:
    Jan 3, 2011
    Posts:
    736
    I need to be able to check the alpha channel for the low-order bit being set (ie. bit 1). I would normally do this via fmod but strumpy doesn't support this and neither does it support bitwise AND, so what I do instead is multiply the alpha channel by 128 which is essentially a << 7 and then take the fractional part and compare it to 0.5. This doesn't seem to be working... I believe the exponent (ie. the vector's magnitude) is the only one changing and the mantissa (floating point component) remains the same, but I need to do some further testing tonight to know for sure. I was wondering if anyone happens to know what I'm doing wrong or some alternative that works.

    Thanks in advance.

    .
     
  2. AnthonyPaulO

    AnthonyPaulO

    Joined:
    Nov 5, 2010
    Posts:
    110
    This must be an impossible question to answer; any questions related to bitwise operation alternatives go unanswered. Is this simply impossible to do?
     
    CloudyVR likes this.
  3. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Bitwise operations only started being supported in shaders in DX10, which Unity skipped. Unity 4 supports DX11, so you might have some luck there.
     
  4. diablo

    diablo

    Joined:
    Jan 3, 2011
    Posts:
    736
    Due to zero answers on the topic, it would seem as if nobody has figured out how to do bit-masking/shifting with Unity's shaders. Yesterday I finally got it working and would like to share how I did it here.

    Let's say you want to get the low-order bit of a component; that is, bit 1. First, we need to realize that 0000000 is represented by float 0.0 and 11111111 by float 1.0, which is a big pain in the rear when doing bitwise operations because we now have to deal with the exponent in addition to the mantissa. For example, if we multiply 11111111 (255) by 128 to shift it left 7 bits, we would normally get 10000000 (128) : since this equals 128 we know the bit is set, if it was 0 then we know the bit is not set. This is not so simple with floats! First of all, we need to make sure the value is normalized since multiplying any value less than 1.0 (such as 254) will not give you the shifting you expect. Second, we want to be able to perform our bit testing on the fractional part , but multiplying the value 1.0 (255) by 128 will simply result in 128.0 with no bit set in the fractional part, which is useless to us. Because of all this, we need to perfrom two tests in order to get this all working.

    First, normalize the value, then perform the following two tests in parallel :

    1) Perform a special case test via step function to see if the value == 1.0. If it is, we know that all bits are set (ergo ours as well) even though the fractional part is 0.

    2) Perform the following operations :
    a) perform your shifting via multiplation/division here to isolate your bit. In my particular case I want the low-order bit so all I need to do is multiply by 128.
    b) get the fractional part of this value.
    c) perform step function to check if it is == to your bitmask, which in my case is 0.5 (128).
    Note : you can condense steps b c by instead checking to see if the value == 128.5, but I separated the steps out for clarity.

    Now add results from steps 1 and 2 together; what we're looking for is to see if either test is true. If the result of the addition is >= 1.0 then we know that at least one test passed and therefore the bit is set.

    That's all folks. Now because I am a complete noob to shaders I'm sure someone could come up with a more optimal way of doing this, so please feel free to post your version so we can all bask in its heavenly glory.

    .
     
    Last edited: Nov 9, 2012
  5. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    When you said you "got it working", are you sure it does what you expect in every case? I'm not sure you can rely on every hardware/driver combination to implement floats in the same way.
     
  6. diablo

    diablo

    Joined:
    Jan 3, 2011
    Posts:
    736
    I just though of something that might be an issue... I am assuming the color components I'm testing haven't been affected by filtering. Does filtering happen before or after our fragment shader operations?

    .
     
    Last edited: Nov 9, 2012
  7. diablo

    diablo

    Joined:
    Jan 3, 2011
    Posts:
    736
    My post on this seemed to have disappeared so I'll post again.

    I'm not *absolutely* sure, but from what I understand the hardware differences between fp implementations is in their precision. Since we're only using 8-bits worth of precision we are well within any implementation's precision range that I'm aware of and therefore should be on the safe side. However, like I said, I'm no expert on the subject and would appreciate an experts $.02.

    .
     
    Last edited: Nov 9, 2012
  8. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Filtering happens before the fragment shader operation.
     
  9. diablo

    diablo

    Joined:
    Jan 3, 2011
    Posts:
    736
    Ahh shoot... thx for the heads up Dan. In my case I need to work on the raw pixel data which means I'll need to use point filtering, but that will make my textures look crappy when I zoom in on them so I'll have to manually implement the bilinear/trilinear in my shader. There's no such thing as a free lunch... :/

     
    Last edited: Nov 9, 2012
  10. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Not when you're trying to work against graphics hardware, that's for sure. What do you need all of this for? Can you use DX11?
     
  11. diablo

    diablo

    Joined:
    Jan 3, 2011
    Posts:
    736
    I will definitely get Unity 4 when it launches, but right now I'm stuck with 3.5 and I need to have something available for testing.

    My custom shader is for a game I'm developing that is similar to Axis and Allies or any other game you've played where when you capture an enemy's territory that territory's color turns to your color. For example, West Germany might be rendered with a nice texture and occupied by Germany (Grey color), so it will have a greyish tint to it, but when the USA (green) captures it, it will then have a green tint to it. You can see what I'm talking about here : http://www.youtube.com/watch?v=c6ZIm38lnM4. I plan on flipping the territory's color from within the shader. Another function of the shader is to make the ocean tiles transparent so that it will make the water shader I have underneath come through. Later on I'll look into combining the two but for now this works.

    Now you're probably wondering what the hell that has to do with shifting and masking bits. Well, the game is played on a world map and each territory on the map has a unique id to it along with the id of the owner (eg. Germany, USA, etc...). On my map there are about 300 land territories and about 100 water territories. My plan was to encode the id of each territory into each pixel; this way my shader can extract the territory id, look it up in a lookup texture, and extract the player's color from the lookup texture so that it can blend in the player's color, thus giving each pixel the tint of the owning player. Now I was planning on using the alpha channel to store this info, but seeing that there are a total of 400 territories I needed to steal a bit from somewhere, so I stole the low-order bit from the red channel since the human-eye is least sensitive to red. This gives me a range of 512 values to play with. So my shader logic is as follows :

    Check low-order bit from red channel :

    0 : this means it's land, so I use the alpha channel as a UV index into the color lookup texture, extract that color, blend it into the current pixel's RGB, and render it at 100% opacity. We are done at this point and do not proceed to any of the following steps.

    1 : this means it can be either ocean or land, so we go to the next step...

    Check the low-order bit of the alpha channel :

    1 : this means it's land, so I go to the step for rendering land mentioned above.

    0 : this means that it's ocean, so I render it as 100% transparent. This allows me to see through it to the water shader underneath.

    Now my shader also has a blend slider that let's me control the amount of blend (aka lerping).

    What this all means is that whenever one player takes another player's territory, all I have to do is go to the lookup texture, find the pixel that corresponds to that territory, and do one SetPixel operation to set the new owner's color; the shader takes care of the rest. The transparent ocean sections of the map reveals the water shader mesh underneath so that I no longer have static oceans. There's optimizations that can be done but for now it all works well.

    .
     
  12. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    I see. I have a feeling that it would be far easier to make a mesh that used separate triangles for each territory, and change the vertex colours in order to do your tinting.