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);
}