PageRenderTime 25ms CodeModel.GetById 33ms RepoModel.GetById 0ms app.codeStats 0ms

/hw2/2b/solution/hw2bp1.c

https://github.com/carmi/comp356
C | 406 lines | 237 code | 62 blank | 107 comment | 23 complexity | 968fb58023ef489ccb9ea03395a3661b MD5 | raw file
  1. /** Simple ray-tracer implementation.
  2. *
  3. * @author N. Danner
  4. */
  5. #include <assert.h>
  6. #include <float.h>
  7. #include <math.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #ifndef NDEBUG
  12. #include <stdarg.h>
  13. #include <time.h>
  14. #endif
  15. #ifdef __MACOSX__
  16. #include <OpenGL/gl.h>
  17. #include <OpenGL/glu.h>
  18. #include <GLUT/glut.h>
  19. #elif defined __LINUX__ || defined __CYGWIN__
  20. #include <GL/gl.h>
  21. #include <GL/glu.h>
  22. #include <GL/glut.h>
  23. #endif
  24. #include "list356.h"
  25. #include "geom356.h"
  26. #include "surface.h"
  27. #include "surfaces_lights.h"
  28. #include "debug.h"
  29. #define max(a, b) a < b ? b : a
  30. #define EPSILON .001
  31. // Window data.
  32. const int DEFAULT_WIN_WIDTH = 800 ;
  33. const int DEFAULT_WIN_HEIGHT = 600 ;
  34. int win_width ;
  35. int win_height ;
  36. // Viewing data.
  37. point3_t eye ;
  38. point3_t look_at ;
  39. vector3_t up_dir ;
  40. // View-plane specification in camera frame basis.
  41. float view_plane_dist ;
  42. float view_plane_width ;
  43. float view_plane_height ;
  44. vector3_t eye_frame_u, eye_frame_v, eye_frame_w ;
  45. // Surface data.
  46. list356_t* surfaces = NULL ;
  47. // Light data.
  48. list356_t* lights = NULL ;
  49. color_t ambient_light = {.1f, .1f, .1f} ;
  50. // Callbacks.
  51. void handle_display(void) ;
  52. void handle_resize(int, int) ;
  53. // Application functions.
  54. void win2world(int, int, vector3_t*) ;
  55. void compute_eye_frame_basis() ;
  56. // Lighting functions.
  57. color_t get_specular_refl(ray3_t* ray, hit_record_t* hit_rec, int depth) ;
  58. float get_lambert_scale(vector3_t* light_dir, hit_record_t* hit_rec) ;
  59. float get_blinn_phong_scale(ray3_t* ray, vector3_t* light_dir,
  60. hit_record_t* hit_rec) ;
  61. void add_scaled_color(color_t* color, color_t* sfc_color, color_t* light_color,
  62. float scale) ;
  63. // The in-memory copy of the framebuffer; allocated by handle_resize.
  64. GLfloat* fb ;
  65. void handle_exit() ;
  66. int main(int argc, char **argv) {
  67. // Initialize the drawing window.
  68. glutInitWindowSize(DEFAULT_WIN_WIDTH, DEFAULT_WIN_HEIGHT) ;
  69. glutInitWindowPosition(0, 0) ;
  70. glutInit(&argc, argv) ;
  71. glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB) ;
  72. // Create the main window.
  73. glutCreateWindow("Ray tracer") ;
  74. glutReshapeFunc(handle_resize) ;
  75. glutDisplayFunc(handle_display) ;
  76. // Application initialization.
  77. set_view_data(&eye, &look_at, &up_dir) ;
  78. set_view_plane(&view_plane_dist, &view_plane_width, &view_plane_height) ;
  79. surfaces = get_surfaces() ;
  80. lights = get_lights() ;
  81. compute_eye_frame_basis() ;
  82. // Enter the main event loop.
  83. atexit(handle_exit) ;
  84. glutMainLoop() ;
  85. return EXIT_SUCCESS ;
  86. }
  87. void handle_exit() {
  88. debug("handle_exit()") ;
  89. if (fb != NULL) free(fb) ;
  90. }
  91. /** Handle a resize event by recording the new width and height.
  92. *
  93. * @param width the new width of the window.
  94. * @param height the new height of the window.
  95. */
  96. void handle_resize(int width, int height) {
  97. debug("handle_resize(%d, %d)\n", width, height) ;
  98. win_width = width ;
  99. win_height = height ;
  100. if (fb != NULL) free(fb) ;
  101. debug("handle_resize(): allocating in-memory framebuffer") ;
  102. fb = malloc(win_width*win_height*3*sizeof(GLfloat)) ;
  103. bzero(fb, (win_width*win_height*3)*sizeof(GLfloat)) ;
  104. }
  105. /** Get the offset into the frame-buffer for a given pixel position.
  106. *
  107. * @param y the y-coordinate (row) of the pixel.
  108. * @param x the x-coordinate (column) of the pixel.
  109. * @param c the color component.
  110. *
  111. * @return the value i such that fb+i is the framebuffer element to
  112. * set for color c of pixel (x, y).
  113. */
  114. int fb_offset(int y, int x, int c) {
  115. return y*(win_width*3) + x*3 + c ;
  116. }
  117. /** Get the shade determined by a given ray.
  118. *
  119. * @param ray the ray to trace.
  120. * @param t0 the start of the interval for which to get a shade.
  121. * @param t1 the end of the interval for which to get a shade.
  122. * @param depth the maximum number of times a reflect ray will be
  123. * cast for objects with non-NULL reflective color.
  124. *
  125. * @return the color corresponding to the closest object hit by
  126. * <code>r</code> in the interval <code>[t0, t1]</code>.
  127. */
  128. color_t ray_trace(ray3_t ray, float t0, float t1, int depth) {
  129. assert(depth >= 0) ;
  130. color_t color = {0.0, 0.0, 0.0} ;
  131. if (depth ==0) return color ;
  132. hit_record_t hit_rec, closest_hit_rec ;
  133. // Get a hit record for the closest object that is hit.
  134. bool hit_something = false ;
  135. list356_itr_t* s = lst_iterator(surfaces) ;
  136. while (lst_has_next(s)) {
  137. surface_t* sfc = lst_next(s) ;
  138. if (sfc_hit(sfc, &ray, t0, t1, &hit_rec)) {
  139. if (hit_rec.t < t1) {
  140. hit_something = true ;
  141. memcpy(&closest_hit_rec, &hit_rec,
  142. sizeof(hit_record_t)) ;
  143. t1 = hit_rec.t ;
  144. }
  145. }
  146. }
  147. lst_iterator_free(s) ;
  148. // If we hit something, color the pixel.
  149. if (hit_something) {
  150. surface_t* sfc = closest_hit_rec.sfc ;
  151. // Specular reflection.
  152. if (sfc->refl_color != NULL) {
  153. color_t refl_color = get_specular_refl(&ray,
  154. &closest_hit_rec, depth) ;
  155. add_scaled_color(&color, sfc->refl_color, &refl_color, 1.0f) ;
  156. }
  157. // Ambient shading.
  158. add_scaled_color(&color, sfc->ambient_color, &ambient_light, 1.0f) ;
  159. // Lighting.
  160. list356_itr_t* light_itr = lst_iterator(lights) ;
  161. while (lst_has_next(light_itr)) {
  162. light_t* light = lst_next(light_itr) ;
  163. vector3_t light_dir ;
  164. pv_subtract(light->position, &(closest_hit_rec.hit_pt),
  165. &light_dir) ;
  166. normalize(&light_dir) ;
  167. // Check for global shadows.
  168. bool do_lighting = true ;
  169. ray3_t light_ray = {closest_hit_rec.hit_pt, light_dir} ;
  170. float light_dist = dist(&closest_hit_rec.hit_pt,
  171. light->position) ;
  172. s = lst_iterator(surfaces) ;
  173. while (lst_has_next(s)) {
  174. surface_t* sfc = lst_next(s) ;
  175. if (sfc_hit(sfc, &light_ray, EPSILON, light_dist, &hit_rec)) {
  176. do_lighting = false ;
  177. break ;
  178. }
  179. }
  180. lst_iterator_free(s) ;
  181. if (!do_lighting) {
  182. continue ;
  183. }
  184. // Lambertian shading.
  185. float scale = get_lambert_scale(&light_dir, &closest_hit_rec) ;
  186. add_scaled_color(&color, sfc->diffuse_color, light->color, scale) ;
  187. // Blin-Phong shading.
  188. float phong_scale = get_blinn_phong_scale(&ray, &light_dir,
  189. &closest_hit_rec) ;
  190. add_scaled_color(&color, sfc->spec_color, light->color,
  191. phong_scale) ;
  192. } // while(lst_has_next(light_itr))
  193. lst_iterator_free(light_itr) ;
  194. } // if (hit_something)
  195. return color ;
  196. }
  197. /** Get the shade from specular reflection.
  198. *
  199. * @param ray the viewing ray.
  200. * @param hit_rec the hit record for the point being shaded.
  201. * @param depth the current ray-tracing recursion depth.
  202. *
  203. * @return the color to add from ideal specular reflection of <code>ray</code>.
  204. */
  205. color_t get_specular_refl(ray3_t* ray, hit_record_t* hit_rec, int depth) {
  206. ray3_t refl_ray ;
  207. refl_ray.base = hit_rec->hit_pt ;
  208. refl_ray.dir = hit_rec->normal ;
  209. multiply(&hit_rec->normal,
  210. 2*dot(&ray->dir, &hit_rec->normal),
  211. &refl_ray.dir) ;
  212. subtract(&ray->dir, &refl_ray.dir, &refl_ray.dir) ;
  213. color_t refl_color = ray_trace(refl_ray, EPSILON, FLT_MAX,
  214. depth-1) ;
  215. return refl_color ;
  216. }
  217. /** Get the scale factor for Lambertian (diffuse) shading from a single
  218. * light source.
  219. *
  220. * @param light_dir the direction to the light.
  221. * @param hit_rec the hit record for the point being shaded.
  222. *
  223. * @return the scale factor to use for Lambertian shading.
  224. */
  225. float get_lambert_scale(vector3_t* light_dir, hit_record_t* hit_rec) {
  226. return max(0.0f, dot(light_dir, &(hit_rec->normal))) ;
  227. }
  228. /** Get the scale factor for Blinn-Phong specular highlighting from a
  229. * single light source.
  230. *
  231. * @param ray the viewing ray.
  232. * @param light_dir the direction to the light.
  233. * @param hit_rec the hit record for the point being shaded.
  234. *
  235. * @return the scale factor to use for Blinn-Phong shading.
  236. */
  237. float get_blinn_phong_scale(ray3_t* ray, vector3_t* light_dir,
  238. hit_record_t* hit_rec) {
  239. vector3_t view, half_v ;
  240. multiply(&ray->dir, -1.0, &view) ;
  241. normalize(&view) ;
  242. add(&view, light_dir, &half_v) ;
  243. normalize(&half_v) ;
  244. float phong_scale = max(0,
  245. dot(&half_v, &hit_rec->normal)) ;
  246. phong_scale = pow(phong_scale, hit_rec->sfc->phong_exp) ;
  247. return phong_scale ;
  248. }
  249. /** Add a scaled product of surface and light colors to a given color.
  250. * Calling this function has the effect of executing
  251. * <pre>
  252. * color->C += (surface_color->C)*(light_color->C)*scale
  253. * </pre>
  254. * for each color component <code>C</code>.
  255. *
  256. * @param color the base color.
  257. * @param sfc_color the surface color.
  258. * @param light_color the light color.
  259. * @param scale the amount to scale the additional color by.
  260. */
  261. void add_scaled_color(color_t* color, color_t* sfc_color, color_t* light_color,
  262. float scale) {
  263. color->red += (sfc_color->red)*(light_color->red)*scale ;
  264. color->green += (sfc_color->green)*(light_color->green)*scale ;
  265. color->blue += (sfc_color->blue)*(light_color->blue)*scale ;
  266. }
  267. /** Display callback; render the scene.
  268. */
  269. void handle_display() {
  270. // The ray itself.
  271. ray3_t ray ;
  272. ray.base = eye ;
  273. color_t color ;
  274. #ifndef NDEBUG
  275. clock_t start_time, end_time ;
  276. start_time = clock() ;
  277. #endif
  278. for (int x=0; x<win_width; ++x) {
  279. for (int y=0; y<win_height; ++y) {
  280. win2world(x, y, &ray.dir) ;
  281. debug_c((x==400 && y==300),
  282. "view ray = {(%f, %f, %f), (%f, %f, %f)}.\n",
  283. eye.x, eye.y, eye.z,
  284. ray.dir.x, ray.dir.y, ray.dir.z) ;
  285. color = ray_trace(ray, 1.0 + EPSILON, FLT_MAX, 5) ;
  286. *(fb+fb_offset(y, x, 0)) = color.red ;
  287. *(fb+fb_offset(y, x, 1)) = color.green ;
  288. *(fb+fb_offset(y, x, 2)) = color.blue ;
  289. }
  290. }
  291. #ifndef NDEBUG
  292. end_time = clock() ;
  293. debug("handle_display(): frame calculation time = %f sec.",
  294. ((double)(end_time-start_time))/CLOCKS_PER_SEC) ;
  295. #endif
  296. glWindowPos2s(0, 0) ;
  297. glDrawPixels(win_width, win_height, GL_RGB, GL_FLOAT, fb) ;
  298. glFlush() ;
  299. glutSwapBuffers() ;
  300. }
  301. /** Compute the eye frame basis from the eye point, look-at point,
  302. * and up direction. The basic algorithm:
  303. * -# w <- normalized (eye - look_at)
  304. * -# u <- normalized (up x w)
  305. * -# v <- w x u.
  306. */
  307. void compute_eye_frame_basis() {
  308. pv_subtract(&eye, &look_at, &eye_frame_w) ;
  309. normalize(&eye_frame_w) ;
  310. cross(&up_dir, &eye_frame_w, &eye_frame_u) ;
  311. normalize(&eye_frame_u) ;
  312. cross(&eye_frame_w, &eye_frame_u, &eye_frame_v) ;
  313. debug("compute_eye_frame_basis(): eye_frame_u = (%f, %f, %f)",
  314. eye_frame_u.x, eye_frame_u.y, eye_frame_u.z) ;
  315. debug("compute_eye_frame_basis(): eye_frame_v = (%f, %f, %f)",
  316. eye_frame_v.x, eye_frame_v.y, eye_frame_v.z) ;
  317. debug("compute_eye_frame_basis(): eye_frame_w = (%f, %f, %f)",
  318. eye_frame_w.x, eye_frame_w.y, eye_frame_w.z) ;
  319. }
  320. /** Compute the viewing ray direction in the world frame basis.
  321. *
  322. * @param x the x-position on the window (in pixels), starting at the left.
  323. * @param y the y-position on the window (in pixels), starting at the top.
  324. * @param dir a vector3_t object that will be filled with the coordinates
  325. * (in the world frame basis) for the direction of the viewing ray
  326. * through <code>(x, y)</code>.
  327. */
  328. void win2world(int x, int y, vector3_t* dir) {
  329. // Compute coordinates in eye frame of corners of view plane.
  330. float left = -view_plane_width/2.0f ;
  331. float bottom = -view_plane_height/2.0f ;
  332. // Compute vector from eye to window position in eye coordinates.
  333. float u = left + (x+.5f)/win_width*view_plane_width ;
  334. float v = bottom + (y+.5f)/win_height*view_plane_height ;
  335. float w = -view_plane_dist ;
  336. debug_c((x == 400 && y == 300),
  337. "win2world(): u, v, w, = %f, %f, %f", u, v, w) ;
  338. // Transform vector to world coordinates.
  339. dir->x = u*eye_frame_u.x + v*eye_frame_v.x + w*eye_frame_w.x ;
  340. dir->y = u*eye_frame_u.y + v*eye_frame_v.y + w*eye_frame_w.y ;
  341. dir->z = u*eye_frame_u.z + v*eye_frame_v.z + w*eye_frame_w.z ;
  342. debug_c((x==400 && y==300),
  343. "win2world(): i, j, k = %f, %f, %f\n", dir->x, dir->y, dir->z) ;
  344. }