Binding FBO as texture for shader

It might just be cause I’m tired and have been hammering away at this too long, but I feel like I’m missing something obvious. I’m drawing camera input onto a frame buffer object and trying to set the FBO data as a sampler2D in my shader. However, when I bind the FBO as a texture, all I’m getting is black. I do this in Cinder all the time, but need to do it for an ofx project. The basic idea is to run a real-time adjustable number of shader passes on video input.

I’ve verified that my video is drawing to the FBO (if I draw the FBO, I see the image). My shader works because I’m using it in Cinder on the same machine. It’s compiling fine here, and I also tested it by just having it output color. I’ve also verified that the (or at least *a*) texture is binding, because if it doesn’t, I get light grey, not black.

My shader code is an update of ofxShader. I know for sure this works, as well, because I’m using on another project to apply a blur filter to drawing data. So I’ve narrowed it down to either I’m sending in the wrong texture to the shader, or my texture is going black somewhere in the routine, or my coordinate systems are not aligned between the shader and the texture.

Here’s the code:

Header:

  
#ifndef _SHADER_FBO_APP  
#define _SHADER_FBO_APP  
  
#define CAPTURE_DEVICE    1  
#define CAPTURE_HEIGHT  480  
#define CAPTURE_WIDTH   640  
  
#include "ofMain.h"  
#include "ofxShader.h"  
#include "ofxFBOTexture.h"  
  
class shaderFboApp : public ofBaseApp  
{  
  
	public:  
  
        // OFX callbacks  
        void draw();  
        void keyReleased(int key);  
		void setup();  
  
    private:  
  
        // Amount of lens distortion  
        GLfloat fDistortion;  
  
        // Number of shader passes  
        int iPasses;  
  
        // Camera input  
        ofVideoGrabber vidGrabber;  
  
        // The shader  
        ofxShader shader;  
  
        // The frame buffer objects  
        ofxFBOTexture fboPing;  
        ofxFBOTexture fboPong;  
  
        // Use these to point to the FBOs when ping/ponging  
        ofxFBOTexture * FBOs[2];  
        int iFBO;  
  
};  
  
#endif  
  

CPP

  
#include "shaderFboApp.h"  
  
//--------------------------------------------------------------  
void shaderFboApp::setup()  
{  
  
    // Set default shader pass count and distortion level  
    fDistortion = 0.5f;  
    iPasses = 0;  
  
    // Set up video input  
    vidGrabber.setDeviceID(CAPTURE_DEVICE);  
	vidGrabber.setDesiredFrameRate(30);  
	vidGrabber.setVerbose(false);  
	vidGrabber.initGrabber(CAPTURE_WIDTH, CAPTURE_HEIGHT);  
  
    // Load the shader  
    shader.loadShader("shaders/lensDistortion");  
  
	// Allocate memory for frame buffer objects  
	fboPing.allocate(CAPTURE_WIDTH, CAPTURE_HEIGHT, GL_RGB);  
	fboPong.allocate(CAPTURE_WIDTH, CAPTURE_HEIGHT, GL_RGB);  
  
	// Set FBO index and references  
	iFBO = 0;  
	FBOs[0] = &fboPing;  
	FBOs[1] = &fboPong;  
  
	// Use white to draw camera input  
	ofSetColor(255, 255, 255, 255);  
  
}  
  
//--------------------------------------------------------------  
void shaderFboApp::draw()  
{  
  
    // Update video frame  
    vidGrabber.grabFrame();  
  
    // Bind the frame buffer object to draw on it  
    FBOs[iFBO]->begin();  
  
    // Draw onto the FBO  
    vidGrabber.draw(0, 0, CAPTURE_WIDTH, CAPTURE_HEIGHT);  
  
    // Unbind the frame buffer object  
    FBOs[iFBO]->end();  
  
    // Iterate through passes  
    for (int i = 0; i < iPasses; i++)  
    {  
  
        // Bind and enable the FBO we just rendered as a texture  
        glEnable(GL_TEXTURE_2D);  
        glBindTexture(GL_TEXTURE_2D, FBOs[iFBO]->texData.textureID);  
        glActiveTexture(GL_TEXTURE0);  
  
        // Bind the shader  
        shader.setShaderActive(true);  
  
        // Set distortion and texture in the shader  
        glUniform1f(glGetUniformLocation(shader.shader, "distortion"), fDistortion);  
        glUniform1i(glGetUniformLocation(shader.shader, "texture"), GL_TEXTURE0);  
  
        // Swap FBO index  
        iFBO = (iFBO + 1) % 2;  
  
        // Bind the other FBO so we can draw onto it  
        FBOs[iFBO]->begin();  
  
        // Draw shader output  
        glMatrixMode(GL_MODELVIEW);  
        glPushMatrix();  
        glLoadIdentity();  
        glMatrixMode(GL_PROJECTION);  
        glPushMatrix();  
        glLoadIdentity();  
        glBegin(GL_QUADS);  
        glVertex3i(-1, -1, -1);  
        glVertex3i(1, -1, -1);  
        glVertex3i(1, 1, -1);  
        glVertex3i(-1, 1, -1);  
        glEnd();  
        glPopMatrix();  
        glMatrixMode(GL_MODELVIEW);  
        glPopMatrix();  
  
        // Unbind the shader  
        shader.setShaderActive(false);  
  
        // Unbind the FBO  
        FBOs[iFBO]->end();  
  
    }  
  
	// Draw the result  
	FBOs[iFBO]->draw(0.0f, 0.0f, (float)CAPTURE_WIDTH, (float)CAPTURE_HEIGHT);  
  
}  
  
//--------------------------------------------------------------  
void shaderFboApp::keyReleased(int key)  
{  
  
    // Use number keys to define shader passes  
    switch (key)  
    {  
        case '0': iPasses = 0;  
        break;  
        case '1': iPasses = 1;  
        break;  
        case '2': iPasses = 2;  
        break;  
        case '3': iPasses = 3;  
        break;  
        case '4': iPasses = 4;  
        break;  
        case '5': iPasses = 5;  
        break;  
        case '6': iPasses = 6;  
        break;  
        case '7': iPasses = 7;  
        break;  
        case '8': iPasses = 8;  
        break;  
        case '9': iPasses = 9;  
        break;  
    }  
  
    // Use -/+ keys to control lens distortion  
    if (key == '-' || '_') fDistortion -= 0.01f;  
    if (key == '=' || '+') fDistortion += 0.01f;  
    fDistortion = CLAMP(fDistortion, 0.0f, 1.0f);  
  
}  
  

Shader

  
#version 120  
  
// Distortion amount  
uniform float distortion;  
  
// Texture  
uniform sampler2D texture;  
  
void main(void)  
{  
  
	// Get coordinates and pixel sizes  
	vec2 texCoord = gl_TexCoord[0].st;  
  
	// Get dot product of center  
	vec2 center = texCoord - vec2(0.5, 0.5);  
    float cD = dot(center, center);  
  
    // Flip distortion  
    float d = -distortion;  
  
	// Set color  
	gl_FragColor = texture2D(texture, texCoord + center * (cD + d * cD * cD) * d);  
  
}  
  

Just a quick stab in the dark here but should this:

  
// Texture  
uniform sampler2D texture;  

be

  
// Texture  
uniform sampler2DRect texture;  

Maybe? I’m in front of my non-OF box so I can’t check at the moment, but I’ll give it a shot when I get home.

I’m not using ARB, so sampler2D should be correct. The shader is definitely compiling fine without having to use ARB.

I’m wondering if ofx just would rather I use ARB? Come to think of it, I was doing everything in ARB on my last project that relied heavily on shaders. Some out loud reasoning…

When the texture seems to be set correctly, each pixel is sampling black based on normalized coordinates. This is telling me that the shader is, in fact, pulling color data from a texture… if memory is not allocated, nothing is sampled – background stays the same as the clear color. So either my normalized coordinates are essentially “missing” the texture, or the screen is not getting drawn to the FBO in the ping pong routine. Being that I’m easily drawing video to the FBO and rendering it, I’m guessing it’s the prior.

I’ll try using sample2DRect, switching to ARB, and using screen coordinates (as opposed to normalized) and see if the sampler in the shader can line up with the texture.

Just not a fan of ARB since it doesn’t truly take advantage of the GPU like the standard shader does (I mean, it does, just not as fully).

Excuse my overly brief answer earlier, but yes, I’m pretty sure (pretty) that ofx uses ARB by default though you can turn it off with ofDisableArbTex(). Is your vertex shader doing anything or is it just a pass through?

=== edit

Excuse me, I hadn’t read your code earlier, I think the normalized coords are your problem, there’s some odd stuff with the OF normalized coords that I haven’t quite dug into. If you get an answer I’d be psyched to hear it, but I’ll check out the code later and see anything pops out at me. Check out ofSetupScreen() in ofGraphics.cpp.

Hey, by default OF will use rectangle textures, not texture_2d. So unless you explicitly state ofDisableArbTex() like Josh mentions BEFORE creating your texture, it will be a rect and all your shader coordinates will be off. So in your case before you call initGrabber (that’s what creates the texture) you should call ofDisableArbtex() to use texture_2d.

Alternatively, if you want to use rect textures, but still want to use normalized coordinates, you can use ofEnableNormalizedCoords() (or something like that, can’t remember now, look in graphics.h). This sets up the texture coodinate matrix so that texture coordinates are always 0…1, no matter what the texture type (rect or 2d).

Finally, I think when binding the uniform for the texture, you need to give 0 not GL_TEXTURE0. I’m not 100% on that, but give that a try too.

btw, ofxShader has a setUniform() method, is there a reason you’re not using that? also ofxFboTexture has a bindTexture method (or something like that), to bind its texture as a texture :stuck_out_tongue:

ofDisableArbTex() did the trick. Woohoo! Thanks, guys! I really don’t do the ARB thing at all so I never would have thought to look in that direction.

Yeah, my “ofxShader” is actually quite a bit different from *the* ofxShader. It’s more focused on just compiling and giving me info about the card, as I prefer to do OpenGL stuff directly (except FBO, obviously, since yours does it so well already).

Thanks again!

Hi Everyone.

Thanks for sharing info on this kind of subject, I try to manage FBO and shader in OF and actually don’t succeed in it.

With this particular example, ofDisableArbTex() in setup before init video grabber didn’t do the trick, I still have a grey screen when iterate through passes. Could it be link to texture location in GL context ?

Another point I do not understand is when Iterate through passes:

  
// Iterate through passes  
    for (int i = 0; i < iPasses; i++)  
    {  
  
        // Bind and enable the FBO we just rendered as a texture  
        glEnable(GL_TEXTURE_2D);  
        glBindTexture(GL_TEXTURE_2D, FBOs[iFBO]->texData.textureID);  
        glActiveTexture(GL_TEXTURE0);  
  

why calling glActiveTexture(GL_TEXTURE0) after binding the FBO texture ? is there any implicit call to activate another texture ?

I think the example posted here is using a different ofxShader, which may be causing some problems. The calls to glActiveTexture() should be just to set which texture object the FBOs will be drawn into, though, if I’m not mistaken that could be done outside of the passes loop. Is your code exactly the same as what’s here?

Yes my code is exactly the same as here, maybe as you suggest, the problem is laying in my standard ofxFBOtexture class.

Another texture activation could be effectively done outside of the loop, but it means in another thread and i have read that openGL is not thread safe at all.

Will dig this two points, thanks!

1 Like

Old topic, but the black also happend to me, it sorted now after i add
ofDisableArbTex();
this tip may help some of you.