Question

I am trying to write a basic volume renderer which uses opengl, and cg for writing shaders. I'm putting my transfer function in a one dimensional texture, and using that in a dependent texture lookup in the fragment shader. My problem is that I'm getting an openGL error when I try to enable the parameter corresponding to that 1D texture.

My code is pretty messy; right now it's basically a mashup of code taken from "Real-Time Volume Rendering" and "The CG User's Manual".

c file:

#include <stdio.h>
#include <stdlib.h>
#include <GL/glut.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>

static CGcontext   myCgContext;
static CGprofile   myCgVertexProfile,
                   myCgFragmentProfile;
static CGprogram   myCgVertexProgram,
                   myCgFragmentProgram;

static const char *myProgramName = "first_volumetric_renderer",
                  *myVertexProgramFileName = "fvr_vertex.cg",
                  *myVertexProgramName = "fvr_vertex",
                  *myFragmentProgramFileName = "fvr_fragment.cg",
                  *myFragmentProgramName = "fvr_fragment";

static CGparameter first_texture, second_texture, transfer_function;

#define XDIM 256
#define YDIM 256
#define ZDIM 256
#define TRANSFER_RESOLUTION 256
static GLubyte raw_data[XDIM][YDIM][ZDIM];
static GLubyte transfer[TRANSFER_RESOLUTION][4];
static GLuint transfer_name;
static GLuint x_textures[XDIM], y_textures[YDIM], z_textures[ZDIM];


static void checkForCgError(const char *situation);

/* print any errors if we get them */
void check_gl_error(const char * where){  
  GLenum error = glGetError();
  if(error != GL_NO_ERROR){
    printf("openGL Error : %s : %s\n", where, gluErrorString(error));
    exit(1);
  }
}

long int file_length(FILE *f){
  long int pos = ftell(f);
  fseek(f, 0, SEEK_END);
  long int result = ftell(f);
  fseek(f, pos, SEEK_SET);
  return result;
}

void get_volume_data(const char *filename){
  FILE *in = fopen(filename, "r");
  if(in == NULL) {
    printf("opening '%s' to get volume data failed, exiting...\n", filename);
    exit(1);
  }
  long int length = file_length(in);
  if(length != XDIM*YDIM*ZDIM){
    printf("the file does not contain a volume of the correct dimensions\n");
    exit(1);
  }
  size_t res = fread((char *)raw_data, 1, length, in);
  if(res < length) printf("error reading in file\n");

  fclose(in);
}


void create_textures(){
  glEnable(GL_TEXTURE_2D);

  // reserve texture identifiers
  glGenTextures(XDIM, x_textures);
  glGenTextures(YDIM, y_textures);
  glGenTextures(ZDIM, z_textures);

  // set texture properties
  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

  // generate slices in X
  {
    int x,y,z;
    GLubyte x_slice[ZDIM][YDIM];
    for(x=0;x < XDIM; x++){
      for(y=0;y < YDIM; y++){
    for(z=0;z < ZDIM; z++){
      x_slice[z][y] = raw_data[x][y][z];
    }
      }
      GLuint texname = x_textures[x];
      glBindTexture(GL_TEXTURE_2D, texname);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, ZDIM, YDIM, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, x_slice);
    }
  }

  // generate slices in Y
  {
    int x,y,z;
    GLubyte y_slice[XDIM][ZDIM];
    for(y=0;y < YDIM; y++){
      for(x=0;x < XDIM; x++){
    for(z=0;z < ZDIM; z++){
      y_slice[x][z] = raw_data[x][y][z];
    }
      }
      GLuint texname = y_textures[y];
      glBindTexture(GL_TEXTURE_2D, texname);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, XDIM, ZDIM, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, y_slice);
    }
  }

  // generate slices in Z
  {
    int x,y,z;
    GLubyte z_slice[XDIM][YDIM];
    for(z=0;z < ZDIM; z++){
      for(y=0;y < YDIM; y++){
    for(x=0;x < XDIM; x++){
      z_slice[x][y] = raw_data[x][y][z];
    }
      }
      GLuint texname = z_textures[z];
      glBindTexture(GL_TEXTURE_2D, texname);
      glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, XDIM, YDIM, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, z_slice);
    }
  }
}

void DrawSliceStack_NegativeZ(int numSlices){
  double dZPos = -1.0;
  double dZStep = 2.0/((double)numSlices);

  int slice;
  for(slice = 0;slice < numSlices;slice++){
    double dZPosTex = (ZDIM * (dZPos + 1.0)/2.0);
    int nTexIdx = (int)dZPosTex;
    double dAlpha = dZPosTex - (double)nTexIdx;

    check_gl_error("in slice-drawing function, before cg-stuff");

    cgGLSetTextureParameter(first_texture, z_textures[nTexIdx]);
    checkForCgError("setting first texture");
    check_gl_error("1");
    cgGLEnableTextureParameter(first_texture);
    checkForCgError("enabling first texture");
    check_gl_error("2");
    cgGLSetTextureParameter(second_texture, z_textures[nTexIdx + 1]);
    checkForCgError("setting second texture");
    check_gl_error("3");
    cgGLEnableTextureParameter(second_texture);
    checkForCgError("enabling second texture");
    check_gl_error("4");
    cgGLSetTextureParameter(transfer_function, transfer_name);
    checkForCgError("setting transfer function");
    check_gl_error("5");
    cgGLEnableTextureParameter(transfer_function);
    checkForCgError("enabling transfer function");
    check_gl_error("before updating parameters");
    cgUpdateProgramParameters(myCgFragmentProgram);
    checkForCgError("updating parameters");

    check_gl_error("before drawing a slice");
    glBegin(GL_QUADS);
    glTexCoord3d(0.0, 0.0, dAlpha);
    glVertex3d(-1.0, -1.0, dZPos);
    glTexCoord3d(0.0, 1.0, dAlpha);
    glVertex3d(-1.0, 1.0, dZPos);
    glTexCoord3d(1.0, 1.0, dAlpha);
    glVertex3d(1.0, 1.0, dZPos);
    glTexCoord3d(1.0, 0.0, dAlpha);
    glVertex3d(1.0, -1.0, dZPos);
    glEnd();
    check_gl_error("after drawing a slice");

    dZPos += dZStep;

    cgGLDisableTextureParameter(first_texture);
    checkForCgError("disabling first texture");
    cgGLDisableTextureParameter(second_texture);
    checkForCgError("disabling second texture");
    cgGLDisableTextureParameter(transfer_function);
    checkForCgError("disabling transfer function");

  }
}

void create_transfer_texture(){
  glEnable(GL_TEXTURE_1D);

  // create the raw data
  int i;
  for(i = 0; i < TRANSFER_RESOLUTION; i++){
    if(i < 50) {
      transfer[i][0] = (GLubyte)0;
      transfer[i][1] = (GLubyte)0;
      transfer[i][2] = (GLubyte)0;
      transfer[i][3] = (GLubyte)0;
    }
    else {
      transfer[i][0] = (GLubyte)255;
      transfer[i][1] = (GLubyte)255;
      transfer[i][2] = (GLubyte)255;
      transfer[i][3] = (GLubyte)i;
    }
  }

  glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
  glGenTextures(1, &transfer_name);
  glBindTexture(GL_TEXTURE_3D, transfer_name);
  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP);
  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, TRANSFER_RESOLUTION, 0, GL_RGBA, GL_UNSIGNED_BYTE, transfer);
  check_gl_error("creating transfer texture");
}

static void checkForCgError(const char *situation)
{
  CGerror error;
  const char *string = cgGetLastErrorString(&error);

  if (error != CG_NO_ERROR) {
    printf("%s: %s: %s\n",
      myProgramName, situation, string);
    if (error == CG_COMPILER_ERROR) {
      printf("%s\n", cgGetLastListing(myCgContext));
    }
    exit(1);
  }
}

void init_CG(){
  // copy-pasted straight from one of the CG examples
  myCgContext = cgCreateContext();
  checkForCgError("creating context");
  cgGLSetDebugMode(CG_FALSE);
  cgSetParameterSettingMode(myCgContext, CG_DEFERRED_PARAMETER_SETTING);


  myCgFragmentProfile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
  cgGLSetOptimalOptions(myCgFragmentProfile);
  checkForCgError("selecting fragment profile");

  myCgFragmentProgram =
    cgCreateProgramFromFile(
      myCgContext,                /* Cg runtime context */
      CG_SOURCE,                  /* Program in human-readable form */
      myFragmentProgramFileName,  /* Name of file containing program */
      myCgFragmentProfile,        /* Profile: OpenGL ARB vertex program */
      myFragmentProgramName,      /* Entry function name */
      NULL);                      /* No extra compiler options */
  checkForCgError("creating fragment program from file");
  cgGLLoadProgram(myCgFragmentProgram);
  checkForCgError("loading fragment program");

  first_texture = cgGetNamedParameter(myCgFragmentProgram, "text0");
  checkForCgError("could not get 'texture0'");
  second_texture = cgGetNamedParameter(myCgFragmentProgram, "text1");
  checkForCgError("could not get 'texture1'");
  transfer_function = cgGetNamedParameter(myCgFragmentProgram, "transfer_function");
  checkForCgError("could not get 'transfer_function'");

  check_gl_error("initializing CG");
}

void reshape(int w, int h)
{
  if (h == 0) h = 1;
  glViewport(0, 0,w,h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-2, 2, -2, 2, -2, 2); // use orthographic projection
  glMatrixMode(GL_MODELVIEW);
}

void display(){
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


  /* cgGLBindProgram(myCgVertexProgram);
  checkForCgError("binding vertex program");

  cgGLEnableProfile(myCgVertexProfile);
  checkForCgError("enabling vertex profile");*/

  cgGLBindProgram(myCgFragmentProgram);
  checkForCgError("binding fragment program");

  cgGLEnableProfile(myCgFragmentProfile);
  checkForCgError("enabling fragment profile");

  check_gl_error("before entering slice-drawing function");
  DrawSliceStack_NegativeZ(ZDIM * 2);

  /*cgGLDisableProfile(myCgVertexProfile);
    checkForCgError("disabling vertex profile");*/

  cgGLDisableProfile(myCgFragmentProfile);
  checkForCgError("disabling fragment profile");

  glutSwapBuffers();

  check_gl_error("Finishing 'display()'");
}

void keyboard(unsigned char c, int x, int y){
}

void init_glut(int argc, char** argv){
  glutInitWindowSize(400, 400);
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
  glutInit(&argc, argv);

  glutCreateWindow(myProgramName);
  glutDisplayFunc(display);
  glutKeyboardFunc(keyboard);
  glutReshapeFunc(reshape);

  glClearColor(1.0, 0.0, 0.0, 0.0);  /* Black background */
}

int main(int argc, char **argv){
  init_glut(argc, argv);
  init_CG();
  get_volume_data("aneurism.raw");
  create_textures();
  create_transfer_texture();

  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glutMainLoop();
  return 0;
}

fragment shader (fvr_fragment.cg):

 float4 fvr_fragment(half3 texUV : TEXCOORD0,
                uniform sampler2D text0,
                uniform sampler2D text1,
                uniform sampler1D transfer_function) : COLOR
    {
      half tex0 = tex2D(text0, texUV.xy);
      half tex1 = tex2D(text1, texUV.xy);

      half interp = lerp(tex0, tex1, texUV.z);
      float4 result = tex1D(transfer_function, interp); 

      return result; 
    }

the volume data

When run, the program outputs:

openGL Error : before updating parameters : invalid operation

the line which causes the error, which I found thanks to my ad-hoc print debugging, is:

cgGLEnableTextureParameter(transfer_function); 

Any ideas?

No correct solution

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top