How to desaturate gui element

I am working on an interface with Lemur, and I need to gray out an icon when it is in a locked or disabled state.

I am using a Container with a QuadBackgroundComponent to display an icon’s texture. I also am using the .setColor(ColorRGBA.Red()); method to display the icon as red when it is in a locked state. But I also need to display it in a disabled state too, which would best be represented by desaturating the icon and graying it out.

I know that I can manually make a grayed out copy of each icon’s texture that is desaturated using an external image editor, but doing it with code to alter a single texture would be optimal.

Is there already a way to do this with Lemur?
If not, then I would be curious to know what shader the QuadBackgroundComponent uses so I could add support for desaturating based on a float value :slightly_smiling_face:

Edit: for reference as to what I am trying to do: I am trying to make a talent/skill tree, similar to this one from world of warcraft that has talent icons grayed out unless the player has points in them.

1 Like

you could use Image class utility to claculate gray-scale and edit texture as alternative one too.

for example 100,50,150 → 100,100,100

could do it in shader too. (some special shader for icons)

since as i understand doing “overlay” will not work for you, since it will only just make it darker?

1 Like

(aside from direct image manipulation as oxplay2 mentions)

The shader is just JME’s unshaded material. It doesn’t provide a way to do the desaturation directly as far as I know.

As to customizing the material, someone pointed out recently that I messed up these components with respect to overriding their materials. Until I fix that, you’d have to fork your own version of the component to customize the material. (Basically, there is a createMaterial() method that subclasses can override but it’s not hooked up right.) If you build Lemur from source then you may get that sooner. I don’t plan to cut a new release before the end of the year but it could happen.


I think I may still be able to do this just by modifying the Unshaded.frag and Unshaded.j3md files.

I was able to access the material of my QuadBackgroundComponent using this code and was able to turn it blue:

quadBackground.getMaterial().getMaterial().setColor("Color", ColorRGBA.Blue);

So if I add support for denaturation in the existing Unsahded madef, then I should be able to just pass in a new float param for desaturation and can get away with modifying the background components existing material rather than creating a new one with a custom shader.

Thanks for the help :slightly_smiling_face:

I could also submit a PR to the exiting Unshaded fragment shader, in case anyone else thinks it would be useful to have this feature.

i think Paul just mean “setMaterial” would be harder to do currently?

I don’t think I would need to do setMaterial() though, if I just modify the Unshaded.j3md shader that the background components are already using.

ok, if you have JME fork, since Unshaded.j3md is JME common file. (or maybe its some other file?)

Without fork it would be harder to replace it.

1 Like

Oh you’re right, I don’t know why I was thinking it was a separate import like the test assets. So it would likely be easier to make the changes in Lemur for a custom background component as mentioned, and build that from source rather than the entire engine.

1 Like

exactly, thats what Paul mean - i belive.

1 Like

Or you can override

and pass your own material.

1 Like

sounds even better, if GuiGlobals is possible to “switch” to your version one.

assuming it might be as private final field, it would be only possible via “un-safe” reflection…

1 Like

Yes, that is possible


Sounds promising, I will give that a try! That would be the best way and should save me from having to rebuild a whole library and change my imports. (Edit: i misunderstood again and missed that the method you linked was protected, at a glance I excitedly and mistakenly saw “public” lol so I guess I still do need to rebuild libraries, but your way should save me from having to make a custom backgroundcomponenet still I think)

Nonetheless, I started making the changes to the Unshaded matdef and frag files, since some modified version of that will be necessary no matter what.

Here’s a link to an updated version of Unshaded.frag on my own branch with a few lines of code added to support desaturation. I could submit a PR if anyone thinks it would be useful to have the changes in core, otherwise the link will still be here

1 Like

vec3 gray = vec3(dot(vec3(0.2126,0.7152,0.0722), color.rgb));

i dont understand.
Why not just:

vec3 gray = vec3(0.5,0.5,0.5);



color = vec4(mix(color.rgb, gray, m_DesaturationValue), 0.0);

i belive 0.0 should be 1.0 or color.a as alpha?

would like to understand your solution here.

1 Like

Good catch, I copied the code from a method in one of my other shaders where the alpha value is overwritten later so I must not have noticed that it gets set to 0.0, I will need to change that. Thanks.

I found the desaturation code a long time ago in a quick google search when I was working on another shader specifically for removing the color from areas of the world. I may also be wrong to use the term “grayed out” interchangeably with “desaturate” since grayed out could imply an entirely gray block with no contrast, wheras desaturated means that all the color was sucked out of the image.

I am no expert when it comes to this type of color math, especially something mildly complicated like desaturation. But I think the way you suggest would result in an entirely gray texture when the desaturation value is 1.0, since every pixel would be using the same shade of gray as its final mix color

So I think that is why the code I found has that equation with the dot product function: in order to determine the proper shade of gray each pixel should display when fully desaturated, so that there is still accurate contrast in the image once the color is all gone.

I tried a few other things before I googled it and got this equation, such as averaging the r/g/b values of each pixel (not my best idea lol). But nothing I tried from my own ideas worked, and then I googled it and realized that there seems to be this specific equation for accurate desaturation.

1 Like

Short version of yaRnMcDonuts’s answer: because your version would treat every pixel as a single gray value and his version converts the existing texture color value to its gray desaturated equivalent.