Perform a shader on every channel

Inside a fragment shader I have a loop like this:

    for (float x = 0; x < vertTexCoord.x; x += xInc) {
    vec2 pos = vec2(x, vertTexCoord.y);
    fcount += texture(texture, pos).r; // red for now
}

In this case we use red, but I wan't to be able to switch to green, blue and alpha as well. What is an easy way to switch to whatever channel I want from inside processing? (And how will that solution impact performance)

Answers

  • Create a shader for each channel or add a number option, maybe? Shader for each channel might be the least prone to errors (I've had shaders ignoring variables that were sent from Processing).

  • edited October 2015 Answer ✓

    You can access vector fields just like an array. Just add some uniform that holds the channel index, let's say channel, and use it in your loop like this:

    fcount += texture(texture, pos)[channel];
    

    And here a complete example (that probably won't work on your Mac):

    PShader myShader;
    
    void setup() {
        size(800, 800, P2D);
        String[] vertSource = {
            "uniform mat4 transform;",
            "attribute vec4 vertex;",
            "void main() {",
                "gl_Position = transform * vertex;",
            "}"
        };
        String[] fragSource = {
            "uniform int channel;",
            "uniform vec2 mouse;",
            "void main() {",
                "gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);",
                "gl_FragColor[channel] = 1.0;",
            "}"
        };
        myShader = new PShader(this, vertSource, fragSource);
        myShader.set("channel", 0);
    }
    
    void draw() {
        filter(myShader); // Filter basically applies the shader to the viewport
    }
    
    void keyPressed() {
        if(key != CODED && key >= '1' && key <= '3')
            myShader.set("channel", key - '1');
    }
    

    Press 1, 2, or 3 to change the selected channel.

    And no, this should have no noticeable impact on the performance.

  • Really cool how flexible the vectors are! Thanks again. I learned so much from you today :)

  • Haha :D Don't mention it! I'm looking forward to see some "funky" shader based sketches from you... maybe a Depth of Field. ;)

  • edited October 2015

    I was working on a Integral image shader yesterday. I made that before using the cpu. With a Integral Image you count up the total seen from the left top corner. So If I have a image and those are the values for red for example:

    [000][012][004][202][000]
    [016][100][008][000][120]
    

    Then the integral Image looks like:

    [000][012][016][218][000]
    [016][128][152][370][490]
    

    Now to get the average color of an area you can use:

    color value(int x1, int y1, int x2, int y2, int[] layer) {
        int minX = min(x1, x2);
        int minY = min(y1, y2);
        int maxX = max(x1, x2);
        int maxY = max(y1, y2);
    
        int nOfPixels = (maxX-minX) * (maxY-minY);
    
        if (nOfPixels == 0) return -1;
    
        final int w = width;
    
        int tc = 0; // total count
    
        // right bottom corner
        int i = maxY*w+maxX;
    
        tc += layer[i];
    
        // left top corner
        i = minY*w+minX;
        tc += layer[i];
    
        // left bottom corner
        i = maxY*w+minX;
        tc -= layer[i];
    
        // right top corner
        i = minY*w+maxX;
        tc -= layer[i];
    
        return tc / nOfPixels;
      }
    

    So this is really fast (once the Integral image is computed).

    But in the evening I figured it's better to use cuda for that :) The problem with a shader is that for the bottom right pixel for example it has to loop over every pixel in the image to compute the total count for that one. This slows down the shader a lot.

    In case your interested, this is how far I came. (I didn't do the channel thing yet since I was to tired)

    #version 410
    
    uniform sampler2D texture;
    
    in vec4 vertTexCoord;
    
    uniform vec2 texOffset;
    
    out vec4 fragColor; 
    
    
    
    void main() {
    
        float fcount = 0;
    
        float xInc = texOffset.x;
    
    
        for (float x = 0; x < vertTexCoord.x; x += xInc) {
            vec2 pos = vec2(x, vertTexCoord.y);
            fcount += texture(texture, pos).r;
        }
    
    
    
        // now we now how much red,
        // how do we store this in a rgba value?
    
        // let's say the value is 4174
        // that would lead in:
        // r 0.0
        // g 16.0
        // b 78.0
        // a 0.0
    
        // getting the integer of that color in processing again gives us 4174!
    
    
        // can we do what we want without using alpha?
        // or can we use alpha and also use a shader for whatever we want to do next?
        // alpha comes last anyway so it probably only limits our range?
        // use 1-alpa for now?
    
        int count = int(fcount * 255.0);
    
        float r = ((count >> 16) & 0xff) / 255.;
        float g = ((count >> 8) & 0xff) / 255.;
        float b = ((count) & 0xff) / 255.;
        float a = ((count >> 24) & 0xff) / 255.;
    
        // alpha is a serious problem..
        fragColor = vec4(r, g, b, 1);
    }
    

    I made this before using a integral image:

    10000 rectangles are drawn each frame with the average of the color of the image for that rectangle.

  • O yeah, I only had the horizontal pass ready in the shader!

Sign In or Register to comment.