Converting ofxFX Ripples example for Raspi 2

I’m trying to get ripples to work on the GPU on the raspi2, I have done it on the cpu using info from this site.

http://freespace.virgin.net/hugo.elias/graphics/x_water.htm

And I tried to understand how it’s been done in the ofxFX ripple example and since it wouldn’t compile for raspi I tried to port it and simplify it. My problem is that the GLSL fragment shader never seems to be applied to my texture, it never seems to average anything and subtract from the previous frame to find the next. I’m just left with the circles drawn under the mouse pointer.

rippleShaderOSX.zip (30.3 KB)

It’s in xcode btw, I’m developing it there first, just trying to replicate and understand the ofxFX ofxRipples example and then taking it across to the pi.

Can anyone help?

It was my vertex shader that was faulty, not passing thru correctly. I think it was because I’d left the GLES pass thru shader in place, replaced it with this and the code worked.

  #version 150
    
    uniform mat4 modelViewProjectionMatrix;
    in vec4 position;
    void main(){
        gl_Position = modelViewProjectionMatrix * position;
    }

Ok now I have working GPU ripples in OSX but I can’t port from opengl / glsl to gles. My shader’s seem to compile ok but they don’t seem to get used when I try and put textures through them and render that into an fbo for drawing later. The image that is eventually displayed is just the circle drawn under the mouse pointer, which is the same bug I had in OSX when the faulty vertex shader was causing problems at this stage.

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    
    bool didLoadShader = shader.load("Empty_GLES.vert", "ripple.frag", "");
    
    if (!didLoadShader)
    {
        ofLogError() << "Load Shader FAIL";
    }
    
    texture1.allocate(ofGetWidth(), ofGetHeight());
    texture2.allocate(ofGetWidth(), ofGetHeight());
    texture3.allocate(ofGetWidth(), ofGetHeight());
    texture1.begin();
    ofClear(0, 0, 0, 0);
    texture1.end();
    texture2.begin();
    ofClear(0,0,0,0);
    texture2.end();
    texture3.begin();
    ofClear(0,0,0,0);
    texture3.end();
    ofEnableAlphaBlending();
    damping = 0.995;
    even = true;

}

//--------------------------------------------------------------
void ofApp::update(){
    //add new waves from mouse pos
    
    if(even)
    {
        ofPushStyle();
        ofPushMatrix();
        texture1.begin();
        ofFill();
        ofSetColor(ofNoise( ofGetFrameNum() ) * 255 * 5, 255);
        ofEllipse(mouseX,mouseY, 10,10);
        texture1.end();
        ofPopMatrix();
        ofPopStyle();
        
        texture3.begin();
        texture3.bind();
        shader.begin();
        shader.setUniformTexture("backbuffer", texture2.getTextureReference(), 0);
        shader.setUniformTexture("tex0", texture1.getTextureReference(), 1);
        shader.setUniform1f("damping", (float)damping );
        //render frame
        ofSetColor(255,255);
        
        /*
        glBegin(GL_QUADS);
        glTexCoord2f(0, 0); glVertex3f(0, 0, 0);
        glTexCoord2f(ofGetWidth(), 0); glVertex3f(ofGetWidth(), 0, 0);
        glTexCoord2f(ofGetWidth(), ofGetHeight()); glVertex3f(ofGetWidth(), ofGetHeight(), 0);
        glTexCoord2f(0,ofGetHeight());  glVertex3f(0,ofGetHeight(), 0);
        glEnd();
        */

        
        GLfloat vtx1[] = {
            0, 0, 0,
            ofGetWidth(), 0, 0,
            ofGetWidth(), ofGetHeight(), 0,
            0, ofGetHeight(), 0
        };
        GLfloat tex1[] = {
            0,0,
            1,0,
            1,1,
            0,1
        };
 
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
        glVertexPointer(3, GL_FLOAT, 0, vtx1);
        glTexCoordPointer(2, GL_FLOAT, 0, tex1);
        glDrawArrays(GL_TRIANGLE_FAN,0,4);
 
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        

        //ofRect(0, 0, ofGetWidth(), ofGetHeight() );
        
        shader.end();
        texture3.unbind();
        texture3.end();
        
        
        
        
        texture2.begin();
        texture3.draw(0,0);
        texture2.end();
        //
        even = false;
    } else
    {
        ofPushStyle();
        ofPushMatrix();
        texture2.begin();
        ofFill();
        ofSetColor(ofNoise( ofGetFrameNum() ) * 255 * 5, 255);
        ofEllipse(mouseX,mouseY, 10,10);
        texture2.end();
        ofPopMatrix();
        ofPopStyle();
        
        texture3.begin();
        texture3.bind();
        shader.begin();
        shader.setUniformTexture("backbuffer", texture1.getTextureReference(), 0);
        shader.setUniformTexture("tex0", texture2.getTextureReference(), 1);
        shader.setUniform1f("damping", (float)damping );
        //render frame
        ofSetColor(255,255);
        /*
        glBegin(GL_QUADS);
        glTexCoord2f(0, 0); glVertex3f(0, 0, 0);
        glTexCoord2f(ofGetWidth(), 0); glVertex3f(ofGetWidth(), 0, 0);
        glTexCoord2f(ofGetWidth(), ofGetHeight()); glVertex3f(ofGetWidth(), ofGetHeight(), 0);
        glTexCoord2f(0,ofGetHeight());  glVertex3f(0,ofGetHeight(), 0);
        glEnd();
        */

        
        GLfloat vtx1[] = {
            0, 0, 0,
            ofGetWidth(), 0, 0,
            ofGetWidth(), ofGetHeight(), 0,
            0, ofGetHeight(), 0
        };
        GLfloat tex1[] = {
            0,0,
            1,0,
            1,1,
            0,1
        };
 
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
        glVertexPointer(3, GL_FLOAT, 0, vtx1);
        glTexCoordPointer(2, GL_FLOAT, 0, tex1);
        glDrawArrays(GL_TRIANGLE_FAN,0,4);
 
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
        

        //ofRect(0, 0, ofGetWidth(), ofGetHeight());
        shader.end();
        texture3.unbind();
        texture3.end();
        
        texture1.begin();
        texture3.draw(0,0);
        texture1.end();
        //
        even = true;
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
    ofBackground(0);
    ofSetColor(255,255);
    ofPushStyle();
    ofEnableAlphaBlending();
    if ( even ) {
        texture2.draw(0,0);
        
    } else
    {
        texture1.draw(0,0);
        
    }
    ofPopStyle();
}

relevant bit of header

 ofShader shader;
    ofFbo texture1;
    ofFbo texture2;
    ofFbo texture3;
    bool even;
    float damping;

vertex shader code

attribute vec4 position;
attribute vec4 color;
attribute vec4 normal;
attribute vec2 texcoord;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

varying vec4 colorVarying;
varying vec2 texCoordVarying;


void main()
{
	vec4 pos = projectionMatrix * modelViewMatrix * position;
	gl_Position = pos;
	
	colorVarying = color;
	texCoordVarying = texcoord;
}

fragment shader code

precision highp float;
uniform sampler2D backbuffer;   // previus buffer
uniform sampler2D tex0;         // actual buffer

uniform float damping;

vec2 offset[4];
varying vec2 texCoordVarying;

void main(){
    vec2 st = texCoordVarying;
    
    offset[0] = vec2(-1.0, 0.0);
    offset[1] = vec2(1.0, 0.0);
    offset[2] = vec2(0.0, 1.0);
    offset[3] = vec2(0.0, -1.0);
    
    //  Grab the information arround the active pixel
    //
    //      [3]
    //
    //  [0]  st  [1]
    //
    //      [2]
    
    vec3 sum = vec3(0.0, 0.0, 0.0);
    
    for (int i = 0; i < 4 ; i++){
        sum += texture2D(tex0, st + offset[i]).rgb;
    }
    
    //  make an average and substract the center value
    //
    sum = (sum / 2.0) - texture2D(backbuffer, st).rgb;
    sum *= damping;
    
    gl_FragColor = vec4(sum, 1.0);
}

I have set the renderer to GLES in the main.cpp as per all the examples I’ve seen of pi code. The bit where I think I’m going wrong is where I’ve changed the code from ofxFX that renders the texture, the original is

glBegin(GL_QUADS);
        glTexCoord2f(0, 0); glVertex3f(0, 0, 0);
        glTexCoord2f(ofGetWidth(), 0); glVertex3f(ofGetWidth(), 0, 0);
        glTexCoord2f(ofGetWidth(), ofGetHeight()); glVertex3f(ofGetWidth(), ofGetHeight(), 0);
        glTexCoord2f(0,ofGetHeight());  glVertex3f(0,ofGetHeight(), 0);
        glEnd();

I changed this to something a bit more GLES friendly but I don’t think it’s worked.

GLfloat vtx1[] = {
            0, 0, 0,
            ofGetWidth(), 0, 0,
            ofGetWidth(), ofGetHeight(), 0,
            0, ofGetHeight(), 0
        };
        GLfloat tex1[] = {
            0,0,
            1,0,
            1,1,
            0,1
        };
 
        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
 
        glVertexPointer(3, GL_FLOAT, 0, vtx1);
        glTexCoordPointer(2, GL_FLOAT, 0, tex1);
        glDrawArrays(GL_TRIANGLE_FAN,0,4);
 
        glDisableClientState(GL_VERTEX_ARRAY);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);

Ok, playing about a bit more I now know that since coords in GLES are normalised I should set my offsets to be +/- 1/resolution and pass that info into the shader. I know that my shader is compiling and doing something because if I set the gl_FracColor(1,0,0,1) then I see that it’s drawn my blobs red (but with grey ones on top - it’s weird). I tried unrolling the sum loop as I read somewhere that texture2D in loops could be funny on different platforms but still no dice.

Ok I now have working ripples based on the freespace algorithm / ofxFX ripples example. Replacing the GL_QUADS code that renders the texture into the fbo in GL/OSX with a simple ofRect call seemed to the trick. Why is this? When I try to render the texture this way in OSX it doesn’t work.

The main problem I have now is that my ripples only go down and to the right, I’m guessing this down to the specific hardware on the pi and the way my fragment shader samples and averages the previous wave texture.

precision highp float;
uniform sampler2D backbuffer;   // previus buffer
uniform sampler2D tex0;         // actual buffer

uniform float damping;
uniform vec2 resolution;

vec2 offset[4];
varying vec2 texCoordVarying;

void main(){

    float stepX = 1.0/float(resolution.x);
    float stepY = 1.0/float(resolution.y);
    
    offset[0] = vec2(-stepX, 0.0);
    offset[1] = vec2(stepX, 0.0);
    offset[2] = vec2(0.0, stepY);
    offset[3] = vec2(0.0, -stepY);

    vec2 st = vec2(gl_FragCoord.xy/resolution);

    //  Grab the information arround the active pixel
    //
    //      [3]
    //
    //  [0]  st  [1]
    //
    //      [2]



    
    vec3 sum = texture2D(tex0, st + offset[0]).rgb;
    sum += texture2D(tex0, st + offset[1]).rgb;
    sum += texture2D(tex0, st + offset[2]).rgb;
    sum += texture2D(tex0, st + offset[3]).rgb;

    //  make an average and substract the center value

    sum = ((sum / 2.0) - texture2D(backbuffer, st).rgb);
    sum *= damping;
    
    gl_FragColor = vec4(sum, 1.0);

}

hard coding different step values gives all kinds of weird and wonderful gl feedbacky type results but nothing really ripple like yet. Any ideas? I feel really close…

Ok got it, I think it was the 0.5 pixel shift that gl_FragCoord gives cos adding roughly half a pixel width seemed to sort it, thanks for all the help guys it was really useful.

1 Like