Passing an array to a shader?

Can I somehow pass an array to a shader, or get acces to its contents?

Its an array of up to maybe 60 images and I want to read their pixel colours.

@Kirl Instead of passing 60 images to the shader, pass 1 at a time. If I’m not mistaken, you can’t get any info back from a shader. So if you read the pixel colors, what info are you after.

@Kirl To read information and retrieve data from shaders, look at the buffer in the reference book. Basically saying myMesh:buffer("variableName"), you can get a variable from a shader.

The array saves the last 60 frames from the camera and I make a new image by combining parts from those images. I have it working without a shader by copying regions and/or individual pixels from those images, but that’s terribly slow as expected.

So every frame I make one new image from all of those 60 images.

I was hoping I could maybe do everything inside of a shader to speed things up?

@CamelCoder Do you have a small example of using buffer to retrieve info from a shader. @Kirl What are you trying to do by making a new image from 60 images.

You can make some trippy effects by drawing parts of the screen later than others. I use a noise where white is the most recent frame and black 60 frames back, or from top to bottom where the top is now and the bottom 60 frames back.

It works, but it’s too slow.

So I’m not trying to read stuff out from the shader (although that sounds useful) but rather get that array of 60 images into the shader. If that’s at all possible.

@Kirl If I understand shaders correctly, which I may not, they work on one image at a time. You don’t send an array of images to it, you send 1 image to it. That doesn’t mean you can’t send 60 images to it one after the other. I’ll have to look thru my shader examples, but I might have something where i take an image and create a different image seperate from the first. If that’s the case, the 60 images can be sent one after the other and all of them can be use to create another seperate image. I’ll let you know what I find.

@Kirl I found the shader I was thinking of. I’m passing 2 images to it and any pixels that are different by an amount I choose, I turn them yellow. I use the camera and every 2 images gets sent to the shader to compare them. So if I can pass 2 images to the shader, maybe you can do 60. I’ll have to play with it more to see what happens if I try more images.

@Kirl Are you trying to do something like this. This uses the camera and each frame part of the image is moved down a section of the screen. You can change nbr to change the number of sections shown.

displayMode(FULLSCREEN)

function setup()
    nbr=30
    h=HEIGHT//nbr
    imgTab={}
    for z=1,nbr do
        table.insert(imgTab,image(WIDTH,h))
    end
end

function draw()
    background(40, 40, 50)
    collectgarbage()
    for a,b in pairs(imgTab) do
        sprite(b,WIDTH/2,HEIGHT-(h*a)+h/2)
    end
    img=image(CAMERA) 
    if img~=nil then
        imgTab[1]=img:copy(0,HEIGHT-h,WIDTH,h)
        for z=nbr,2,-1 do
            imgTab[z]=imgTab[z-1]:copy(0,0,WIDTH,h)   
        end  
    end
    fill(255)
    text(1/DeltaTime//1,WIDTH/2,HEIGHT-30)
end

@Kirl Here’s an example of passing multiple images to a shader. The only problem is an error on the line m.shader.tbl trying to create a table. I don’t know shaders well enough to know why, so maybe someone else can fix it. The second version sends 3 seperate images to the shader. That one works.

This version sends a table to the shader, but causes an error.

function setup() 
    img1=readImage("Planet Cute:Enemy Bug")
    img2=readImage("Planet Cute:Character Horn Girl")
    img3=readImage("Planet Cute:Star")
    m=mesh()
    m:addRect(300,300,img1.width,img1.height)
    m.shader=shader(Shader.vs,Shader.fs)

    m.shader.tbl={img1,img2,img3}    -- this line causes an error
end

function draw() 
    background(40, 40, 50) 
    m:draw()
end

Shader = 
    {   vs = [[
        uniform mat4 modelViewProjection;
        attribute vec4 position; 
        attribute vec2 texCoord;
        varying highp vec2 vTexCoord;    
        void main() 
        {   vTexCoord = texCoord;
            gl_Position = modelViewProjection * position;
        }]],
   
        fs = [[
        uniform lowp sampler2D tbl[3];              // 3 = number of images
        varying highp vec2 vTexCoord; 
        mediump vec4 col1;
        void main() 
        {   
            col1=vec4(0.,0.,0.,0.);
            for (lowp int z=0;z<3;z++)              // 3 = number of images
                col1 = col1 + texture2D( tbl[z], vTexCoord);
            gl_FragColor = col1/3.;                 // 3 = number of images
        }]]
    }

This version sends 3 different images to the shader and combines them.


function setup() 
    img1=readImage("Planet Cute:Enemy Bug")
    img2=readImage("Planet Cute:Character Horn Girl")
    img3=readImage("Planet Cute:Star")
    m=mesh()
    m:addRect(300,300,img1.width,img1.height)
    m.shader=shader(Shader.vs,Shader.fs)

    m.shader.t1=img1
    m.shader.t2=img2
    m.shader.t3=img3
end

function draw() 
    background(40, 40, 50) 
    m:draw()
end

Shader = 
    {   vs = [[
        uniform mat4 modelViewProjection;
        attribute vec4 position; 
        attribute vec2 texCoord;
        varying highp vec2 vTexCoord;    
        void main() 
        {   vTexCoord = texCoord;
            gl_Position = modelViewProjection * position;
        }]],
   
        fs = [[
        uniform lowp sampler2D t1;  
        uniform lowp sampler2D t2;
        uniform lowp sampler2D t3;
        varying highp vec2 vTexCoord; 
        mediump vec4 col1;
        void main() 
        {   
            mediump vec4 col1 = texture2D( t1, vTexCoord);
            mediump vec4 col2 = texture2D( t2, vTexCoord);
            mediump vec4 col3 = texture2D( t3, vTexCoord);
            gl_FragColor = (col1+col2+col3) / 3.;
        }]]
    }

thanks dave! I’ll look into your examples! =)

Below is an example of the kind of effects I’d like to do with shaders, the effect doen’t really work at these speeds. It can be a great effect with a static camera especially, anything that moves will deform while the rest of the scene appears normal.

-- Cam

-- Use this function to perform your initial setup
function setup()
    cameraSource(CAMERA_BACK)
    
    frames = {}
    frameBuffNr = 30
    
    parameter.boolean("toggleMode", true)
    parameter.integer("frameBuffNr", 1, 60, frameBuffNr)
    parameter.number("waves", 0, .1, .01)
    
    pic = image(400,400)    -- eventual resolution
end

function p2pNoise(pic1, pic2)
    rx,ry = pic1.width/pic2.width, pic1.height/pic2.height
    for i=1, pic2.width do
        for j=1, pic2.height do
            fi = (noise(i*waves, j*waves)+1)/2 * #frames //1 +1   -- frame index
            
            c = color( frames[fi]:get(i*rx//1, j*ry//1) )
            pic2:set(i,j, c)
        end
    end
    sprite(pic, WIDTH/2, HEIGHT/2 ,WIDTH, HEIGHT)
end

function p2pScan(pic1, pic2)
    steps = frameBuffNr
    step = HEIGHT/steps
    for i=1, HEIGHT, step do
        local r = i/HEIGHT
        fi = math.ceil(r * #frames) -- (math.sin(r * math.pi) +1)/2 * #frames //1 +1 --
        il = frames[fi]:copy(1,math.ceil(r*pic1.height), pic1.width, math.ceil(pic1.height/steps))
        sprite(il, WIDTH/2, i+math.ceil(pic1.height/steps)/2, WIDTH, step)
    end
end

-- This function gets called once every frame
function draw()
    -- This sets a dark background color 
    background(40, 40, 50)

    -- Do your drawing here
    collectgarbage()
    
    while #frames > frameBuffNr do
        table.remove(frames)
    end
    
    img = image(CAMERA)
    if img ~= nil then
        table.insert(frames, 1, img)
        
        if toggleMode == true then   p2pScan(img, pic)
        else                         p2pNoise(img, pic) end
            
        sprite(img, 100,100, 200,200)
    end
    
    text(1//DeltaTime, WIDTH/2,HEIGHT-50)
end

@Kirl Tried your latest code. Keeps crashing after a few seconds. I thought you needed collectgarbage(), but you already have it. Added a collectgarbage() inside the while loop and that seemed to fix the crashes. This runs at 0 to 5 frames/sec on my iPad Air.

EDIT: Still crashes, just takes longer for it to happen.

@Kirl Here an example using a shader. I take an image and randomly shift lines of 10 pixels back and forth. You can add code to move the lines more smoothly if you want. This runs a 6 FPS on my iPad Air.

supportedOrientations(PORTRAIT_ANY)
displayMode(FULLSCREEN)

function setup() 
    offset={}
    m=mesh()
    m:addRect(WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
    m.texture=readImage("Cargo Bot:Startup Screen") 
    m.shader=shader(vShader,fShader)
end

function draw() 
    background(40, 40, 50) 
    for z=1,100 do
        offset[z]=math.random()/50
    end
    m.shader.offset=offset
    m:draw()
    fill(255,0,0)
    text(1/DeltaTime//1,WIDTH/2,HEIGHT-50)
end

vShader = [[
    uniform mat4 modelViewProjection;
    attribute vec4 position; 
    attribute vec2 texCoord;
    varying highp vec2 vTexCoord;
    void main() 
    {   vTexCoord = texCoord;
        gl_Position = modelViewProjection * position;
    }    ]]
   
fShader = [[
    uniform lowp sampler2D texture;
    varying highp vec2 vTexCoord;
    uniform lowp float offset[100];
    lowp float xx;
    void main() 
    {   lowp int d=0;
        {   for (highp float z=0.; z<=1.; z=z+.01)
            {   if (vTexCoord.y>z && vTexCoord.y<z+.01)
                    xx=offset[d]; 
                d=d+1;               
            }
            if (vTexCoord.x+xx>=1.0)
                xx=xx-1.0;
        }
        gl_FragColor=texture2D( texture,vec2(vTexCoord.x+xx,vTexCoord.y));
    }    ]]

Thanks dave, I’ll play around with it.

BTW, if it crashes you have to lower the framebuffnr before running (this is the nr of camera frames it saves), I was surprised I managed save up to 30-60 images myself.

This reminds me of a related question I had; is it possible to set the resolution of the actual camera image? So that I can lower the size of the frames that I store in the array?

@Kirl As far as I know, you can’t change anything with the camera. But you can copy a section of the screen to a smaller size.

function setup()
end

function draw()
    background(0)
    collectgarbage()
    img1=image(CAMERA)
    if img1~=nil then
        img=img1:copy(100,100,400,400)
        sprite(img,WIDTH/2,HEIGHT/2)
    end
end