I have 32 variables in my shader code and I think that's too many. How do I make less variables?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By exuin

I have 32 variables in my shader code and I think that’s too many. How do I make less variables?

I’m just looking for something that doesn’t require me to have so many different variables. I need to replace 4 colors with 4 other colors, but each color has 4 shades, so I have 32 colors in all.

uniform vec4 REPLACE_0_0 : hint_color = vec4(.902, .271, .224, 1);
uniform vec4 REPLACE_0_1 : hint_color = vec4(.678, .184, .271, 1);
uniform vec4 REPLACE_0_2 : hint_color = vec4(.471, .114, .31, 1);
uniform vec4 REPLACE_0_3 : hint_color = vec4(0.31, 0.114, 0.2981, 1);
const vec4 COLOR_0_0 = vec4(230.0/255.0, 69.0/255.0, 57.0/255.0, 1);
const vec4 COLOR_0_1 = vec4(173.0/255.0, 47.0/255.0, 69.0/255.0, 1);
const vec4 COLOR_0_2 = vec4(120.0/255.0, 29.0/255.0, 79.0/255.0, 1);
const vec4 COLOR_0_3 = vec4(79.0/255.0, 29.0/255.0, 76.0/255.0, 1);
uniform vec4 REPLACE_1_0 : hint_color = vec4(0.941, 0.71, 0.255, 1);
uniform vec4 REPLACE_1_1 : hint_color = vec4(0.812, 0.459, 0.169, 1);
uniform vec4 REPLACE_1_2 : hint_color = vec4(0.671, 0.318, 0.188, 1);
uniform vec4 REPLACE_1_3 : hint_color = vec4(0.49, 0.22, 0.2, 1);
const vec4 COLOR_1_0 = vec4(240.0/255.0, 181.0/255.0, 65.0/255.0, 1);
const vec4 COLOR_1_1 = vec4(207.0/255.0, 117.0/255.0, 43.0/255.0, 1);
const vec4 COLOR_1_2 = vec4(171.0/255.0, 81.0/255.0, 48.0/255.0, 1);
const vec4 COLOR_1_3 = vec4(125.0/255.0, 56.0/255.0, 51.0/255.0, 1);
uniform vec4 REPLACE_2_0 : hint_color = vec4(0.784, 0.831, 0.365, 1);
uniform vec4 REPLACE_2_1 : hint_color = vec4(0.388, 0.671, 0.247, 1);
uniform vec4 REPLACE_2_2 : hint_color = vec4(0.231, 0.49, 0.31, 1);
uniform vec4 REPLACE_2_3 : hint_color = vec4(0.184, 0.341, 0.325, 1);
const vec4 COLOR_2_0 = vec4(200.0/255.0, 212.0/255.0, 93.0/255.0, 1);
const vec4 COLOR_2_1 = vec4(99.0/255.0, 171.0/255.0, 63.0/255.0, 1);
const vec4 COLOR_2_2 = vec4(59.0/255.0, 125.0/255.0, 79.0/255.0, 1);
const vec4 COLOR_2_3 = vec4(47.0/255.0, 87.0/255.0, 83.0/255.0, 1);
uniform vec4 REPLACE_3_0 : hint_color = vec4(0.573, 0.91, 0.753, 1);
uniform vec4 REPLACE_3_1 : hint_color = vec4(0.31, 0.643, 0.722, 1);
uniform vec4 REPLACE_3_2 : hint_color = vec4(0.298, 0.408, 0.522, 1);
uniform vec4 REPLACE_3_3 : hint_color = vec4(0.227, 0.247, 0.369, 1);
const vec4 COLOR_3_0 = vec4(146.0/255.0, 232.0/255.0, 192.0/255.0, 1);
const vec4 COLOR_3_1 = vec4(79.0/255.0, 164.0/255.0, 184.0/255.0, 1);
const vec4 COLOR_3_2 = vec4(76.0/255.0, 104.0/255.0, 133.0/255.0, 1);
const vec4 COLOR_3_3 = vec4(58.0/255.0, 63.0/255.0, 94.0/255.0, 1);

I tried making an array but it just gave me the error Array initialization is only supported on high-end platform!. Is this related to the fact that I’m using GLES2?

:bust_in_silhouette: Reply From: Zylann

If your goal is purely to reduce the number of uniforms, find out which ones can be calculated from others, and compute them instead of exposing as uniforms.
However, your shader does not have that many uniforms (does it? Is it a warning from your graphics driver?). const don’t cost anything.
I don’t think uniform arrays are supported in Godot 3, so the only way to have something similar is to use a texture, unfortunately.

If what you are doing is a palette swap, using a 32x1 texture with the 32 colors lined up is the easiest way. You could then use specific single-channel grayscale pixels in your sprites to replace with real colors using the palette more easily.
Then you can change the replaced colors by changing the palette.

So instead of using 32 if conditionals and 32 expensive texture samplings in your shader, you can index using a single call: take source “color” in the red channel, map it to the X and Y coordinate within the palette texture, sample the actual color.

float color_index = texture(TEXTURE, UV).r;
vec2 palette_position = vec2(color_index, 0.0);
COLOR = texture(palette, palette_position);

There are variations of this techniques of course, where you can use the green channel as Y coordinate in your palette (so R=X for hue, G=Y for shade).

If your goal is to really replace real colors with other real colors, then sorry, you will probably need 32 if conditionals and plenty of uniforms. Using a palette for this would cut down uniform count, but would become 32 expensive texture() calls.

If none of this sounds workable for you, maybe reconsider what you actually want to do, and why you want a shader to do this in the first place.

I tried making some of the uniforms consts but it just messes up the shader. I guess consts can’t be calculated from uniforms. I think I can’t use arrays because of GLES2, since I also can’t use dynamic loops apparently either because of it. What I’m trying to do is to let the user choose the color of the character’s sprite, so I’m replacing colors in the character’s sprite with the user’s choices.

exuin | 2020-09-23 19:25

I guess consts can’t be calculated from uniforms

Just make local vars then.

What I’m trying to do is to let the user choose the color of the character’s sprite, so I’m replacing colors in the character’s sprite with the user’s choices.

Sounds like a good use case for palette swap then. Or break up the sprite into parts which you use modulate on, or even directly modify the texture in GDScript (since it has to be done only once after the player made their choice)

Zylann | 2020-09-24 12:48

Actually, I’m making a character sprite generator. So the user will be able to set the color to any color, multiple times. I did replace a bunch of consts with local variables then.

exuin | 2020-09-24 17:36