Shawn Blanch (blanchsb)

23 replies · posted

Question: Blender Color Strangeness

Can someone explain what is happening on the following settings in 2 different parts of blender?
A texture brush color of (0.5,0.5,0.5) gives me a "(Gamma Corrected)" HEX of #808080



But a Diffuse Material Color of (0.5,0.5,0.5) gives me a Gamma Corrected HEX of #BCBCBC?


The values on the brush color grow to 0.737255 if I enter the HEX code of BCBCBC

The values on the diffuse color shrink to 0.215861 if I enter the HEX code of 808080

So, now for my question: Why the difference between texture brush color and diffuse color?

When I color sample the color from another application I get 808080and the 8bit RGB code of
(0.5, 0.5,0.5) or (128,128,128)

My own Explanation of what is going on:
I think the texture color is directly converting a hex value of 808080 to linear color space (i.e. go from linear to 8 bit color by multiplying the linear decimal value by 256 and then converting each channel's 8 bit value to a combined HEX value #RRGGBB) i.e. foing from (0.5,0.5,0.5) to (128,128,128) to HEX #808080 (or 0x808080 if you are working with python code) basically it is converting red channel from 128 to hex 80, same for green channel, same for blue channel and storing them in the 6 place holders r=80 g=80 b=80

or combined #808080

I think the material's diffuse color is using blenders gamma corrected HEX value by transforming the linear color space to sRGB (or whatever color space you are using in the color management section, typically this is set to sRGB) and then to HEX. There is a post in a python about this conversion:
getting HEX from Blender sRGB

Can someone spot check me on this?
spikeyxxx or maybe jlampel know what is going on? I think the comment saying Gamma Correction in the brush color is actually false.

  • It's inconsiistent behavior. The RGB (0.5, 0.5, 0.5) in the Shader Editor is  the visual mid-grey. Nowadays, this is written as a Value (in HSV) of 0.5. The HEX notation denotes the linear value of the red, green and blue colors; if you would measure the Value indicator next to the color wheel, taking the bottom as 0 and the top as 255, you'd get: 11*16 + 12*1 = 188 and 188/255 would yield a Value of about 0,7372549 and thus a RGB of approximately (0.737, 0.737, 0.737). 

    So the HEX value denotes the 'absolute position' and the RGB and (nowadays) Value in HSV denotes the 'human, visual positon' (what the average human considers to be half-grey).

    In Texture Painting, everything is linear; an RGB of (0.5, 0.5, 0.5) is halfway between black and white, which makes it look darker than mid-grey for humans.

    The hexadecimal notation is just a way to easily read binary,; it's a direct 'translation.

    Between decimal and hexadecimal, the connection is not so straightforward: you count: 0, 1, ...,9, A, B,..., F, 10, ...,FF (and further if you want).

    But in binary, F2, for instance, is just: 1111 0010

    In short, the two colors in the Shader and in the Texture Brush are not the same, and this shows in the HEX values.

    I am not sure what Gamma correction is exactly, so I will not say if this is wrong in one of them, but your life will probably be better, when you ignore the term whenever you see it ;)

  • Yeah that Gamma correction is what the Shader is doing to the HEX value in order to give the same visually perception of the color that is directly being converted to over in the texture brush color.
    It's not what the texture paint brush is doing, it is directly converting the HEX into RGB without any

    Blender Color Management: https://docs.blender.org/manual/en/latest/render/color_management.html

    From the manual: Image Color Space seems to communicate what the Texture brush is working with I think.


    On to my next question:
    How do I accurately convert from linear 0.0000-1.0000 over to a proper HEX value withoutGamma correction (i.e. should be a straight conversion).
    My problem is I have yet to find a way to EXACTLY convert the value over to the HEX value blender displays every time when using Python code.
    Brecht (one of the blender devs) used this as a conversion:

    hex(max(min(int(srgb * 255 + 0.5), 255), 0))

    sRGB is a color value from 0-1. So he multiplies it by 255 and then adds 0.5 and sets the bounds of the result to be 255 at the top (using min(result,255)) and at at the bottom (using max(result,0)). Then he converts the answer into hexidecimal using a built in Python function hex()

    I don't seem to get the exact same HEX value every time when I randomly generate a color value though. The conversion has something off a little.

  • blanchsb the HEX values are everywhere in Blender linear:

    Mathematically, 0.5 should be converted to 127 or 0F, but that is probably why 0.5 is added to the values after having been multiplied by 255....But still, pretty much linear.

    The RGB color in the Texture Paint Brush is also linear, so there a formula like Brecht uses should work for the conversion, but not when using sRGB, because sRGB is not linear! And sRGB is used in for instance the Shader Editor.

    Theoretically, sRGB is equal to RGB to the power of 2.2 ('Gamma') but the actual transformation is using a lesser power near Black and something like 2.4 near white values...I do not know what transformation (from linear to sRGB ) Blender uses internally, but that is what gets yourr values off probably...the decimal to Hex conversion is straightforward and doesn't depend on software.





    • spikeyxxx You're certainly right, yet (naively spoken) I would expect 0.5 to be mapped to 127.5 . But how would you write this as HEX 😉?

    • This is a much debated topic to be honest. Some people have the idea to map 127, others to 128 others to different values like in the case above Brecht was using (x/255) + 0.5
      At the end of the day the direct conversion for the Brush color only: I found that Blender is using is HEX value converted to 0-255 value then divided by 255 gives the transformation exactly to the non-gamma-corrected linear value for RGB and displays the correct HEX value in the color picker that I started with every time (I have tested over a100+ different color selections)

    • duerer that is because you are a human and used to start counting at 1 (and right you are!)...

      There are, however 256 different 'combinations' possible from 0 to 255, which gives us ('naturally') natural numbers 1 to 256 and half of 256 is 128 and not 127.5 ;)

      (You could of course also map 256 shades of grey to 8 bits...)

      The decision to start counting at zero might seem a bit strange, but it comes from the fact that 0000 0000 (for instance) is being 'mapped' to 0....(even when describing negative numbers or fractions...).

    • Yes, because computers work in zeros and ones and we use that first bit 0 in the base 2 system to define 0 like Spikey shows above.

      1 byte = 8 combined bits of 0 or 1 (when you fill the bit with a 1 you gotta move on to the next bit, hence the base 2 system, you get 2 values to work with in each place whereas the base 10 system we get 0 through 9 to work with or 10 options before moving to the 10's place and so on and so on) which gives 2^8 combinations (256 combinations) or values ranging from 0-255......unless you go negative then it is -1 to -256 in most cases that I am aware of.
      0000 0000

      Even the PLC's that control the turbines at my work use this concept.

    • blanchsb when you also want to use negative integers, it is customary to use the so-called two's complement (you can look that up) system, which, in a 8 bit system, would mean: 0000 0000 to 0111 1111 gives 0 to 127 and 1000 0000 to 1111 1111 gives -128 to -1.


    • yeah it consumes the last bit in the series for the negative sign. Two's compliment is used in our IO modules and confused me for the longest time. You still are working with 8 bits but you lose half of the range in either positive or negative direction (no longer 0-256.)

  • Thanks spikeyxxx

    I think I got the straight conversion working just dividing the 8-bit RGBby 255 and going into linear space. Not sure why Brecht was adding in the 0.5 but it was actually better results leaving it out. I now have a way to convert brush colors from any hex value I want in python for a brush color in blender. Pretty excited to figure out 1 piece of the puzzle.


    Now I need to figure out the next piece of the puzzle converting the gamma correction on a hexidecimal value.


    If anyone is interested in the code to do the color conversion I can post it later.

  • I was trying to work with random color gen on a HEX value. But I wanted to make sure blender color picker showed the exact color value in hex after converting the hex into linear non-gamma corrected color.

    I used 2 different conversion formulas to cross check myself. But I get the correct HEX displayed value on the brushes every time now.

  • The "Blender 3.0 Manual" here writes this about the "Color Picker" and "Gamma Correction":

    In Blender, the RGB and HSV/HSL values are in Scene Linear color space, and are therefore not Gamma corrected. On the contrary, Hex are automatically Gamma corrected for the sRGB Color Space. For more information, see Color Management.

    • Yes that is for everything but the image editor brush colors. They are not gamma correcting the hex values you enter. I want to make a 👾 report about it specifically for the brush color. 

      It doesn’t need to change (they intend to have it that way) but the description on the HEX says it is gamma corrected when it is not.


      try it yourself: enter 0.5 on the rgb channels for a texture brush and do the same on a material color. You’ll get different hex values for the same linear rgb.

    • That bug is really bugging! The treatment of colors in Blender is partly still a mystery to me as for example the "Color Management's" transformation for converting your rendered image into a specific display device's color space. There's a gamma correction but I'm not sure whether this is all. The "Blender Manual" isn't very precise there. At least I know that as soon as I save my rendering as .exr-file, no "Color Management" is applied by Blender.

    • duerer 

      In Blender, the RGB and HSV/HSL values are in Scene Linear color space

      This is not correct imho; this is obviously not linear:

      Same with RGB (0.5, 0.5, 0.5). And like blanchsb mentioned, with Texture Paint Brushes, RGB and HSV are linear.

      The Hex values are always linear (see my beautiful image above), and if they are Gamma corrected, then with a Gamma = 1.


    • The gamma correction is given in the following equation from what I understand for everything else besides the brush color:
      It's a conversion based on what range the color value falls in (c represents an individual color channel)

    • blanchsb that is probably what they use in Blender then, but it is just formula; there is no such thing as an absolute Gamma correction formula; 'Gamma' is a bloated word, meaning something like 'to the power of'... a 'Gamma' of 1 (default in Blender Gamma Node!) would mean: raise to the power of 1, also known as: do nothing.

      Here's an image squared twice and put through a Gamma Node, set to 2 another two times, can you tell which is which? (of course you can't, because they are the same!):


    • spikeyxxx Gamma correction varies based user selection in the Render Color management tab (default is set to sRGB correction). But the value is also dependent on what the color value is. In most cases with blender the internal gamma correction is somewhere along the lines of 2.2. That node in your picture is in addition to that internal color correction value for most workspaces in blender.
      From the older Blender 2.79 manual:


      The latest manual just states it is being done but the value of internal correction should still be the same.

    • Where's Jonathon to pitch in. I figured he'd be all up in the color space know-how lol.

      Thanks for the comments all.
      I will be able to confirm that formula does the proper conversion in the other color spaces this weekend when I make a proper
      HEX to RGB to linear (gamma and un-gamma corrected) converter in blender to be used as a module.

      Goal is to have something available in Python to go from one end to the other and get the same value  every time that matches for the color. Working in the color spaces is difficult without the proper conversion formulas in place.

    • blanchsb you are riight, but I just wanted to say that Gamma is, in itself, a meaningless word:

    • Too bad they still use it daily in my blender workspace lol

    • Indeed! Unfortunately Shawn! 

      But the reality is, that when it comes to digital color, 97.7301% (this is just an estimate) of the people (including PS and so developers), do not know what they are talking about (and that includes me, of course!).

    • You can add me to that group. I at least figured out the brush colors though. I can successfully convert HEX to brush RGB in Blender every time now using a random HEX genetator.