ofxFBO Texture Mapping – Cylinder

I am creating an app that needs to be projected onto a curved surface. To correct for the warp, I am attempting to map an FBO onto a cylinder to curve the digital image the same amount as the physical screen is curved. I can get a normal ofTexture to map onto the cylinder just fine, but cannot get an FBO texture to do the same.

I have made my FBO a power of two.
I have tried the mapping both with ofEnableArbTex() and ofDisableArbTex().
I can draw the FBO object normally using its draw() method.
I have tried ofxFBOTexture.bindAsTexture(), but it doesn’t seem to give me any texture mapping at all.
Using the .bind() and .unbind() seems to be working, as the color of the cylinder will change depending on the position of the image on the fbo object, but I can’t seem to get it to map the texture correctly.

I have looked over http://forum.openframeworks.cc/t/project-in-a-quot;curvedquot;-surface/2513/2 http://forum.openframeworks.cc/t/apply-video-to-3d-shape/2959/0 http://forum.openframeworks.cc/t/texturing-a-glusphere/1942/0 understanding only bits and pieces and using smatterings of the code where appropriate.

I think I am just mapping the texture wrong, but can’t figure out how to do it right.

Code is as follows:

  
void testApp::setup(){	  
  
	//Quad Controls  
	num_slices = 180;  
	cylinder_width = 300;  
	cylinder_height = 300;  
	  
	x_rotate = 90;  
	y_rotate = 180;  
	z_rotate = 30;  
	  
	cat_image.loadImage("cat.jpg");  
	//ofImage with a picture of a cat (yes a cat, you are on the internet...)  
	  
	fbo_tex.allocate(1024, 1024, true);  
	  
	//tried the code with and without this...  
	//fbo_tex.clear(1,1,1,1);  
  
	// enable depth test, so we only see the front  
    glEnable(GL_DEPTH_TEST);  
}  
   
  
//--------------------------------------------------------------  
void testApp::update(){  
}  
  
//--------------------------------------------------------------  
void testApp::draw(){  
	ofSetColor(0xffffff);  
	  
	fbo_tex.begin();  
	//draw the image on the fbo (I'm stretching it, but that shouldn't matter...)  
	//also, moving the image on the fbo changes the color of the cylinder, which makes me think it is a mapping issue  
		cat_image.draw(0,0, ofGetWidth(), ofGetHeight());	  
	fbo_tex.end();  
		  
	// create a new quadric to hold our cylinder  
    GLUquadric* quad = gluNewQuadric();  
    // tell GLU how to create the cylinder  
    gluQuadricNormals(quad, GLU_SMOOTH);  
	gluQuadricDrawStyle(quad, GLU_FILL);  
	gluQuadricTexture(quad, GL_TRUE);  
    gluQuadricOrientation(quad, GLU_OUTSIDE);  
	  
	//move the cylinder where it is seen  
	ofTranslate(ofGetWidth() / 2, ofGetHeight() / 2 - cylinder_height*.5, 0);  
	ofRotateX( x_rotate );  
	ofRotateY( y_rotate );  
	//I am rotating to make sure the texture isn't hiding on one side or another  
	ofRotateZ( z_rotate * ofGetElapsedTimef() *4 );  
  
	//Bind the fbo texture  
	fbo_tex.bind();  
	//fbo_tex.bindAsTexture(); //also tried this here instead  
		gluCylinder(quad, cylinder_width, cylinder_width, cylinder_height, num_slices, 1);	  
	fbo_tex.unbind();  
      
	// delete the cylinder from memory   
    gluDeleteQuadric(quad);  
	  
	//fbo_tex.draw(0, 0, ofGetWidth(), ofGetHeight());  
	//uncommenting here will draw the fbo with the ubiquitous cat picture...  
}  

Coming from a background of Flash, with bits of Processing and Max/MSP, I have never had to deal with any of this before, so I feel extra lost. Thanks in advance for helping yet one more OF newbie.

I have also looked over http://forum.openframeworks.cc/t/3d-video-texture/3547/0 and I feel the answer might be somewhere down this road, but it hasn’t become clear to me after 3+ readings.

I am now placing:

  
	fbo_tex.texData.textureTarget =  GL_TEXTURE_2D;  
  

just before I bind the fbo to the cylinder, which is mapping the fbo to the cylinder, which is great.

I now have another issue, I need the extra cylinder to be black, but when I draw a black background between the fbo tags, it doesn’t seem to work. It looks as though OpenGL is taking little bits from all the gl elements and adding them, semi-randomly, to the texture.

Do I need to somehow clear GL before I draw to the fbo? How would i do that? Called right before the fbo_tex.begin(), glFlush() doesn’t seem to do anything. Also, calling fbo_tex.clear doesn’t seem to do anything either.

Still working…any help would be greatly appreciated as GL is all completely new to me.

**EDIT**
I cannot draw shapes to the fbo object either. It shows up when i draw the fbo on its own, but not on the cylinder.

Also, I can only draw the picture in one configuration, I cannot move it around on the cylinder. Again, it draws in a different location when the fbo is drawn by itself.

I got it! The answer was indeed http://forum.openframeworks.cc/t/3d-video-texture/3547/0

Here is my code, I hope that someone else won’t have to spend 4 days on this again!

  
void testApp::setup(){	  
	  
	//Quad Controls  
	num_slices = 180;  
	cylinder_width = 300;  
	cylinder_height = 300;  
	  
	x_rotate = 90;  
	y_rotate = 180;  
	z_rotate = -90;  
	  
	ofDisableArbTex();  
	cat_image.loadImage("cat.jpg");  
	  
	fbo_tex.allocate(1024, 1024, true);  
	fbo_tex.clear(0,0,0,0);  
	  
	// enable depth test, so we only see the front  
    glEnable(GL_DEPTH_TEST);  
	  
}  
  
  
//--------------------------------------------------------------  
void testApp::update(){  
	ofBackground(0,0,0);  
}  
  
//--------------------------------------------------------------  
void testApp::draw(){  
	fbo_tex.begin();  
	  
		ofSetColor(255, 0, 0);  
		ofCircle(256, 256, 50);  
		ofSetColor(0xffffff);  
		cat_image.draw(0, 0, 512, 1024);  
	  
	fbo_tex.end();  
	  
	ofSetColor(0xffffff);  
	  
	// create a new quadric to hold our cylinder  
    GLUquadric* quad = gluNewQuadric();  
    // tell GLU how to create the cylinder  
    gluQuadricNormals(quad, GLU_SMOOTH);  
	gluQuadricDrawStyle(quad, GLU_FILL);  
	gluQuadricTexture(quad, GL_TRUE);  
    gluQuadricOrientation(quad, GLU_OUTSIDE);  
	  
	glPushMatrix();  
	ofTranslate(ofGetWidth()*.5, ofGetHeight()*.5 + cylinder_height*.5, 500); //+ 1/2 cylinder height  
	ofRotateX( -x_rotate );//added -x on rotation  
	/*this probably has to do with fbo flipping the y axis when you draw to it. */  
	ofRotateY( y_rotate );  
	ofRotateZ( z_rotate );  
	  
	fbo_tex.bind();  
	  
    glMatrixMode(GL_TEXTURE);  
    glPushMatrix();  
    glLoadIdentity();  
      
    ofTextureData texData = fbo_tex.getTextureData();  
    if(texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB) {  
        glScalef(fbo_tex.getWidth(), fbo_tex.getHeight(), 1.0f);  
    } else {  
        glScalef(fbo_tex.getWidth() / texData.tex_w, fbo_tex.getHeight() / texData.tex_h, 1.0f);  
    }  
	glMatrixMode(GL_MODELVIEW);       
	  
	// draw a cylinder with radius cylinder_width, cylinder_height pixels high, num_slices is smoothness  
	gluCylinder(quad, cylinder_width, cylinder_width, cylinder_height, num_slices, 1);	  
	  
	fbo_tex.unbind();  
	glMatrixMode(GL_TEXTURE);  
	glPopMatrix();  
	glMatrixMode(GL_MODELVIEW);  
	  
	// delete the cylinder from memory   
    gluDeleteQuadric(quad);  
	  
	glPopMatrix();  
}  

Thanks to Memo for knowing OpenGL way better than I do.

I ditched the Cylinder mapping because I couldn’t get the perspective view correct. I did a bezier shape instead, as it could conform a little better to the shape of the screen. There wasn’t an example of how to draw a bezier shape on the forums that I could find, so here is the code I have.

  
  
//This sets up my Bezier Surface  
//there is probably a better way to do this, but I am very new to OpenGL  
GLfloat ctrlpoints[4][4][3] = {  
{	{0, 0, 0},			{341, 0, 0},	{683, 0, 0},	{1024, 0, 0}	},  
{	{0, 284, 0},		{341, 284, 0},	{683, 284, 0},	{1024, 284, 0}	},  
{	{0, 484, 0},		{341, 484, 0},	{683, 484, 0},	{1024, 484, 0}	},  
{	{0, 768, 0},		{341, 768, 0},	{683, 768, 0},	{1024, 768, 0}	}  
};  
//This sets up my Texture Surface  
//I am mapping the texture upside down because it gets fliped when the FBO coords get fliped.   
//See line 149-150 of ofxFBOTexture.cpp  
GLfloat texpts [2][2][2] = {  
{ {1, 1}, {0, 1} },	{ {1, 0}, {0, 0} }  
};  
//--------------------------------------------------------------  
void testApp::setup(){	  
	//loading a test grid image to see where the screen coords are  
	cat_image.loadImage("myGrid.jpg");  
	  
	//set up Frame Buffer Object  
	fbo_tex.allocate(1024, 1024, true);  
	fbo_tex.clear(0,0,0,0);  
  
	// enable depth test, so we only see the front  
	glEnable(GL_DEPTH_TEST);  
	//set up bezier surface  
	glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlpoints[0][0][0]);  
	//set up texture map for bezier surface  
	glMap2f(GL_MAP2_TEXTURE_COORD_2, 0, 1, 2, 2, 0, 1, 4, 2, &texpts[0][0][0]);  
	glEnable(GL_MAP2_TEXTURE_COORD_2);  
	glEnable(GL_MAP2_VERTEX_3);  
	glEnable(GL_AUTO_NORMAL);  
	glMapGrid2f(20, 0, 1, 20, 0, 1);  
	glShadeModel(GL_FLAT);  
	  
	x_image = 0;  
	y_image = 0;  
	w_image = 1024;  
	h_image = 1024;  
}  
//--------------------------------------------------------------  
void testApp::update(){  
	ofBackground(0,0,0);  
}  
//--------------------------------------------------------------  
void testApp::draw(){  
	fbo_tex.begin();  
		cat_image.draw(x_image, y_image, w_image, h_image);  
	fbo_tex.end();  
	  
	ofSetColor(0xffffff);  
		  
	fbo_tex.bind();  
  
	glMatrixMode(GL_TEXTURE);  
	glPushMatrix();//to scale the texture  
	glLoadIdentity();  
	  
	ofTextureData texData = fbo_tex.getTextureData();  
	if(texData.textureTarget == GL_TEXTURE_RECTANGLE_ARB) {  
		glScalef(fbo_tex.getWidth(), fbo_tex.getHeight(), 1.0f);  
	} else {  
		glScalef(fbo_tex.getWidth() / texData.tex_w, fbo_tex.getHeight() / texData.tex_h, 1.0f);  
	}  
	glMatrixMode(GL_MODELVIEW);  
	//draw the bezier shape  
	glEvalMesh2(GL_FILL, 0, 20, 0, 20);  
		  
	fbo_tex.unbind();  
	glMatrixMode(GL_TEXTURE);  
	glPopMatrix();// texture scale pop matrix  
}  
  

I didn’t include it, but if you change a bezier value, you have to re-map the bezier shape. I couldn’t find a real good way around this, but I’m sure there is one. Example:

  
  
//(...on key press, etc....)  
//move the top left corner 20 pixels down (y += 20)  
ctrlpoints[0][0][1] += 20;  
//remap the bezier shape  
glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &ctrlpoints[0][0][0]);  
  

If someone has a better/faster way of doing this, that would be great.

I hope someone else can use this also.

@ blainer_s: Just wanted to say thank you so much for posting this method… It’s really quite awesome! I’ve made it into an addon - I hope you don’t mind.

ofxBezierWarp

I’ve tried to use this addon with of 0.8.4 and I can’t see the grid.
Does anyone here have used it with success in of 0.8.+??

Thanks

I had the same problem, solved on OSX Yosemite (OF 084, XCode 502) changing these lines in ofxBezierWarp.h

void allocate(int w, int h, int pixelFormat = GL_BGRA);
    void allocate(int w, int h, int numXPoints, int numYPoints, float pixelsPerGridDivision, int pixelFormat = GL_BGRA);

to this

void allocate(int w, int h, int pixelFormat = GL_RGBA);
    void allocate(int w, int h, int numXPoints, int numYPoints, float pixelsPerGridDivision, int pixelFormat = GL_RGBA);

should work for you too

ciao
Davide