GLSL: Issue with binding Uniform Array to GLSL Shader - Outputting Black

Hi there,

I am working on a GLSL shader where I map vertex values to a color palette based on a specified min/max range.

ghsl_colorlist.gh (14.1 KB)

As shown in the image below:

// Fragment

#version 330

// --- Inputs from Vertex Shader ---
in float v_normalized; 

// --- Output ---
out vec4 fragment_color;

void main() {
    // 1. Hardcoded Color Palette (Your 10 CFD colors)
    // We define these locally to ensure they always work regardless of external input.
    vec3 colors[10];
    colors[0] = vec3(75.0, 107.0, 169.0) / 255.0;  // Blueish
    colors[1] = vec3(115.0, 147.0, 202.0) / 255.0;
    colors[2] = vec3(170.0, 200.0, 247.0) / 255.0;
    colors[3] = vec3(193.0, 213.0, 208.0) / 255.0;
    colors[4] = vec3(245.0, 239.0, 103.0) / 255.0;
    colors[5] = vec3(252.0, 230.0, 74.0) / 255.0;   // Yellowish
    colors[6] = vec3(239.0, 156.0, 21.0) / 255.0;
    colors[7] = vec3(234.0, 123.0, 0.0) / 255.0;
    colors[8] = vec3(234.0, 74.0, 0.0) / 255.0;
    colors[9] = vec3(234.0, 38.0, 0.0) / 255.0;    // Reddish

    // 2. Mapping Logic
    // With 10 colors, there are exactly 9 segments to interpolate between.
    float seg = v_normalized * 9.0; 
    
    // Find the two nearest color indices
    int i1 = int(floor(seg));
    int i2 = int(ceil(seg));
    
    // Clamp indices to stay within array bounds [0, 9]
    i1 = clamp(i1, 0, 9);
    i2 = clamp(i2, 0, 9);
    
    // 3. Linear Interpolation (Lerp)
    // fract(seg) gives the fractional part (0.0 to 1.0) between the two indices
    vec3 finalRGB = mix(colors[i1], colors[i2], fract(seg));

    // 4. Final Output (Opaque color)
    fragment_color = vec4(finalRGB, 1.0);
}

  • Bottom version: I attempted to use an input uniform vec3 color_list[] to pass the same colors, but the result is completely black.
// Fragment
#version 330

// --- Color Inputs from Grasshopper ---
// We define a fixed size array. Ensure your input list has 10 colors.
uniform vec3 color_list[10]; 
uniform int color_count;

// --- Data Inputs from Vertex Shader ---
in float v_normalized; 

// --- Output ---
out vec4 fragment_color;

void main() {
    vec3 final_rgb = vec3(0.0);

    // 1. Safety Check: Ensure we have colors to work with
    if (color_count > 1) {
        // Calculate the position within the 9 segments (for 10 colors)
        float seg = v_normalized * float(color_count - 1);
        int i1 = int(floor(seg));
        
        // 2. Robust Array Access
        // Using a constant-length loop is the most compatible way to access 
        // uniform arrays in many GLSL environments.
        for(int i = 0; i < 10; i++) {
            if(i == i1) {
                vec3 c1 = color_list[i];
                vec3 c2 = color_list[min(i + 1, color_count - 1)];
                
                // 3. Range Correction
                // If Grasshopper sends 0-255, we convert to 0.0-1.0
                if (max(c1.r, max(c1.g, c1.b)) > 1.0) c1 /= 255.0;
                if (max(c2.r, max(c2.g, c2.b)) > 1.0) c2 /= 255.0;
                
                // 4. Linear Interpolation between the two colors
                final_rgb = mix(c1, c2, fract(seg));
                break; 
            }
        }
    } else if (color_count == 1) {
        final_rgb = color_list[0];
        if (max(final_rgb.r, max(final_rgb.g, final_rgb.b)) > 1.0) final_rgb /= 255.0;
    }

    // 5. Final Fragment Output
    fragment_color = vec4(final_rgb, 1.0);
}

Could anyone point out what I might be doing wrong or how to properly bind the color list? Any guidance would be greatly appreciated.

Thanks in advance!

LLMs are not great at this kind of thing. The custom code does a lot of unnecessary actions without changing much, when in reality what you needed to do to is change two components of the original:

  1. Instead of building the colors array in main() and referring to that in the call to mix(), simply refer to color_list in mix().
  2. The shader is expecting vectors from color_list, so provide it with vectors instead of colors.

The result is as as follows:


ghsl_colorlist_ch_01.gh (20.2 KB)

Hi @Cole_Howell1,

Thank you so much for your previous response.
As a GLSL beginner, your help has been incredibly valuable to me.

I have one additional question:
is there a way to avoid hardcoding the array size and color segments?
Currently, the script uses a fixed size (e.g., color_list[10]). What if I’d like to make it work with multiple color sets where the number of colors varies between sets (as seen in the new attachment).
ghsl_colorlist_w_multi_color_set.gh (17.4 KB)

Is it possible to let the shader to automatically adapt to the length of the input list rather than having to manually change the code each time?

Thanks.

GLSL is built to be as lightweight as possible, and as such lacks a lot of features that other modern languages have. Arrays must have a known length at compile time (in this case, whenever you close the edit code window), so having a dynamic array is not really feasible in this context - if you were making the program yourself, there might be a way.

The work around in our case is to give an array larger than anything we would expect to surpass, then only populate the desired portion with our colors. We then only read the segments of the provided colors and ignore the rest. Lastly, we pad our color list in Grasshopper to be this expected length. Modifying the code to do this is not too bad as we are already passing color_count to the shader, so we just use that instead of the preset value of ten.

Here is the result:


ghsl_colorlist_w_multi_color_set_ch_01.gh (18.8 KB)

2 Likes

Wow amazing, that do the trick. Thank you so much!