nuclear@0: #include nuclear@0: #include nuclear@0: #include nuclear@0: nuclear@0: #include nuclear@0: #ifndef __APPLE__ nuclear@0: #include nuclear@0: #else nuclear@0: #include nuclear@0: #endif nuclear@0: nuclear@0: #include nuclear@0: #include nuclear@0: #include "sdr.h" nuclear@0: nuclear@3: #define XFER_MAP_SZ 512 nuclear@3: nuclear@0: struct slice_file { nuclear@0: char *name; nuclear@0: struct slice_file *next; nuclear@0: }; nuclear@0: nuclear@0: int init(void); nuclear@0: void disp(void); nuclear@0: void reshape(int x, int y); nuclear@0: void keyb(unsigned char key, int x, int y); nuclear@0: void mouse(int bn, int state, int x, int y); nuclear@0: void motion(int x, int y); nuclear@0: int parse_args(int argc, char **argv); nuclear@0: nuclear@1: static void create_ray_texture(int xsz, int ysz, float vfov, vec2_t *tex_scale); nuclear@0: static vec3_t get_primary_ray_dir(int x, int y, int w, int h, float vfov_deg); nuclear@0: static int round_pow2(int x); nuclear@3: static void create_transfer_map(float mean, float sdev); nuclear@0: nuclear@0: float cam_theta = 0, cam_phi = 0, cam_dist = 4.0; nuclear@0: float cam_x, cam_y, cam_z; nuclear@0: nuclear@0: vec2_t tex_scale; nuclear@0: struct slice_file *flist; nuclear@0: int nslices; nuclear@0: unsigned int sdr, vol_tex, ray_tex; nuclear@0: int win_xsz, win_ysz; nuclear@1: int raytex_needs_recalc = 1; nuclear@0: nuclear@3: unsigned int xfer_tex; nuclear@3: float xfer_mean = 0.5, xfer_sdev = 1.0; nuclear@3: int xfertex_needs_recalc = 1; nuclear@3: nuclear@0: int main(int argc, char **argv) nuclear@0: { nuclear@0: glutInit(&argc, argv); nuclear@0: glutInitWindowSize(1280, 720); nuclear@0: glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); nuclear@0: nuclear@0: if(parse_args(argc, argv) == -1) { nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: glutCreateWindow("Volume Raytracer"); nuclear@0: nuclear@0: glutDisplayFunc(disp); nuclear@0: glutReshapeFunc(reshape); nuclear@0: glutKeyboardFunc(keyb); nuclear@0: glutMouseFunc(mouse); nuclear@0: glutMotionFunc(motion); nuclear@0: nuclear@0: glewInit(); nuclear@0: nuclear@0: if(init() == -1) { nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: glutMainLoop(); nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: int init(void) nuclear@0: { nuclear@0: int i, vol_xsz, vol_ysz; nuclear@0: nuclear@0: if(!(sdr = create_program_load("volray.v.glsl", "volray.p.glsl"))) { nuclear@1: return -1; nuclear@0: } nuclear@1: set_uniform_int(sdr, "volume", 0); nuclear@1: set_uniform_int(sdr, "ray_tex", 1); nuclear@3: set_uniform_int(sdr, "xfer_tex", 2); nuclear@0: nuclear@0: glGenTextures(1, &vol_tex); nuclear@0: glBindTexture(GL_TEXTURE_3D, vol_tex); nuclear@0: glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@0: glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@1: glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP); nuclear@1: glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP); nuclear@1: glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP); nuclear@0: nuclear@0: for(i=0; inext; nuclear@0: nuclear@0: if(!(pix = img_load_pixels(sfile->name, &xsz, &ysz, IMG_FMT_RGBA32))) { nuclear@0: fprintf(stderr, "failed to load image: %s\n", sfile->name); nuclear@0: return -1; nuclear@0: } nuclear@0: nuclear@0: if(i == 0) { nuclear@0: /* allocate storage for the texture */ nuclear@0: glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, xsz, ysz, nslices, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); nuclear@0: nuclear@0: vol_xsz = xsz; nuclear@0: vol_ysz = ysz; nuclear@0: nuclear@0: } else { nuclear@0: if(xsz != vol_xsz || ysz != vol_ysz) { nuclear@0: fprintf(stderr, "%s: inconsistent slice size: %dx%d. expected: %dx%d\n", nuclear@0: sfile->name, xsz, ysz, vol_xsz, vol_ysz); nuclear@0: img_free_pixels(pix); nuclear@0: return -1; nuclear@0: } nuclear@0: } nuclear@0: free(sfile); nuclear@0: nuclear@0: glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, i, xsz, ysz, 1, GL_RGBA, GL_UNSIGNED_BYTE, pix); nuclear@0: img_free_pixels(pix); nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: void disp(void) nuclear@0: { nuclear@1: if(raytex_needs_recalc) { nuclear@1: create_ray_texture(win_xsz, win_ysz, 50.0, &tex_scale); nuclear@1: } nuclear@3: if(xfertex_needs_recalc) { nuclear@3: create_transfer_map(xfer_mean, xfer_sdev); nuclear@3: } nuclear@1: nuclear@0: glMatrixMode(GL_MODELVIEW); nuclear@0: glLoadIdentity(); nuclear@1: glRotatef(-90, 1, 0, 0); nuclear@0: glTranslatef(cam_x, cam_y, -cam_z); nuclear@0: glRotatef(cam_theta, 0, 1, 0); nuclear@0: glRotatef(cam_phi, 1, 0, 0); nuclear@0: glTranslatef(0, 0, -cam_dist); nuclear@0: nuclear@0: glMatrixMode(GL_TEXTURE); nuclear@1: glLoadIdentity(); nuclear@0: glScalef(tex_scale.x, tex_scale.y, 1.0); nuclear@0: nuclear@0: glActiveTexture(GL_TEXTURE0); nuclear@1: glBindTexture(GL_TEXTURE_3D, vol_tex); nuclear@0: glEnable(GL_TEXTURE_3D); nuclear@0: nuclear@0: glActiveTexture(GL_TEXTURE1); nuclear@1: glBindTexture(GL_TEXTURE_2D, ray_tex); nuclear@0: glEnable(GL_TEXTURE_2D); nuclear@0: nuclear@3: glActiveTexture(GL_TEXTURE2); nuclear@3: glBindTexture(GL_TEXTURE_1D, xfer_tex); nuclear@3: glEnable(GL_TEXTURE_1D); nuclear@3: nuclear@0: bind_program(sdr); nuclear@0: glBegin(GL_QUADS); nuclear@0: glColor3f(1, 1, 1); nuclear@1: glTexCoord2f(0, 1); glVertex2f(-1, -1); nuclear@1: glTexCoord2f(1, 1); glVertex2f(1, -1); nuclear@1: glTexCoord2f(1, 0); glVertex2f(1, 1); nuclear@1: glTexCoord2f(0, 0); glVertex2f(-1, 1); nuclear@0: glEnd(); nuclear@0: bind_program(0); nuclear@0: nuclear@3: glActiveTexture(GL_TEXTURE2); nuclear@3: glDisable(GL_TEXTURE_1D); nuclear@1: glActiveTexture(GL_TEXTURE1); nuclear@0: glDisable(GL_TEXTURE_2D); nuclear@0: glActiveTexture(GL_TEXTURE0); nuclear@0: glDisable(GL_TEXTURE_3D); nuclear@0: nuclear@0: glMatrixMode(GL_TEXTURE); nuclear@1: glLoadIdentity(); nuclear@0: nuclear@0: glutSwapBuffers(); nuclear@0: assert(glGetError() == GL_NO_ERROR); nuclear@0: } nuclear@0: nuclear@0: void reshape(int x, int y) nuclear@0: { nuclear@1: printf("reshape: %dx%d\n", x, y); nuclear@0: glViewport(0, 0, x, y); nuclear@0: nuclear@0: if(x != win_xsz || y != win_ysz) { nuclear@1: raytex_needs_recalc = 1; nuclear@0: win_xsz = x; nuclear@0: win_ysz = y; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void keyb(unsigned char key, int x, int y) nuclear@0: { nuclear@0: switch(key) { nuclear@0: case 27: nuclear@0: exit(0); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static int bnstate[32]; nuclear@0: static int prev_x, prev_y; nuclear@0: nuclear@0: void mouse(int bn, int state, int x, int y) nuclear@0: { nuclear@0: bnstate[bn - GLUT_LEFT_BUTTON] = state == GLUT_DOWN; nuclear@0: prev_x = x; nuclear@0: prev_y = y; nuclear@0: } nuclear@0: nuclear@0: void motion(int x, int y) nuclear@0: { nuclear@0: int dx = x - prev_x; nuclear@0: int dy = y - prev_y; nuclear@0: nuclear@0: prev_x = x; nuclear@0: prev_y = y; nuclear@0: nuclear@0: if(bnstate[0]) { nuclear@0: cam_theta += dx * 0.5; nuclear@0: cam_phi += dy * 0.5; nuclear@0: nuclear@1: if(cam_phi <= -90) cam_phi = -89; nuclear@1: if(cam_phi >= 90) cam_phi = 89; nuclear@0: nuclear@0: glutPostRedisplay(); nuclear@0: } nuclear@0: nuclear@0: if(bnstate[1]) { nuclear@1: cam_x += dx * 0.025; nuclear@1: cam_y += dy * 0.025; nuclear@0: glutPostRedisplay(); nuclear@0: } nuclear@0: nuclear@0: if(bnstate[2]) { nuclear@1: cam_dist += dy * 0.025; nuclear@0: if(cam_dist < 0.0) cam_dist = 0.0; nuclear@0: glutPostRedisplay(); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: int parse_args(int argc, char **argv) nuclear@0: { nuclear@0: int i; nuclear@0: struct slice_file *tail; nuclear@3: char *endp; nuclear@0: nuclear@0: for(i=1; iname = argv[i]; nuclear@2: sfile->next = 0; nuclear@2: nuclear@2: if(!flist) { nuclear@2: flist = tail = sfile; nuclear@2: } else { nuclear@2: tail->next = sfile; nuclear@2: tail = sfile; nuclear@2: } nuclear@2: nslices++; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: if(!nslices) { nuclear@0: fprintf(stderr, "pass the slice filenames\n"); nuclear@0: return -1; nuclear@0: } nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@1: static void create_ray_texture(int xsz, int ysz, float vfov, vec2_t *tex_scale) nuclear@0: { nuclear@0: int i, j; nuclear@1: int cur_tex_xsz, cur_tex_ysz; nuclear@0: int tex_xsz = round_pow2(xsz); nuclear@0: int tex_ysz = round_pow2(ysz); nuclear@0: float *teximg, *dir; nuclear@0: nuclear@1: if(!(teximg = malloc(3 * xsz * ysz * sizeof *teximg))) { nuclear@1: return; nuclear@0: } nuclear@0: dir = teximg; nuclear@0: nuclear@1: for(i=0; i cur_tex_xsz || tex_ysz > cur_tex_ysz) { nuclear@1: glBindTexture(GL_TEXTURE_2D, ray_tex); nuclear@1: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); nuclear@1: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); nuclear@1: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); nuclear@1: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); nuclear@1: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F_ARB, tex_xsz, tex_ysz, 0, GL_RGB, GL_FLOAT, 0); nuclear@1: } nuclear@1: nuclear@1: glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, xsz, ysz, GL_RGB, GL_FLOAT, teximg); nuclear@0: free(teximg); nuclear@0: nuclear@0: if(tex_scale) { nuclear@0: tex_scale->x = (float)xsz / (float)tex_xsz; nuclear@0: tex_scale->y = (float)ysz / (float)tex_ysz; nuclear@0: } nuclear@1: raytex_needs_recalc = 0; nuclear@0: } nuclear@0: nuclear@0: static vec3_t get_primary_ray_dir(int x, int y, int w, int h, float vfov_deg) nuclear@0: { nuclear@0: float vfov = M_PI * vfov_deg / 180.0; nuclear@0: float aspect = (float)w / (float)h; nuclear@0: nuclear@0: float ysz = 2.0; nuclear@0: float xsz = aspect * ysz; nuclear@0: nuclear@0: float px = ((float)x / (float)w) * xsz - xsz / 2.0; nuclear@0: float py = 1.0 - ((float)y / (float)h) * ysz; nuclear@0: float pz = 1.0 / tan(0.5 * vfov); nuclear@0: nuclear@0: float mag = sqrt(px * px + py * py + pz * pz); nuclear@0: nuclear@0: return v3_cons(px / mag, py / mag, pz / mag); nuclear@0: } nuclear@0: nuclear@0: static int round_pow2(int x) nuclear@0: { nuclear@0: x--; nuclear@0: x = (x >> 1) | x; nuclear@0: x = (x >> 2) | x; nuclear@0: x = (x >> 4) | x; nuclear@0: x = (x >> 8) | x; nuclear@0: x = (x >> 16) | x; nuclear@0: return x + 1; nuclear@0: } nuclear@0: nuclear@3: static void create_transfer_map(float mean, float sdev) nuclear@3: { nuclear@3: static float map[XFER_MAP_SZ]; nuclear@3: int i; nuclear@3: nuclear@3: if(!xfer_tex) { nuclear@3: glGenTextures(1, &xfer_tex); nuclear@3: glBindTexture(GL_TEXTURE_1D, xfer_tex); nuclear@3: glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); nuclear@3: glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); nuclear@3: glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); nuclear@3: glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); nuclear@3: glTexImage1D(GL_TEXTURE_1D, 0, GL_LUMINANCE32F_ARB, XFER_MAP_SZ, 0, GL_LUMINANCE, GL_FLOAT, 0); nuclear@3: } nuclear@3: nuclear@3: for(i=0; i