# HG changeset patch # User John Tsiombikas # Date 1333148922 -10800 # Node ID b050ce167ff1805a55ba347cfdf80d61b586189d foo diff -r 000000000000 -r b050ce167ff1 Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile Sat Mar 31 02:08:42 2012 +0300 @@ -0,0 +1,26 @@ +src = $(wildcard src/*.c) +obj = $(src:.c=.o) +dep = $(obj:.o=.d) +bin = volray + +CC = gcc +CFLAGS = -pedantic -Wall -g +LDFLAGS = $(libgl) -limago -lvmath -lm + +ifeq ($(shell uname -s), Darwin) + libgl = -framework OpenGL -framework GLUT +else + libgl = -lGL -lglut -lGLEW +endif + +$(bin): $(obj) + $(CC) -o $@ $(obj) $(LDFLAGS) + +-include $(dep) + +%.d: %.c + @$(CPP) $(CFLAGS) $< -MM -MT $(@:.d=.o) >$@ + +.PHONY: clean +clean: + rm -f $(obj) $(bin) diff -r 000000000000 -r b050ce167ff1 src/sdr.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sdr.c Sat Mar 31 02:08:42 2012 +0300 @@ -0,0 +1,333 @@ +#ifndef NO_SHADERS + +#include +#include +#include +#include +#include +#include + +#if defined(unix) || defined(__unix__) +#include +#include +#endif /* unix */ + +#include "sdr.h" + +unsigned int create_vertex_shader(const char *src) +{ + return create_shader(src, GL_VERTEX_SHADER); +} + +unsigned int create_pixel_shader(const char *src) +{ + return create_shader(src, GL_FRAGMENT_SHADER); +} + +unsigned int create_shader(const char *src, unsigned int sdr_type) +{ + unsigned int sdr; + int success, info_len; + char *info_str = 0; + GLenum err; + + sdr = glCreateShader(sdr_type); + assert(glGetError() == GL_NO_ERROR); + glShaderSource(sdr, 1, &src, 0); + err = glGetError(); + assert(err == GL_NO_ERROR); + glCompileShader(sdr); + assert(glGetError() == GL_NO_ERROR); + + glGetShaderiv(sdr, GL_COMPILE_STATUS, &success); + assert(glGetError() == GL_NO_ERROR); + glGetShaderiv(sdr, GL_INFO_LOG_LENGTH, &info_len); + assert(glGetError() == GL_NO_ERROR); + + if(info_len) { + if((info_str = malloc(info_len + 1))) { + glGetShaderInfoLog(sdr, info_len, 0, info_str); + assert(glGetError() == GL_NO_ERROR); + } + } + + if(success) { + fprintf(stderr, info_str ? "done: %s\n" : "done\n", info_str); + } else { + fprintf(stderr, info_str ? "failed: %s\n" : "failed\n", info_str); + glDeleteShader(sdr); + sdr = 0; + } + + free(info_str); + return sdr; +} + +void free_shader(unsigned int sdr) +{ + glDeleteShader(sdr); +} + +unsigned int load_vertex_shader(const char *fname) +{ + return load_shader(fname, GL_VERTEX_SHADER); +} + +unsigned int load_pixel_shader(const char *fname) +{ + return load_shader(fname, GL_FRAGMENT_SHADER); +} + +unsigned int load_shader(const char *fname, unsigned int sdr_type) +{ +#if defined(unix) || defined(__unix__) + struct stat st; +#endif + unsigned int sdr; + size_t filesize; + FILE *fp; + char *src; + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "failed to open shader %s: %s\n", fname, strerror(errno)); + return 0; + } + +#if defined(unix) || defined(__unix__) + fstat(fileno(fp), &st); + filesize = st.st_size; +#else + fseek(fp, 0, SEEK_END); + filesize = ftell(fp); + fseek(fp, 0, SEEK_SET); +#endif /* unix */ + + if(!(src = malloc(filesize + 1))) { + fclose(fp); + return 0; + } + fread(src, 1, filesize, fp); + src[filesize] = 0; + fclose(fp); + + fprintf(stderr, "compiling %s shader: %s... ", (sdr_type == GL_VERTEX_SHADER ? "vertex" : "pixel"), fname); + sdr = create_shader(src, sdr_type); + + free(src); + return sdr; +} + + +unsigned int get_vertex_shader(const char *fname) +{ + return get_shader(fname, GL_VERTEX_SHADER); +} + +unsigned int get_pixel_shader(const char *fname) +{ + return get_shader(fname, GL_FRAGMENT_SHADER); +} + +unsigned int get_shader(const char *fname, unsigned int sdr_type) +{ + unsigned int sdr; + + if(!(sdr = load_shader(fname, sdr_type))) { + return 0; + } + return sdr; +} + + +/* ---- gpu programs ---- */ + +unsigned int create_program(void) +{ + unsigned int prog = glCreateProgram(); + assert(glGetError() == GL_NO_ERROR); + return prog; +} + +unsigned int create_program_link(unsigned int vs, unsigned int ps) +{ + unsigned int prog; + + if(!(prog = create_program())) { + return 0; + } + + if(vs) { + attach_shader(prog, vs); + assert(glGetError() == GL_NO_ERROR); + } + if(ps) { + attach_shader(prog, ps); + assert(glGetError() == GL_NO_ERROR); + } + + if(link_program(prog) == -1) { + free_program(prog); + return 0; + } + return prog; +} + +unsigned int create_program_load(const char *vfile, const char *pfile) +{ + unsigned int vs = 0, ps = 0; + + if(vfile && !(vs = get_vertex_shader(vfile))) { + return 0; + } + if(pfile && !(ps = get_pixel_shader(pfile))) { + return 0; + } + return create_program_link(vs, ps); +} + +void free_program(unsigned int sdr) +{ + glDeleteProgram(sdr); +} + +void attach_shader(unsigned int prog, unsigned int sdr) +{ + glAttachShader(prog, sdr); + assert(glGetError() == GL_NO_ERROR); +} + +int link_program(unsigned int prog) +{ + int linked, info_len, retval = 0; + char *info_str = 0; + + glLinkProgram(prog); + assert(glGetError() == GL_NO_ERROR); + glGetProgramiv(prog, GL_LINK_STATUS, &linked); + assert(glGetError() == GL_NO_ERROR); + glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &info_len); + assert(glGetError() == GL_NO_ERROR); + + if(info_len) { + if((info_str = malloc(info_len + 1))) { + glGetProgramInfoLog(prog, info_len, 0, info_str); + assert(glGetError() == GL_NO_ERROR); + } + } + + if(linked) { + fprintf(stderr, info_str ? "linking done: %s\n" : "linking done\n", info_str); + } else { + fprintf(stderr, info_str ? "linking failed: %s\n" : "linking failed\n", info_str); + retval = -1; + } + + free(info_str); + return retval; +} + +int bind_program(unsigned int prog) +{ + GLenum err; + + glUseProgram(prog); + if(prog && (err = glGetError()) != GL_NO_ERROR) { + /* maybe the program is not linked, try linking first */ + if(err == GL_INVALID_OPERATION) { + if(link_program(prog) == -1) { + return -1; + } + glUseProgram(prog); + return glGetError() == GL_NO_ERROR ? 0 : -1; + } + return -1; + } + return 0; +} + +/* ugly but I'm not going to write the same bloody code over and over */ +#define BEGIN_UNIFORM_CODE \ + int loc, curr_prog; \ + glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); \ + if(curr_prog != prog && bind_program(prog) == -1) { \ + return -1; \ + } \ + if((loc = glGetUniformLocation(prog, name)) != -1) + +#define END_UNIFORM_CODE \ + if(curr_prog != prog) { \ + bind_program(curr_prog); \ + } \ + return loc == -1 ? -1 : 0 + +int set_uniform_int(unsigned int prog, const char *name, int val) +{ + BEGIN_UNIFORM_CODE { + glUniform1i(loc, val); + } + END_UNIFORM_CODE; +} + +int set_uniform_float(unsigned int prog, const char *name, float val) +{ + BEGIN_UNIFORM_CODE { + glUniform1f(loc, val); + } + END_UNIFORM_CODE; +} + +int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z) +{ + BEGIN_UNIFORM_CODE { + glUniform3f(loc, x, y, z); + } + END_UNIFORM_CODE; +} + +int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w) +{ + BEGIN_UNIFORM_CODE { + glUniform4f(loc, x, y, z, w); + } + END_UNIFORM_CODE; +} + +int set_uniform_matrix4(unsigned int prog, const char *name, float *mat) +{ + BEGIN_UNIFORM_CODE { + glUniformMatrix4fv(loc, 1, GL_FALSE, mat); + } + END_UNIFORM_CODE; +} + +int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat) +{ + BEGIN_UNIFORM_CODE { + glUniformMatrix4fv(loc, 1, GL_TRUE, mat); + } + END_UNIFORM_CODE; +} + +int get_attrib_loc(unsigned int prog, const char *name) +{ + int loc, curr_prog; + + glGetIntegerv(GL_CURRENT_PROGRAM, &curr_prog); + if(curr_prog != prog && bind_program(prog) == -1) { + return -1; + } + + loc = glGetAttribLocation(prog, (char*)name); + + if(curr_prog != prog) { + bind_program(curr_prog); + } + return loc; +} + +void set_attrib_float3(int attr_loc, float x, float y, float z) +{ + glVertexAttrib3f(attr_loc, x, y, z); +} + +#endif /* !NO_SHADERS */ diff -r 000000000000 -r b050ce167ff1 src/sdr.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/sdr.h Sat Mar 31 02:08:42 2012 +0300 @@ -0,0 +1,49 @@ +#ifndef SDR_H_ +#define SDR_H_ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* ---- shaders ---- */ +unsigned int create_vertex_shader(const char *src); +unsigned int create_pixel_shader(const char *src); +unsigned int create_shader(const char *src, unsigned int sdr_type); +void free_shader(unsigned int sdr); + +unsigned int load_vertex_shader(const char *fname); +unsigned int load_pixel_shader(const char *fname); +unsigned int load_shader(const char *src, unsigned int sdr_type); + +unsigned int get_vertex_shader(const char *fname); +unsigned int get_pixel_shader(const char *fname); +unsigned int get_shader(const char *fname, unsigned int sdr_type); + +int add_shader(const char *fname, unsigned int sdr); +int remove_shader(const char *fname); + +/* ---- gpu programs ---- */ +unsigned int create_program(void); +unsigned int create_program_link(unsigned int vs, unsigned int ps); +unsigned int create_program_load(const char *vfile, const char *pfile); +void free_program(unsigned int sdr); + +void attach_shader(unsigned int prog, unsigned int sdr); +int link_program(unsigned int prog); +int bind_program(unsigned int prog); + +int set_uniform_int(unsigned int prog, const char *name, int val); +int set_uniform_float(unsigned int prog, const char *name, float val); +int set_uniform_float3(unsigned int prog, const char *name, float x, float y, float z); +int set_uniform_float4(unsigned int prog, const char *name, float x, float y, float z, float w); +int set_uniform_matrix4(unsigned int prog, const char *name, float *mat); +int set_uniform_matrix4_transposed(unsigned int prog, const char *name, float *mat); + +int get_attrib_loc(unsigned int prog, const char *name); +void set_attrib_float3(int attr_loc, float x, float y, float z); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SDR_H_ */ diff -r 000000000000 -r b050ce167ff1 src/volray.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/volray.c Sat Mar 31 02:08:42 2012 +0300 @@ -0,0 +1,332 @@ +#include +#include +#include + +#include +#ifndef __APPLE__ +#include +#else +#include +#endif + +#include +#include +#include "sdr.h" + +struct slice_file { + char *name; + struct slice_file *next; +}; + +int init(void); +void disp(void); +void reshape(int x, int y); +void keyb(unsigned char key, int x, int y); +void mouse(int bn, int state, int x, int y); +void motion(int x, int y); +int parse_args(int argc, char **argv); + +unsigned int create_ray_texture(int xsz, int ysz, float vfov, vec2_t *tex_scale); +static vec3_t get_primary_ray_dir(int x, int y, int w, int h, float vfov_deg); +static int round_pow2(int x); + +float cam_theta = 0, cam_phi = 0, cam_dist = 4.0; +float cam_x, cam_y, cam_z; + +vec2_t tex_scale; +struct slice_file *flist; +int nslices; +unsigned int sdr, vol_tex, ray_tex; +int win_xsz, win_ysz; + +int main(int argc, char **argv) +{ + glutInit(&argc, argv); + glutInitWindowSize(1280, 720); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); + + if(parse_args(argc, argv) == -1) { + return 1; + } + + glutCreateWindow("Volume Raytracer"); + + glutDisplayFunc(disp); + glutReshapeFunc(reshape); + glutKeyboardFunc(keyb); + glutMouseFunc(mouse); + glutMotionFunc(motion); + + glewInit(); + + if(init() == -1) { + return 1; + } + + glutMainLoop(); + return 0; +} + +int init(void) +{ + int i, vol_xsz, vol_ysz; + + if(!(sdr = create_program_load("volray.v.glsl", "volray.p.glsl"))) { + return 1; + } + + glGenTextures(1, &vol_tex); + glBindTexture(GL_TEXTURE_3D, vol_tex); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + + for(i=0; inext; + + if(!(pix = img_load_pixels(sfile->name, &xsz, &ysz, IMG_FMT_RGBA32))) { + fprintf(stderr, "failed to load image: %s\n", sfile->name); + return -1; + } + + if(i == 0) { + /* allocate storage for the texture */ + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, xsz, ysz, nslices, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + + vol_xsz = xsz; + vol_ysz = ysz; + + } else { + if(xsz != vol_xsz || ysz != vol_ysz) { + fprintf(stderr, "%s: inconsistent slice size: %dx%d. expected: %dx%d\n", + sfile->name, xsz, ysz, vol_xsz, vol_ysz); + img_free_pixels(pix); + return -1; + } + } + free(sfile); + + glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, i, xsz, ysz, 1, GL_RGBA, GL_UNSIGNED_BYTE, pix); + img_free_pixels(pix); + } + + return 0; +} + +void disp(void) +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef(cam_x, cam_y, -cam_z); + glRotatef(cam_theta, 0, 1, 0); + glRotatef(cam_phi, 1, 0, 0); + glTranslatef(0, 0, -cam_dist); + + glMatrixMode(GL_TEXTURE); + glPushMatrix(); + glScalef(tex_scale.x, tex_scale.y, 1.0); + + glActiveTexture(GL_TEXTURE0); + glEnable(GL_TEXTURE_3D); + glBindTexture(GL_TEXTURE_3D, vol_tex); + + glActiveTexture(GL_TEXTURE1); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, ray_tex); + + bind_program(sdr); + glBegin(GL_QUADS); + glColor3f(1, 1, 1); + glTexCoord2f(0, 1); + glVertex2f(-1, -1); + glTexCoord2f(1, 1); + glVertex2f(1, -1); + glTexCoord2f(1, 0); + glVertex2f(1, 1); + glTexCoord2f(0, 0); + glVertex2f(-1, 1); + glEnd(); + bind_program(0); + + glDisable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glDisable(GL_TEXTURE_3D); + + glMatrixMode(GL_TEXTURE); + glPopMatrix(); + + glutSwapBuffers(); + assert(glGetError() == GL_NO_ERROR); +} + +void reshape(int x, int y) +{ + glViewport(0, 0, x, y); + + if(x != win_xsz || y != win_ysz) { + ray_tex = create_ray_texture(x, y, 50.0, &tex_scale); + win_xsz = x; + win_ysz = y; + } +} + +void keyb(unsigned char key, int x, int y) +{ + switch(key) { + case 27: + exit(0); + } +} + +static int bnstate[32]; +static int prev_x, prev_y; + +void mouse(int bn, int state, int x, int y) +{ + bnstate[bn - GLUT_LEFT_BUTTON] = state == GLUT_DOWN; + prev_x = x; + prev_y = y; +} + +void motion(int x, int y) +{ + int dx = x - prev_x; + int dy = y - prev_y; + + prev_x = x; + prev_y = y; + + if(bnstate[0]) { + cam_theta += dx * 0.5; + cam_phi += dy * 0.5; + + if(cam_phi < -90) cam_phi = -90; + if(cam_phi > 90) cam_phi = 90; + + glutPostRedisplay(); + } + + if(bnstate[1]) { + cam_y += dy * 0.1; + glutPostRedisplay(); + } + + if(bnstate[2]) { + cam_dist += dy * 0.1; + if(cam_dist < 0.0) cam_dist = 0.0; + glutPostRedisplay(); + } +} + + +int parse_args(int argc, char **argv) +{ + int i; + struct slice_file *tail; + + for(i=1; iname = argv[i]; + sfile->next = 0; + + if(!flist) { + flist = tail = sfile; + } else { + tail->next = sfile; + tail = sfile; + } + nslices++; + } + + if(!nslices) { + fprintf(stderr, "pass the slice filenames\n"); + return -1; + } + + return 0; +} + + +unsigned int create_ray_texture(int xsz, int ysz, float vfov, vec2_t *tex_scale) +{ + int i, j; + unsigned int tex; + int tex_xsz = round_pow2(xsz); + int tex_ysz = round_pow2(ysz); + float *teximg, *dir; + + if(!(teximg = malloc(3 * tex_xsz * tex_ysz * sizeof *teximg))) { + return 0; + } + dir = teximg; + + for(i=0; ix = (float)xsz / (float)tex_xsz; + tex_scale->y = (float)ysz / (float)tex_ysz; + } + return tex; +} + +static vec3_t get_primary_ray_dir(int x, int y, int w, int h, float vfov_deg) +{ + float vfov = M_PI * vfov_deg / 180.0; + float aspect = (float)w / (float)h; + + float ysz = 2.0; + float xsz = aspect * ysz; + + float px = ((float)x / (float)w) * xsz - xsz / 2.0; + float py = 1.0 - ((float)y / (float)h) * ysz; + float pz = 1.0 / tan(0.5 * vfov); + + float mag = sqrt(px * px + py * py + pz * pz); + + return v3_cons(px / mag, py / mag, pz / mag); +} + +static int round_pow2(int x) +{ + x--; + x = (x >> 1) | x; + x = (x >> 2) | x; + x = (x >> 4) | x; + x = (x >> 8) | x; + x = (x >> 16) | x; + return x + 1; +} + diff -r 000000000000 -r b050ce167ff1 volray.p.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/volray.p.glsl Sat Mar 31 02:08:42 2012 +0300 @@ -0,0 +1,72 @@ +uniform sampler3D volume; +uniform sampler2D ray_tex; + +struct Ray { + vec3 origin, dir; +}; + +struct ISect { + bool hit; + float t; + vec3 pos; + vec3 normal; +}; + +vec3 ray_march(Ray ray); +vec3 shade(Ray ray, ISect isect); +Ray get_primary_ray(); + +void main() +{ + Ray ray = get_primary_ray(); + + gl_FragColor = vec4(ray_march(ray), 1.0); +} + +/*vec3 sky(Ray ray) +{ + vec3 col1 = vec3(0.75, 0.78, 0.8); + vec3 col2 = vec3(0.56, 0.7, 1.0); + + float t = max(ray.dir.y, -0.5); + return mix(col1, col2, t); +}*/ + +vec3 ray_march(Ray ray) +{ + const float ray_step = 0.05; + float energy = 1.0; + vec3 pos = ray.origin; + + // assuming view space + while(pos.z < 1.0) { + energy -= texture3D(volume, pos).x; + pos += ray.dir * ray_step; + } + + return vec3(energy, energy, energy); +} + +vec3 shade(Ray ray, ISect isect) +{ + vec3 ldir = normalize(vec3(10.0, 10.0, -10.0) - isect.pos); + vec3 vdir = -ray.dir; + vec3 hdir = normalize(ldir + vdir); + + float ndotl = dot(ldir, isect.normal); + float ndoth = dot(hdir, isect.normal); + + vec3 dcol = vec3(1.0, 1.0, 1.0) * max(ndotl, 0.0); + vec3 scol = vec3(0.6, 0.6, 0.6) * pow(max(ndoth, 0.0), 50.0); + + return vec3(0.01, 0.01, 0.01) + dcol + scol; +} + +Ray get_primary_ray() +{ + Ray ray; + vec2 tc = gl_TexCoord[0].xy; + ray.dir = gl_NormalMatrix * normalize(texture2D(ray_tex, tc).xyz); + ray.origin = (gl_ModelViewMatrix * vec4(0.0, 0.0, 0.0, 1.0)).xyz; + return ray; +} diff -r 000000000000 -r b050ce167ff1 volray.v.glsl --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/volray.v.glsl Sat Mar 31 02:08:42 2012 +0300 @@ -0,0 +1,5 @@ +void main() +{ + gl_Position = gl_Vertex; + gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0; +}