Я пытаюсь написать базовый том рендеринга, который использует opengl и cg для написания шейдеров.Я помещаю свою передаточную функцию в одномерную текстуру и использую ее в зависимом поиске текстуры в фрагментном шейдере.Моя проблема в том, что я получаю ошибку openGL, когда пытаюсь включить параметр, соответствующий этой 1D текстуре.
Мой код довольно грязный;прямо сейчас это в основном гибридный код, взятый из «Рендеринга томов в реальном времени» и «Руководства пользователя CG».
c файл:
#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;
}
фрагментный шейдер (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;
}
объем данных
При запуске программа выводит:
openGL Error : before updating parameters : invalid operation
строка, которая вызывает ошибку, которую янайден благодаря моей специальной отладке печати:
cgGLEnableTextureParameter(transfer_function);
Есть идеи?