Okay, so here’s the problem. I have a C++ set of classes that load, compile, and link a vertex and a fragment shader to a program object. They all use OpenGL 1.5 ARB extensions, and when rendering to a single window, it seems as if I can change the shaders at will.
However, I’m working in Qt under MacOS, and when I create multiple QGLWidgets (OpenGL window panes, probably having their own context to render on), attempting to modify and even create the shading programs has some odd effects:
-
If the shading objects are created during the initialization phase, and preloaded with the shaders, all work fine. If I attempt to modifiy the programs at this point, only the most recently created shader program is modified. Also, the program object for multiple (distinct) programs is always the same. ??
-
If the shading objects are created after the initialization process, only the most recently created shader object works. Changing the shading program after creating the object does nothing.
-
Neither deleting nor “deactivating” the shader program nor specific shaders changes any of the above functionality.
4.) Simply disabling the shader does not seem to return OpenGL to the ‘fixed’ pipeline.
The shaders themselves work perfectly. And so far as I can see, the code works perfectly too. No errors are thrown at any state. This is really irritating me, so any help would be greatly appreciated. Now for the long chunk of source code.
// Shader.H
#ifndef ShaderH
#define ShaderH
#include <GLUT/glut.h>
class Shader
{
private:
char *readFile(const char *filename);
protected:
bool loaded;
bool created;
bool compiled;
GLhandleARB shaderObject;
void create(GLenum type);
virtual GLenum getShaderType() = 0;
public:
Shader(const char *filename=0);
~Shader();
GLhandleARB getShader() { return shaderObject; }
void open(const char *filename);
friend class ShaderProgram;
};
#endif
// Shader.cpp
#include "Shader.h"
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
Shader::Shader(const char *filename)
{
loaded=false;
created=false;
compiled=false;
shaderObject=0;
if (filename) open(filename);
}
Shader::~Shader()
{
glDeleteObjectARB(shaderObject);
}
void Shader::create(GLenum type)
{
if (created) return;
shaderObject=glCreateShaderObjectARB(type);
created=true;
}
char *Shader::readFile(const char *filename)
{
ifstream file(filename);
if (!file.is_open()) return 0;
int size;
char *program;
file.seekg(0,ios_base::end);
size=file.tellg();
file.seekg(0,ios_base::beg);
file.clear();
program=new char[size+1];
file.read(program,size);
program[size]=0;
return program;
}
void Shader::open(const char *filename)
{
char *program;
const GLcharARB **programPtr=(const GLcharARB **)&program;
program=readFile(filename);
if (program==0) return;
if (shaderObject==0) create(getShaderType());
glShaderSourceARB(shaderObject,1,programPtr,NULL);
loaded=true;
delete program;
glCompileShaderARB(shaderObject);
int status;
glGetObjectParameterivARB(shaderObject,GL_OBJECT_COMPILE_STATUS_ARB,(GLint *)&status);
if (!status)
throw "Shader::exception(80): shader did not compile!";
compiled=true;
}
// FragmentShader.h
#ifndef FragmentShaderH
#define FragmentShaderH
#include "Shader.h"
class FragmentShader : public Shader
{
private:
protected:
virtual GLenum getShaderType() { return GL_FRAGMENT_SHADER_ARB; }
public:
FragmentShader(const char *filename=0) : Shader(filename) {}
};
// VertexShader.h
#ifndef VertexShaderH
#define VertexShaderH
#include "Shader.h"
class VertexShader : public Shader
{
private:
protected:
virtual GLenum getShaderType() { return GL_VERTEX_SHADER_ARB; }
public:
VertexShader(const char *filename=0) : Shader(filename) {}
};
#endif
// ShaderProgram.h
#ifndef ShaderProgramH
#define ShaderProgramH
#include "VertexShader.h"
#include "FragmentShader.h"
class ShaderProgram
{
private:
GLhandleARB programObject;
VertexShader vertexShader;
FragmentShader fragmentShader;
bool vertexShaderUsed;
bool fragmentShaderUsed;
bool enabled;
char *getInfoLog(GLhandleARB object);
GLint getUniformLocation(const char *varName);
protected:
public:
ShaderProgram(const char *vfile=0,const char *ffile=0);
~ShaderProgram();
void loadVertexShader(const char *filename);
void loadFragmentShader(const char *filename);
void loadShaders(const char *vfile,const char *ffile);
void enableVertexShader();
void enableFragmentShader();
void enableShaders();
void enable();
void disable();
void disableVertexShader();
void disableFragmentShader();
void disableShaders();
bool inline isEnabled() { return enabled; }
GLhandleARB inline getProgramObject() { return programObject; }
void setUniform(const char *varName,int i1);
void setUniform(const char *varName,int i1,int i2);
void setUniform(const char *varName,int i1,int i2,int i3);
void setUniform(const char *varName,int i1,int i2,int i3,int i4);
void setUniform(const char *varName,float f1);
void setUniform(const char *varName,float f1,float f2);
void setUniform(const char *varName,float f1,float f2,float f3);
void setUniform(const char *varName,float f1,float f2,float f3,float f4);
void setUniformv(const char *varName,int size,int *ptr);
void setUniformv(const char *varName,int size,float *ptr);
void setUniformMatrix(const char *varName,int size,float *ptr);
};
#endif
//ShaderProgram.cpp
#include "ShaderProgram.h"
#include <iostream>
using namespace std;
ShaderProgram::ShaderProgram(const char *vfile,const char *ffile)
{
programObject=glCreateProgramObjectARB();
if (vfile) vertexShader.open(vfile);
if (ffile) fragmentShader.open(ffile);
vertexShaderUsed=false;
fragmentShaderUsed=false;
enabled=false;
}
ShaderProgram::~ShaderProgram()
{
disableShaders();
glDeleteObjectARB(programObject);
}
char *ShaderProgram::getInfoLog(GLhandleARB object)
{
char *infoLog;
int infoLogLength;
glGetObjectParameterivARB(object,GL_OBJECT_INFO_LOG_LENGTH_ARB,(GLint *)&infoLogLength);
infoLog=new char[infoLogLength+1];
glGetInfoLogARB(object,infoLogLength,(GLsizei *)&infoLogLength,infoLog);
return infoLog;
}
void ShaderProgram::loadShaders(const char *vfile,const char *ffile)
{
loadVertexShader(vfile);
loadFragmentShader(ffile);
}
void ShaderProgram::loadVertexShader(const char *filename)
{
try
{
if (filename) vertexShader.open(filename);
}
catch (const char *error)
{
char *infoLog=getInfoLog(vertexShader.shaderObject);
cout << error << endl;
cout << infoLog << endl;
delete infoLog;
}
}
void ShaderProgram::loadFragmentShader(const char *filename)
{
try
{
if (filename) fragmentShader.open(filename);
}
catch (const char *error)
{
char *infoLog=getInfoLog(fragmentShader.shaderObject);
cout << error << endl;
cout << infoLog << endl;
delete infoLog;
}
}
void ShaderProgram::enable()
{
disable();
glUseProgramObjectARB(programObject);
enabled=true;
}
void ShaderProgram::enableShaders()
{
enableVertexShader();
enableFragmentShader();
}
void ShaderProgram::enableVertexShader()
{
int status;
disableVertexShader();
glAttachObjectARB(programObject,vertexShader.shaderObject);
glLinkProgramARB(programObject);
glGetObjectParameterivARB(programObject,GL_LINK_STATUS,(GLint *)&status);
if (status==GL_FALSE)
throw "ShaderProgram::error(103):: shader program did not link!";
vertexShaderUsed=true;
}
void ShaderProgram::enableFragmentShader()
{
disableFragmentShader();
glAttachObjectARB(programObject,fragmentShader.shaderObject);
glLinkProgramARB(programObject);
fragmentShaderUsed=true;
}
void ShaderProgram::disable()
{
glUseProgramObjectARB(0);
if (enabled) enabled=false;
}
void ShaderProgram::disableShaders()
{
disableVertexShader();
disableFragmentShader();
}
void ShaderProgram::disableVertexShader()
{
if (vertexShaderUsed)
{
glDetachObjectARB(programObject,vertexShader.shaderObject);
vertexShaderUsed=false;
}
}
void ShaderProgram::disableFragmentShader()
{
if (fragmentShaderUsed)
{
glDetachObjectARB(programObject,fragmentShader.shaderObject);
fragmentShaderUsed=false;
}
}
GLint ShaderProgram::getUniformLocation(const char *varName)
{
GLint loc=glGetUniformLocationARB(programObject,varName);
cout << "loc = " << loc << endl;
return loc;
}
void ShaderProgram::setUniform(const char *varName,int i1)
{
GLint loc=getUniformLocation(varName);
glUniform1iARB(loc,i1);
}
// other setUniform function clipped for space
// ShaderWidget.h
#ifndef ShaderWidgetH
#define ShaderWidgetH
#include <QtGui>
#include <QtOpenGl>
#include "ShaderProgram.h"
class ShaderWidget : public QGLWidget
{
Q_OBJECT;
private:
int layer;
ShaderProgram *program;
GLuint contour;
GLuint volume;
GLuint colors;
protected:
void paintGL();
void initializeGL();
void resizeGL(int width,int height);
public:
ShaderWidget(QWidget *parent=0);
~ShaderWidget();
void loadContour(int layer);
void loadVolume(int layer);
void setColors();
void loadShaders(const char *vname,const char *fname);
};
#endif
// ShaderWidget.h
#include "ShaderWidget.h"
#include <iostream>
#include <fstream>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include "ShaderProgram.h"
using namespace std;
ShaderWidget::ShaderWidget(QWidget *parent) : QGLWidget(parent)
{
layer=0;
program=0;
contour=0;
volume=0;
colors=0;
}
ShaderWidget::~ShaderWidget()
{
glDeleteTextures(1,&contour);
glDeleteTextures(1,&volume);
glDeleteTextures(1,&colors);
delete program;
}
void ShaderWidget::loadContour(int layer)
{
char slice[512*512*4];
int *intSlice=(int *)slice;
ifstream file("../../32_cubes.dat",ios::binary);
// load the 100th axial slice
file.seekg(6+512*512*4*layer,ios_base::beg);
file.read(slice,512*512*4);
file.close();
if (!contour) glGenTextures(1,&contour);
glActiveTextureARB(GL_TEXTURE1_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,contour);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,512,512,0,GL_RGBA,GL_UNSIGNED_BYTE,slice);
}
void ShaderWidget::loadVolume(int layer)
{
char slice[512*512*2];
short *ptr=(short *)slice;
ifstream file("../../CTscan.dat",ios::binary);
// load the 100th axial slice
file.seekg(6+512*512*2*layer,ios_base::beg);
file.read(slice,512*512*2);
file.close();
for (int i=0;i<512*512;i++)
{
ptr[i]+=1000;
if (ptr[i]<0) ptr[i]=0;
if (ptr[1]>4095) ptr[i]=4095;
ptr[i]<<=4;
}
if (!volume) glGenTextures(1,&volume);
glActiveTextureARB(GL_TEXTURE0_ARB);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D,volume);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexImage2D(GL_TEXTURE_2D,0,GL_LUMINANCE16,512,512,0,GL_LUMINANCE,GL_UNSIGNED_SHORT,slice);
}
void ShaderWidget::setColors()
{
char color[32*4] =
{
64, 0, 0,255, 128, 0, 0,255, 192, 0, 0,255, 255, 0, 0,255,
0, 64, 0,255, 0,128, 0,255, 0,192, 0,255, 0,255, 0,255,
0, 0, 64,255, 0, 0,128,255, 0, 0,192,255, 0, 0,255,255,
64, 64, 0,255, 128,128, 0,255, 192,192, 0,255, 255,255, 0,255,
64, 0, 64,255, 128, 0,128,255, 192, 0,192,255, 255, 0,255,255,
0, 64, 64,255, 0,128,128,255, 0,192,192,255, 0,255,255,255,
255, 64, 64,255, 192,128,128,255, 128,192,192,255, 64,255,255,255,
64,255, 64,255, 128,194,128,255, 192,128,192,255, 255, 64,255,255,
};
glGenTextures(1,&colors);
glActiveTextureARB(GL_TEXTURE3_ARB);
glEnable(GL_TEXTURE_1D);
glBindTexture(GL_TEXTURE_1D,colors);
glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_1D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
//glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage1D(GL_TEXTURE_1D,0,GL_RGBA,32,0,GL_RGBA,GL_UNSIGNED_BYTE,color);
}
void ShaderWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (!program) return;
program->enable();
program->setUniform("brightness",0.0f);
program->setUniform("contrast",1.0f);
program->setUniform("contourData",1);
program->setUniform("contourMix",2);
program->setUniform("colorMap",3);
glTranslatef(0.0,0.0,-2.0);
glScalef(2.0,2.0,2.0);
glBegin(GL_QUADS);
{
glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0.0,1.0);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0.0,1.0);
glMultiTexCoord1fARB(GL_TEXTURE3_ARB,0.0);
glVertex3f(-1.0,-1.0,0.0);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB,1.0,1.0);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB,1.0,1.0);
glMultiTexCoord1fARB(GL_TEXTURE3_ARB,1.0);
glVertex3f( 1.0,-1.0,0.0);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB,1.0,0.0);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB,1.0,0.0);
glMultiTexCoord1fARB(GL_TEXTURE3_ARB,1.0);
glVertex3f( 1.0, 1.0,0.0);
glMultiTexCoord2fARB(GL_TEXTURE0_ARB,0.0,0.0);
glMultiTexCoord2fARB(GL_TEXTURE1_ARB,0.0,0.0);
glMultiTexCoord1fARB(GL_TEXTURE3_ARB,0.0);
glVertex3f(-1.0, 1.0,0.0);
}
glEnd();
//glFlush();
program->disable();
glTranslatef(0.0,0.0,0.5);
// The below shape shows up as a black triangle!
glBegin(GL_TRIANGLES);
{
glColor3f(1.0,0.0,1.0);
glVertex3f(0.0,0.0,0.0);
glColor3f(0.0,1.0,0.0);
glVertex3f(1.0,0.0,0.0);
glColor3f(0.0,0.0,1.0);
glVertex3f(1.0,1.0,0.0);
}
glEnd();
glFlush();
}
void ShaderWidget::resizeGL(int width,int height)
{
glViewport(0,0,width,height);
glClearColor(0,0,0,0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glFrustum(-1.5,1.5,-1.5,1.5,0.9,10.0);
}
void ShaderWidget::initializeGL()
{
glEnable(GL_DEPTH_TEST);
glShadeModel(GL_SMOOTH);
loadVolume(0);
loadContour(0);
setColors();
/*
program=new ShaderProgram;
try
{
program->loadShaders("../../toon.vert","../../toon.frag");
program->enableShaders();
}
catch (const char *err)
{
std::cout << err << std::endl;
}
*/
}
void ShaderWidget::loadShaders(const char *vname,const char *fname)
{
if (program) delete program;
program=new ShaderProgram;
//if (!program) return;
//program->disableShaders();
//program->disable();
try
{
program->loadShaders(vname,fname);
program->enableShaders();
}
catch (const char *err)
{
std::cout << err << std::endl;
}
program->enable();
}
// main.cpp
#include <iostream>
#include <QtGui>
#include <QtOpenGl>
#include "ShaderWidget.h"
using namespace std;
int main(int argc,char **argv)
{
QApplication app(argc,argv);
QWidget window;
ShaderWidget left,middle,right;
QHBoxLayout layout;
layout.addWidget(&left);
layout.addWidget(&middle);
layout.addWidget(&right);
left.show();
middle.show();
right.show();
window.setLayout(&layout);
window.resize(400,400);
window.show();
left.loadShaders("../../conmap.vert","../../conmap.frag");
middle.loadShaders("../../conmap.vert","../../conmap.frag");
right.loadShaders("../../conmap.vert","../../conmap.frag");
return app.exec();
}
Sorry about the length, but I don’t want any confusion about what my code does and doesn’t do. (Oh, and if you find any syntactic errors, it was from copy/paste, and I can assure you, it does compile.) Thanks in advance for anyone brave enough to read through all this!