arnaud13
arnaud13

Reputation: 109

Draw to renderbuffer with FBO

My question is pretty close to this one: FBO and Render to renderbuffer. I am currently trying to render a simple triangle on a renderbuffer like in this tutorial. My program is a plugin to Igor Pro that performs off screen rendering with OpenGL.

Unfortunately I am getting only zero when I am trying to read the pixel values. I already know that the initBuffers function works and that the problem lies in the display function.

1) What is the problem with my code?

2) I'd like to render a list of vertex data without using the immediate mode. I have previously done an implementation with VBOs. Can I reuse that code?

My code is the following:

/*  
RenderTriangle.cpp
*/

#include "VC10\triangulation.h"
#include "XOPStandardHeaders.h"         // Include ANSI headers, Mac headers, IgorXOP.h, XOP.h and XOPSupport.h
#include <vector>


// Prototypes
HOST_IMPORT int main(IORecHandle ioRecHandle);

// Global Variables
const int windowSize = 512;

int size,el_size;
float* waveX=NULL;
float* waveY=NULL;
float* waveZ=NULL;
GLuint framebuffer, renderbuffer;

// Custom error codes
#define OLD_IGOR 1 + FIRST_XOP_ERR
#define NON_EXISTENT_WAVE 2 + FIRST_XOP_ERR
#define REQUIRES_SP_OR_DP_WAVE 3 + FIRST_XOP_ERR

#pragma pack(2)     // All structures passed to Igor are two-byte aligned.
typedef struct WaveParams {
  waveHndl waveZ;
  waveHndl waveY;
  waveHndl waveX;   
  double result;
} WaveParams, *WaveParamsPtr;
#pragma pack()      // Restore default structure alignment

// Prototypes of the OpenGL callbacks
void initBuffers(void);
void display(void);
void readPixels(void);

void initBuffers(void){

  GLenum status;

  //Generate the fbo where we will bind a render buffer object
  glGenFramebuffers(1,&framebuffer);
  glBindFramebuffer(GL_DRAW_FRAMEBUFFER,framebuffer);

  //Generate render buffer object
  glGenRenderbuffers(1,&renderbuffer);
  glBindRenderbuffer(GL_RENDERBUFFER,renderbuffer);

  glRenderbufferStorage(GL_RENDERBUFFER,GL_RGBA8, windowSize, windowSize);

  glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);

  status = glCheckFramebufferStatus(GL_FRAMEBUFFER);

  if (status != GL_FRAMEBUFFER_COMPLETE)
    XOPNotice("Error Framebuffer Complete");
  else
    XOPNotice("Framebuffer Complete");
}

void display(void){//display a simple triangle

  glViewport(0,0,windowSize,windowSize);
  glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
  glClearColor(1.0f, 0.0f, 0.0f,0.5f);
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();

  glBegin(GL_TRIANGLES);// Drawing Using Triangles
    glColor3f(1.0f, 0.0f, 0.0f);glVertex3f( 0.0f, 1.0f, 0.0f);// Top
    glColor3f(1.0f, 0.0f, 0.0f);glVertex3f(-1.0f,-1.0f, 0.0f);// Bottom Left
    glColor3f(1.0f, 0.0f, 0.0f);glVertex3f( 1.0f,-1.0f, 0.0f);// Bottom Right
  glEnd();// Finished Drawing The Triangle

  glFlush();

}

// Idle callback function for OpenGL API
void readPixels(void){

  //glBindFramebuffer(GL_FRAMEBUFFER, 0);

  float* pixels = (float*)malloc(windowSize*windowSize*sizeof(float));
  glReadBuffer(GL_COLOR_ATTACHMENT0);
  glReadPixels(0, 0, windowSize, windowSize, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);

  waveHndl waveH;
  char waveName[MAX_OBJ_NAME+1];
  DataFolderHandle dfH;
  CountInt dimensionSizes[MAX_DIMENSIONS+1];
  float* wp;
  IndexInt p;
  double offset, delta;
  int err;
  strcpy(waveName, "wave1");
  dfH = NULL; // Put wave in the current data folder
  MemClear(dimensionSizes, sizeof(dimensionSizes));

  dimensionSizes[ROWS] = windowSize; // Make 1D wave with 100 points
  dimensionSizes[COLUMNS] = windowSize;

  if (MDMakeWave(&waveH, waveName, dfH, dimensionSizes, NT_FP32, 1))
    XOPNotice("Can't create wave when reading pixel values");

  wp = (float*)WaveData(waveH); // Get a pointer to the wave data

  for(p = 0; p < windowSize*windowSize; p++)
  *wp++ = pixels[p]; // Store point number in wave



}
void deleteBuffers(){
  glDeleteFramebuffers(1,&framebuffer);
  glDeleteRenderbuffers(1,&renderbuffer);
}
// Main function for the XOP
extern "C" int
  RenderTriangle(WaveParamsPtr p)
{
  //Pointers to the input waves
  waveX=(float*)WaveData(p->waveX);
  waveY=(float*)WaveData(p->waveY);
  waveZ=(float*)WaveData(p->waveZ);

  //Creates the window
  char *myargv [1];
  int myargc=1, windowHandle;
  myargv [0]=strdup ("RenderTriangle");

  glutInit(&myargc,myargv);
  glutInitDisplayMode(GLUT_SINGLE | GLUT_DEPTH | GLUT_RGB);
  glutInitWindowSize(windowSize,windowSize);
  windowHandle = glutCreateWindow("OpenGL");

  //Init glew library 
  glewInit(); 

  //Creates the fbo and render buffer object
  initBuffers(); 

  display();

  readPixels();

  //Deletes the fbo and render buffer object
  deleteBuffers();

  p->result = 1;

  return 0;
}

static XOPIORecResult
  RegisterFunction()
{
  int funcIndex;

  funcIndex = (int)GetXOPItem(0);           // Which function invoked ?
  switch (funcIndex) {
  case 0:                                   // y = RenderTriangle(w,x) (curve fitting function).
    return (XOPIORecResult)RenderTriangle;  // This function is called using the direct method.
    break;
  }
  return 0;
}

/*  XOPEntry()

This is the entry point from the host application to the XOP for all
messages after the INIT message.
*/
extern "C" void
  XOPEntry(void)
{   
  XOPIORecResult result = 0;

  switch (GetXOPMessage()) {
  case FUNCADDRS:
    result = RegisterFunction();    // This tells Igor the address of our function.
    break;
  }
  SetXOPResult(result);
}

/*  main(ioRecHandle)

This is the initial entry point at which the host application calls XOP.
The message sent by the host must be INIT.
main() does any necessary initialization and then sets the XOPEntry field of the
ioRecHandle to the address to be called for future messages.
*/
HOST_IMPORT int
  main(IORecHandle ioRecHandle)
{   
  XOPInit(ioRecHandle);                         // Do standard XOP initialization.
  SetXOPEntry(XOPEntry);                            // Set entry point for future calls.

  if (igorVersion < 600) {
    SetXOPResult(OLD_IGOR);
    return EXIT_FAILURE;
  }

  SetXOPResult(0);
  return EXIT_SUCCESS;
}

Upvotes: 1

Views: 1480

Answers (3)

kakrafoon
kakrafoon

Reputation: 506

Likewise, here is an example using renderbuffers.

How to render offscreen on OpenGL?

1) Create context 2) Create FBO (storage for color, depth, stencil) and bind 3) Draw 4) Readback (according to 2))

I am not sure if it is correct to say that the whole operation would be faster and easier to implement with textures. It all depends on what we want to do (glReadPixels is synchronous).

Upvotes: 0

datenwolf
datenwolf

Reputation: 162164

You need a working OpenGL context for the rest to function and you don't create one! You're using GLUT as a framework, but GLUT creates a render context only when glutCreateWindow is called. Prior to that you got no active RC at all, and the rest won't work.

Maybe Igor itself creates a OpenGL context at some point and you're trashing into that one.

Instead of using GLUT you should use system level APIs to

  1. store the current context state (if a context is active in the current thread, and if so, the context handle)

  2. if not already done: create an off-screen render context for your plugin (use a PBuffer or a invisible window together with FBOs)

  3. do your rendering with your own context

  4. restore context state saved in 1.

Upvotes: 1

fen
fen

Reputation: 10105

I suggest another tutorial: http://www.songho.ca/opengl/gl_fbo.html

but regarding your code:

it would be simplier to render directly to texture. That way there will be no reason to use glReadPixels. The whole operation will be faster and easier to implement.

create empty texture and add it to FBO via glFramebufferTexture2D and after rendering you have the results in the texture.

Upvotes: 1

Related Questions