// --------------------------------------------------------------------------
// GPU raycasting tutorial
// Made by Peter Trier jan 2007
//
// This file contains all the elements nessesary to implement a simple
// GPU volume raycaster.
// Notice this implementation requires a shader model 3.0 gfxcard
// --------------------------------------------------------------------------
#include <windows.h>
#include <GL/glew.h>
#include <Cg/cg.h>
#include <Cg/cgGL.h>
#include <GL/gl.h>
#include <GL/glut.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <cmath>
#include <ctime>
#include <cassert>
#include "Vector3.h"
#define MAX_KEYS 256
#define WINDOW_SIZE 800
#define VOLUME_TEX_SIZE 128
using namespace std;
// Globals ------------------------------------------------------------------
bool gKeys[MAX_KEYS];
bool toggle_visuals = true;
CGcontext context;
CGprofile vertexProfile, fragmentProfile;
CGparameter param1,param2;
GLuint renderbuffer;
GLuint framebuffer;
CGprogram vertex_main,fragment_main; // the raycasting shader programs
GLuint volume_texture; // the volume texture
GLuint backface_buffer; // the FBO buffers
GLuint final_image;
float stepsize = 1.0/50.0;
/// Implementation ----------------------------------------
void cgErrorCallback()
{
CGerror lastError = cgGetError();
if(lastError)
{
cout << cgGetErrorString(lastError) << endl;
if(context != NULL)
cout << cgGetLastListing(context) << endl;
exit(0);
}
}
// Sets a uniform texture parameter
void set_tex_param(char* par, GLuint tex,const CGprogram &program,CGparameter param)
{
param = cgGetNamedParameter(program, par);
cgGLSetTextureParameter(param, tex);
cgGLEnableTextureParameter(param);
}
// load_vertex_program: loading a vertex program
void load_vertex_program(CGprogram &v_program,char *shader_path, char *program_name)
{
assert(cgIsContext(context));
v_program = cgCreateProgramFromFile(context, CG_SOURCE,shader_path,
vertexProfile,program_name, NULL);
if (!cgIsProgramCompiled(v_program))
cgCompileProgram(v_program);
cgGLEnableProfile(vertexProfile);
cgGLLoadProgram(v_program);
cgGLDisableProfile(vertexProfile);
}
// load_fragment_program: loading a fragment program
void load_fragment_program(CGprogram &f_program,char *shader_path, char *program_name)
{
assert(cgIsContext(context));
f_program = cgCreateProgramFromFile(context, CG_SOURCE, shader_path,
fragmentProfile,program_name, NULL);
if (!cgIsProgramCompiled(f_program))
cgCompileProgram(f_program);
cgGLEnableProfile(fragmentProfile);
cgGLLoadProgram(f_program);
cgGLDisableProfile(fragmentProfile);
}
void enable_renderbuffers()
{
glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, framebuffer);
glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, renderbuffer);
}
void disable_renderbuffers()
{
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}
void vertex(float x, float y, float z)
{
glColor3f(x,y,z);
glMultiTexCoord3fARB(GL_TEXTURE1_ARB, x, y, z);
glVertex3f(x,y,z);
}
// this method is used to draw the front and backside of the volume
void drawQuads(float x, float y, float z)
{
glBegin(GL_QUADS);
/* Back side */
glNormal3f(0.0, 0.0, -1.0);
vertex(0.0, 0.0, 0.0);
vertex(0.0, y, 0.0);
vertex(x, y, 0.0);
vertex(x, 0.0, 0.0);
/* Front side */
glNormal3f(0.0, 0.0, 1.0);
vertex(0.0, 0.0, z);
vertex(x, 0.0, z);
vertex(x, y, z);
vertex(0.0, y, z);
/* Top side */
glNormal3f(0.0, 1.0, 0.0);
vertex(0.0, y, 0.0);
vertex(0.0, y, z);
vertex(x, y, z);
vertex(x, y, 0.0);
/* Bottom side */
glNormal3f(0.0, -1.0, 0.0);
vertex(0.0, 0.0, 0.0);
vertex(x, 0.0, 0.0);
vertex(x, 0.0, z);
vertex(0.0, 0.0, z);
/* Left side */
glNormal3f(-1.0, 0.0, 0.0);
vertex(0.0, 0.0, 0.0);
vertex(0.0, 0.0, z);
vertex(0.0, y, z);
vertex(0.0, y, 0.0);
/* Right side */
glNormal3f(1.0, 0.0, 0.0);
vertex(x, 0.0, 0.0);
vertex(x, y, 0.0);
vertex(x, y, z);
vertex(x, 0.0, z);
glEnd();
}
// create a test volume texture, here you could load your own volume
void create_volumetexture()
{
int size = VOLUME_TEX_SIZE*VOLUME_TEX_SIZE*VOLUME_TEX_SIZE* 4;
GLubyte *data = new GLubyte[size];
for(int x = 0; x < VOLUME_TEX_SIZE; x++)
{for(int y = 0; y < VOLUME_TEX_SIZE; y++)
{for(int z = 0; z < VOLUME_TEX_SIZE; z++)
{
data[(x*4) + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = z%250;
data[(x*4)+1 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = y%250;
data[(x*4)+2 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 250;
data[(x*4)+3 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 230;
Vector3 p = Vector3(x,y,z)- Vector3(VOLUME_TEX_SIZE-20,VOLUME_TEX_SIZE-30,VOLUME_TEX_SIZE-30);
bool test = (p.length() < 42);
if(test)
data[(x*4)+3 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 0;
p = Vector3(x,y,z)- Vector3(VOLUME_TEX_SIZE/2,VOLUME_TEX_SIZE/2,VOLUME_TEX_SIZE/2);
test = (p.length() < 24);
if(test)
data[(x*4)+3 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 0;
if(x > 20 && x < 40 && y > 0 && y < VOLUME_TEX_SIZE && z > 10 && z < 50)
{
data[(x*4) + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 100;
data[(x*4)+1 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 250;
data[(x*4)+2 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = y%100;
data[(x*4)+3 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 250;
}
if(x > 50 && x < 70 && y > 0 && y < VOLUME_TEX_SIZE && z > 10 && z < 50)
{
data[(x*4) + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 250;
data[(x*4)+1 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 250;
data[(x*4)+2 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = y%100;
data[(x*4)+3 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 250;
}
if(x > 80 && x < 100 && y > 0 && y < VOLUME_TEX_SIZE && z > 10 && z < 50)
{
data[(x*4) + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 250;
data[(x*4)+1 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 70;
data[(x*4)+2 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = y%100;
data[(x*4)+3 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 250;
}
p = Vector3(x,y,z)- Vector3(24,24,24);
test = (p.length() < 40);
if(test)
data[(x*4)+3 + (y * VOLUME_TEX_SIZE * 4) + (z * VOLUME_TEX_SIZE * VOLUME_TEX_SIZE * 4)] = 0;
}}}
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glGenTextures(1, &volume_texture);
glBindTexture(GL_TEXTURE_3D, volume_texture);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER);
glTexImage3D(GL_TEXTURE_3D, 0,GL_RGBA, VOLUME_TEX_SIZE, VOLUME_TEX_SIZE,VOLUME_TEX_SIZE,0, GL_RGBA, GL_UNSIGNED_BYTE,data);
delete []data;
cout << "volume texture created" << endl;
}
// ok let's start things up
void init()
{
cout << "glew init " << endl;
GLenum err = glewInit();
// initialize all the OpenGL extensions
glewGetExtension("glMultiTexCoord2fvARB");
if(glewGetExtension("GL_EXT_framebuffer_object") )cout << "GL_EXT_framebuffer_object support " <<