Indirect index into implicitly-sized array

I want to use multiple colors that are stored in an array. Here I set red and green for example:

  targetColorsRGBA = new float[] {1, 0, 0, 1, 
                              0, 1, 0, 1};     

  shader.set("targetColors", targetColorsRGBA, 4);
  shader.set("targetColorsCount", (int) (targetColorsRGBA.length/4));

Now how can I use that array? If I use targetColors[i] I get an error.

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

#define PROCESSING_TEXTURE_SHADER

uniform sampler2D texture;
varying vec4 vertTexCoord;

uniform vec4[] targetColors;
uniform int targetColorsCount;



void main() {


  vec4 result = texture2D(texture, vertTexCoord.st).rgba;

  for (int i = 0; i < targetColorsCount; i++) { // targetColors.length doesnt work

    result.r += targetColors[i].r; // <<<< help!!!
  }


  gl_FragColor = result;

}

This works but it is an insult to programming:

for (int i = 0; i < targetColorsCount; i++) { 
    if (i == 0) {
      result.r += targetColors[0].r;
    }
    else if (i == 1) {
      result.r += targetColors[1].r;
    }
    else if (i == 2) {
      result.r += targetColors[2].r;
    }
  }

please help!

Answers

  • edited September 2015 Answer ✓

    You have to implicitly set the array's size inside the shader:

    // First class arrays require GLSL version 1.20 (or higher)
    #version 120
    
    #define PROCESSING_TEXTURE_SHADER
    
    uniform sampler2D texture;
    varying vec4 vertTexCoord;
    
    uniform vec4[2] targetColors; // Set the array's size
    
    void main() {
        vec4 result = texture2D(texture, vertTexCoord.st).rgba;
        for(int i = 0; i < targetColors.length(); i++) // Use length() instead of length
            result.r += targetColors[i].r;
        gl_FragColor = result;
    }
    

    And in case you wanna use dynamically sized arrays, just recompile the shader every time the array's size has changed:

    void setup() {
    
        size(600, 300, P2D);
    
        PImage someImage = loadImage("http" + "://forum.processing.org/two/uploads/userpics/970/n4IUAUUTANWJL.png");
    
        updateShader(new float[] { 1, 0, 0, 1, 0, 1, 0, 1 });
        image(someImage, 0, 0, 300, 300);
    
        updateShader(new float[] { 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1 });
        image(someImage, 300, 0, 300, 300);
    
    }
    
    void updateShader(float[] targetColorsRGBA) {
    
        String[] vertSource = {
            "#version 120", // First class arrays require GLSL version 1.20 (or higher)
    
            "uniform mat4 transform;",
    
            "attribute vec4 vertex;",
            "attribute vec4 color;",
            "attribute vec2 texCoord;",
    
            "varying vec4 vertColor;",
            "varying vec2 vertTexCoord;",
    
            "void main() {",
                "gl_Position = transform * vertex;",
    
                "vertColor = color;",
                "vertTexCoord = texCoord;",
            "}"
        };
        String[] fragSource = {
            "#version 120", // First class arrays require GLSL version 1.20 (or higher)
    
            "#define PROCESSING_TEXTURE_SHADER",
    
            "uniform sampler2D texture;",
            "varying vec4 vertTexCoord;",
            "uniform vec4[" + (targetColorsRGBA.length / 4) + "] targetColors;", // Set the array's size
            "uniform int targetColorsCount;",
    
            "void main() {",
                "vec4 result = texture2D(texture, vertTexCoord.st).rgba;",
                "for(int i = 0; i < targetColors.length(); i++)", // In GLSL arrays have a length() function
                    "result.rgb += targetColors[i].rgb;",
                "gl_FragColor = result;",
            "}"
        };
        PShader myShader = new PShader(this, vertSource, fragSource);
    
        myShader.set("targetColors", targetColorsRGBA, 4);
    
        shader(myShader);
    
    }
    

    Btw.: Some graphics card drivers are less strict and do not force you to set the required GLSL version, but most of them do and will simply crash your sketch if you are using features of version 1.10 and higher.

  • Thanks man, that helped a lot!

  • I want to compile the shader within processing now but I get version '120' is not supported. How can I figure out what it should be?

  • Using a Mac? Because the PShader(PApplet, String[], String[]) doesn't seem to work as expected on OS X.

  • yeah I'm working on my last Mac :) Been using mac for around 10 years now and i'm sick of it...

  • Well there's a simple solution, just by a Windows PC. :P

    Just kidding. ;) What GLSL version do you have?

    import com.jogamp.opengl.GL2GL3;
    
    void setup() {
        size(100, 100, P3D);
        GL2GL3 gl = ((PJOGL)g.beginPGL()).gl.getGL2GL3();
        println("GLSL " + gl.glGetString(GL2GL3.GL_SHADING_LANGUAGE_VERSION));
    }
    
  • I have a beast from a pc, half the price of a mac, double the performance :) And once this monitor come out i'm even more happy :)

    http://www.144hzmonitors.com/monitors/asus-pg279q-27-inch-1440p-144hz-ips-g-sync-monitor/

    I have GLSL 4.10.

    About the PShader(PApplet, String[], String[]) not working as expected, i assume this is a processing bug?

    I changed the code you posted to use '#version 410' but now I get a beautiful error:

    Cannot compile vertex shader:ERROR: 0:3: 'attribute' : syntax error: syntax error

    Does this run for you on the pc (I don't have a monitor at the moment :-* ) I wan't a bare bones example to report as a bug.

    It seems like the shader can't init. (Make a call to myShader.init(); gives the same error).

    void setup() {
    
        size(600, 300, P2D);
    
        PImage someImage = loadImage("http" + "://forum.processing.org/two/uploads/userpics/970/n4IUAUUTANWJL.png");
    
        updateShader();
        image(someImage, 0, 0, 300, 300);
    
    }
    
    void updateShader() {
    
        String[] vertSource = {
            "#version 410", // First class arrays require GLSL version 1.20 (or higher)
    
            "uniform mat4 transform;",
    
            "attribute vec4 vertex;",
            "attribute vec4 color;",
            "attribute vec2 texCoord;",
    
            "varying vec4 vertColor;",
            "varying vec2 vertTexCoord;",
    
            "void main() {",
                "gl_Position = transform * vertex;",
    
                "vertColor = color;",
                "vertTexCoord = texCoord;",
            "}"
        };
        String[] fragSource = {
            "#version 410", // First class arrays require GLSL version 1.20 (or higher)
    
            "#define PROCESSING_TEXTURE_SHADER",
    
            "uniform sampler2D texture;",
            "varying vec4 vertTexCoord;",
    
            "void main() {",
                "vec4 result = texture2D(texture, vertTexCoord.st).rgba;",
                "gl_FragColor = result;",
            "}"
        };
    
        PShader myShader = new PShader(this, vertSource, fragSource);
    
        shader(myShader);
    
    }
    
  • edited October 2015

    Nice monitor! :D I have GLSL 4.5.

    Your code actually runs as my nvidea drivers are pretty forgiving, but it shouldn't. attribute and varying are deprecated since GLSL version 1.3 (if I remember correctly), you have to replace attribute with in and varying with out in the vertex shader and varying with in in the fragment shader. Additionally you'd have to add your own out vec4 for gl_FragColor (it's also deprecated).

    void setup() {
        size(600, 300, P2D);
        PImage someImage = loadImage("http" + "://forum.processing.org/two/uploads/userpics/970/n4IUAUUTANWJL.png");
        updateShader();
        image(someImage, 0, 0, 300, 300);
    }
    
    void updateShader() {
        String[] vertSource = {
            "#version 410",
    
            "uniform mat4 transform;",
    
            "in vec4 vertex;",
            "in vec2 texCoord;",
    
            "out vec2 vertTexCoord;",
    
            "void main() {",
                "gl_Position = transform * vertex;",
                "vertTexCoord = texCoord;",
            "}"
        };
        String[] fragSource = {
            "#version 410",
    
            "uniform sampler2D texture;",
    
            "in vec2 vertTexCoord;",
    
            "out vec4 fragColor;",
    
            "void main() {",
                "vec4 result = texture2D(texture, vertTexCoord).rgba;",
                "fragColor = result;",
            "}"
        };
        PShader myShader = new PShader(this, vertSource, fragSource);
        shader(myShader);
    }
    

    This, should work, also I can't really test version specific changes as my drivers are too forgiving. :D

  • Long live teamviewer, I looked at my pc and I have GLSL 4.5 as well. And yeah it seems to be very forgiven. On my pc it runs as well, on my mac I get:

    Cannot compile fragment shader:ERROR: 0:7: Invalid call of undeclared identifier 'texture2D'ERROR: 0:8: Use of undeclared identifier 'result'

    I founded that texture2D is replaced by texture. Which seems correct since using this get rids of the error.

    However now I get Cannot link shader program:Unknow error (<the spelling error is correct!). It happens on shader(myShader);.

    Maybe I was a bit early with opening an issue. Although it was opened for the Sting[] String[] issue.

    https://github.com/processing/processing/issues/3969

  • Ok I know a bite more. This should help you as well :D

    Another thing is that you don't longer need #define PROCESSING_TEXTURE_SHADER" in Processing 3. These pre-processor directives were required up to 2.1 -codeanticode

    Then in the vertex shader you have out vec2 vertTexCoord;. In the fragment you have in vec4 vertTexCoord;". Those 2 should match.

    This runs on my mac!

    void setup() {
        size(600, 300, P2D);
        PImage someImage = loadImage("http" + "://forum.processing.org/two/uploads/userpics/970/n4IUAUUTANWJL.png");
        updateShader();
        image(someImage, 0, 0, 300, 300);
    }
    
    void updateShader() {
        String[] vertSource = {
            "#version 410", // First class arrays require GLSL version 1.20 (or higher)
    
            "uniform mat4 transform;",
    
            "in vec4 vertex;",
            "in vec4 color;",
            "in vec2 texCoord;",
    
            "out vec4 vertColor;",
            "out vec2 vertTexCoord;",
    
            "void main() {",
                "gl_Position = transform * vertex;",
    
                "vertColor = color;",
                "vertTexCoord = texCoord;",
            "}"
        };
        String[] fragSource = {
            "#version 410", // First class arrays require GLSL version 1.20 (or higher)
    
            "uniform sampler2D texture;",
    
            "in vec2 vertTexCoord;",
    
            "out vec4 fragColor;",
    
            "void main() {",
                "vec4 result = texture(texture, vertTexCoord.st).rgba;",
                "fragColor = result;",
            "}"
        };
        PShader myShader = new PShader(this, vertSource, fragSource);
        shader(myShader);
    }
    

    I went for vec2 since it's probably a few nanoseconds faster :!!

  • edited October 2015

    Cannot compile fragment shader:ERROR: 0:7: Invalid call of undeclared identifier 'texture2D'ERROR: 0:8: Use of undeclared identifier 'result'

    Oh, sorry! texture2D() is also depricated! Use texture() instead. Updated the code.

    Then in the vertex shader you have out vec2 vertTexCoord;. In the fragment you have in vec4 vertTexCoord;". Those 2 should match.

    Sorry again, I copied it from your code, although it shouldn't matter because of vector packing.

    Another thing is that you don't longer need #define PROCESSING_TEXTURE_SHADER" in Processing 3. These pre-processor directives were required up to 2.1 -codeanticode

    Yeah, because it will actually default to PROCESSING_TEXTURE_SHADER.

    I went for vec2 since it's probably a few nanoseconds faster

    Probably not, as modern graphics cards will compute 32bit (one vec4) or 64bit (two vec4) at once. Working with smaller data packets can actually slow down the shader execution, but that's up to the GLSL optimizer. Anyways, it's of course always a good practice to use as few memory/instructions as possible.

  • Good point about the byte size, didn't think of that.

Sign In or Register to comment.