diff --git a/README.md b/README.md index 1e36dc5..9bb5a5e 100755 --- a/README.md +++ b/README.md @@ -6,142 +6,62 @@ Fall 2013 Due Wednesday, 10/02/13 ------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -NOTE: -------------------------------------------------------------------------------- -This project requires an NVIDIA graphics card with CUDA capability! Any card after the Geforce 8xxx series will work. If you do not have an NVIDIA graphics card in the machine you are working on, feel free to use any machine in the SIG Lab or in Moore100 labs. All machines in the SIG Lab and Moore100 are equipped with CUDA capable NVIDIA graphics cards. If this too proves to be a problem, please contact Patrick or Liam as soon as possible. - ------------------------------------------------------------------------------- INTRODUCTION: ------------------------------------------------------------------------------- -In this project, you will extend your raytracer from Project 1 into a full CUDA based global illumination pathtracer. +This is a ray-parallel GPU pathtracer. I thought the most challenging part of +the assignment was definitely stream compaction, which I implemented using the +double buffer method. For each bounce of the ray, I mark which rays are dead +(rays are dead if they hit the light or intersect no objects at all), and then +do an inclusive scan based on the ray's status. When I have the scanned array +that contains the new ray indices and how many rays are alive total, I build a +new pool of rays based on this data, and then launch the next bounce with less +rays to account for. The number of blocks used in the pathtracer kernel +decreases each bounce since I calculated the number of blocks needed based +on how many alive rays there are per bounce. -For this project, you may either choose to continue working off of your codebase from Project 1, or you may choose to use the included basecode in this repository. The basecode for Project 2 is the same as the basecode for Project 1, but with some missing components you will need filled in, such as the intersection testing and camera raycasting methods. +I also did fresnel refraction using Schlick's approximation. -How you choose to extend your raytracer into a pathtracer is a fairly open-ended problem; the supplied basecode is meant to serve as one possible set of guidelines for doing so, but you may choose any approach you want in your actual implementation, including completely scrapping the provided basecode in favor of your own from-scratch solution. - -------------------------------------------------------------------------------- -CONTENTS: ------------------------------------------------------------------------------- -The Project2 root directory contains the following subdirectories: - -* src/ contains the source code for the project. Both the Windows Visual Studio solution and the OSX makefile reference this folder for all source; the base source code compiles on OSX and Windows without modification. -* scenes/ contains an example scene description file. -* renders/ contains two example renders: the raytraced render from Project 1 (GI_no.bmp), and the same scene rendered with global illumination (GI_yes.bmp). -* PROJ1_WIN/ contains a Windows Visual Studio 2010 project and all dependencies needed for building and running on Windows 7. -* PROJ1_OSX/ contains a OSX makefile, run script, and all dependencies needed for building and running on Mac OSX 10.8. -* PROJ1_NIX/ contains a Linux makefile for building and running on Ubuntu - 12.04 LTS. Note that you will need to set the following environment - variables: - - - PATH=$PATH:/usr/local/cuda-5.5/bin - - LD_LIBRARY_PATH=/usr/local/cuda-5.5/lib64:/lib - -The projects build and run exactly the same way as in Project0 and Project1. - +SCREENSHOTS ------------------------------------------------------------------------------- -REQUIREMENTS: -------------------------------------------------------------------------------- -In this project, you are given code for: - -* All of the basecode from Project 1, plus: -* Intersection testing code for spheres and cubes -* Code for raycasting from the camera - -You will need to implement the following features. A number of these required features you may have already implemented in Project 1. If you have, you are ahead of the curve and have less work to do! - -* Full global illumination (including soft shadows, color bleeding, etc.) by pathtracing rays through the scene. -* Properly accumulating emittance and colors to generate a final image -* Supersampled antialiasing -* Parallelization by ray instead of by pixel via stream compaction -* Perfect specular reflection - -You are also required to implement at least two of the following features. Some of these features you may have already implemented in Project 1. If you have, you may NOT resubmit those features and instead must pick two new ones to implement. +Broken stream compaction -* Additional BRDF models, such as Cook-Torrance, Ward, etc. Each BRDF model may count as a separate feature. -* Texture mapping -* Bump mapping -* Translational motion blur -* Fresnel-based Refraction, i.e. glass -* OBJ Mesh loading and rendering without KD-Tree -* Interactive camera -* Integrate an existing stackless KD-Tree library, such as CUKD (https://github.com/unvirtual/cukd) -* Depth of field +![Alt text](/renders/finished/bad.jpg "early test") -Alternatively, implementing just one of the following features can satisfy the "pick two" feature requirement, since these are correspondingly more difficult problems: +Diffuse -* Physically based subsurface scattering and transmission -* Implement and integrate your own stackless KD-Tree from scratch. -* Displacement mapping -* Deformational motion blur +![Alt text](/renders/finished/diffuse.jpg "basic diffuse") -As yet another alternative, if you have a feature or features you really want to implement that are not on this list, let us know, and we'll probably say yes! +Look how much difference antialiasing makes! -------------------------------------------------------------------------------- -NOTES ON GLM: -------------------------------------------------------------------------------- -This project uses GLM, the GL Math library, for linear algebra. You need to know two important points on how GLM is used in this project: - -* In this project, indices in GLM vectors (such as vec3, vec4), are accessed via swizzling. So, instead of v[0], v.x is used, and instead of v[1], v.y is used, and so on and so forth. -* GLM Matrix operations work fine on NVIDIA Fermi cards and later, but pre-Fermi cards do not play nice with GLM matrices. As such, in this project, GLM matrices are replaced with a custom matrix struct, called a cudaMat4, found in cudaMat4.h. A custom function for multiplying glm::vec4s and cudaMat4s is provided as multiplyMV() in intersections.h. +![Alt text](/renders/finished/noAntialias.jpg "no antialiasing" ) -------------------------------------------------------------------------------- -README -------------------------------------------------------------------------------- -All students must replace or augment the contents of this Readme.md in a clear -manner with the following: +![Alt text](/renders/finished/antialias.jpg "antialiasing") -* A brief description of the project and the specific features you implemented. -* At least one screenshot of your project running. -* A 30 second or longer video of your project running. To create the video you - can use http://www.microsoft.com/expression/products/Encoder4_Overview.aspx -* A performance evaluation (described in detail below). +Diffuse, reflection, and Fresnel refraction +![Alt text](/renders/finished/fresnel.bmp "Fresnel refraction") ------------------------------------------------------------------------------- PERFORMANCE EVALUATION ------------------------------------------------------------------------------- -The performance evaluation is where you will investigate how to make your CUDA -programs more efficient using the skills you've learned in class. You must have -performed at least one experiment on your code to investigate the positive or -negative effects on performance. - -One such experiment would be to investigate the performance increase involved -with adding a spatial data-structure to your scene data. -Another idea could be looking at the change in timing between various block -sizes. +Performance evaluation of ray parallel pathtracing with stream compaction vs. +pixel parallel pathtracing. -A good metric to track would be number of rays per second, or frames per -second, or number of objects displayable at 60fps. - -We encourage you to get creative with your tweaks. Consider places in your code -that could be considered bottlenecks and try to improve them. - -Each student should provide no more than a one page summary of their -optimizations along with tables and or graphs to visually explain any -performance differences. +![Alt text](/renders/finished/analysis.jpg "Fresnel refraction") ------------------------------------------------------------------------------- -THIRD PARTY CODE POLICY +VIDEO ------------------------------------------------------------------------------- -* Use of any third-party code must be approved by asking on the Google group. If it is approved, all students are welcome to use it. Generally, we approve use of third-party code that is not a core part of the project. For example, for the ray tracer, we would approve using a third-party library for loading models, but would not approve copying and pasting a CUDA function for doing refraction. -* Third-party code must be credited in README.md. -* Using third-party code without its approval, including using another student's code, is an academic integrity violation, and will result in you receiving an F for the semester. +Video link: -------------------------------------------------------------------------------- -SELF-GRADING -------------------------------------------------------------------------------- -* On the submission date, email your grade, on a scale of 0 to 100, to Liam, liamboone+cis565@gmail.com, with a one paragraph explanation. Be concise and realistic. Recall that we reserve 30 points as a sanity check to adjust your grade. Your actual grade will be (0.7 * your grade) + (0.3 * our grade). We hope to only use this in extreme cases when your grade does not realistically reflect your work - it is either too high or too low. In most cases, we plan to give you the exact grade you suggest. -* Projects are not weighted evenly, e.g., Project 0 doesn't count as much as the path tracer. We will determine the weighting at the end of the semester based on the size of each project. +https://vimeo.com/user10815579 ------------------------------------------------------------------------------- -SUBMISSION +THIRD PARTY CODE ------------------------------------------------------------------------------- -As with the previous project, you should fork this project and work inside of your fork. Upon completion, commit your finished project back to your fork, and make a pull request to the master repository. -You should include a README.md file in the root directory detailing the following - -* A brief description of the project and specific features you implemented -* At least one screenshot of your project running, and at least one screenshot of the final rendered output of your pathtracer -* Instructions for building and running your project if they differ from the base code -* A link to your blog post detailing the project -* A list of all third-party code used +For stream compaction, I used the pseudocode from the class slides. + +I referred to http://www.cs.unc.edu/~rademach/xroads-RT/RTarticle.html +to do refraction and the wikipedia page to do Schlick's approximation. diff --git a/renders/finished/analysis.jpg b/renders/finished/analysis.jpg new file mode 100644 index 0000000..d2043e6 Binary files /dev/null and b/renders/finished/analysis.jpg differ diff --git a/renders/finished/antialias.jpg b/renders/finished/antialias.jpg new file mode 100644 index 0000000..608c34f Binary files /dev/null and b/renders/finished/antialias.jpg differ diff --git a/renders/finished/bad.jpg b/renders/finished/bad.jpg new file mode 100644 index 0000000..745905e Binary files /dev/null and b/renders/finished/bad.jpg differ diff --git a/renders/finished/diffuse.jpg b/renders/finished/diffuse.jpg new file mode 100644 index 0000000..16dce75 Binary files /dev/null and b/renders/finished/diffuse.jpg differ diff --git a/renders/finished/fresnel.bmp b/renders/finished/fresnel.bmp new file mode 100644 index 0000000..366d2c1 Binary files /dev/null and b/renders/finished/fresnel.bmp differ diff --git a/renders/finished/noAntialias.jpg b/renders/finished/noAntialias.jpg new file mode 100644 index 0000000..c623210 Binary files /dev/null and b/renders/finished/noAntialias.jpg differ diff --git a/scenes/cube.obj b/scenes/cube.obj new file mode 100644 index 0000000..259a9e5 --- /dev/null +++ b/scenes/cube.obj @@ -0,0 +1,36 @@ +# This file uses centimeters as units for non-parametric coordinates. + +v -0.726254 3.626313 1.325702 +v 1.034476 3.172953 0.492459 +v -0.227552 5.563140 1.325702 +v 1.533178 5.109779 0.492459 +v -1.034476 5.770910 -0.492459 +v 0.726254 5.317549 -1.325702 +v -1.533178 3.834084 -0.492459 +v 0.227552 3.380723 -1.325702 +vt 0.375000 0.000000 +vt 0.625000 0.000000 +vt 0.375000 0.250000 +vt 0.625000 0.250000 +vt 0.375000 0.500000 +vt 0.625000 0.500000 +vt 0.375000 0.750000 +vt 0.625000 0.750000 +vt 0.375000 1.000000 +vt 0.625000 1.000000 +vt 0.875000 0.000000 +vt 0.875000 0.250000 +vt 0.125000 0.000000 +vt 0.125000 0.250000 +f 1/1 2/2 3/3 +f 3/3 2/2 4/4 +f 3/3 4/4 5/5 +f 5/5 4/4 6/6 +f 5/5 6/6 7/7 +f 7/7 6/6 8/8 +f 7/7 8/8 1/9 +f 1/9 8/8 2/10 +f 2/2 8/11 4/4 +f 4/4 8/11 6/12 +f 7/13 1/1 5/14 +f 5/14 1/1 3/3 diff --git a/scenes/cubeScene.txt b/scenes/cubeScene.txt new file mode 100644 index 0000000..b5b5e45 --- /dev/null +++ b/scenes/cubeScene.txt @@ -0,0 +1,175 @@ +MATERIAL 0 //white diffuse +RGB 1 1 1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 1 //red diffuse +RGB .63 .06 .04 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 2 //green diffuse +RGB .15 .48 .09 +SPECEX 50 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 3 //red glossy +RGB .63 .06 .04 +SPECEX 20 +SPECRGB 1 1 1 +REFL 1 +REFR 1 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 4 //white glossy +RGB 1 1 1 +SPECEX 25 +SPECRGB 1 1 1 +REFL 1 +REFR 1 +REFRIOR 2.2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 5 //glass +RGB 0 0 0 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 1 +REFRIOR 2.2 +SCATTER 0 +ABSCOEFF .02 5.1 5.7 +RSCTCOEFF 13 +EMITTANCE 0 + +MATERIAL 6 //green glossy +RGB .15 .48 .09 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 2.6 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 7 //light +RGB 1 1 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 1 + +MATERIAL 8 //light +RGB 1 1 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 10 + +CAMERA +RES 1024 720 +FOVY 25 +ITERATIONS 2000 +FILE test.bmp +frame 0 +EYE 0 4.5 15 +VIEW 0 0 -1 +UP 0 1 0 +FOCAL 12 +APERTURE 0.2 + +OBJECT 0 +cube +material 0 +frame 0 +TRANS 0 0 0 +ROTAT 0 0 90 +SCALE .01 10 10 + +OBJECT 1 +cube +material 0 +frame 0 +TRANS 0 5 -5 +ROTAT 0 90 0 +SCALE .01 10 10 + +OBJECT 2 +cube +material 0 +frame 0 +TRANS 0 10 0 +ROTAT 0 0 90 +SCALE .01 10 10 + +OBJECT 3 +cube +material 1 +frame 0 +TRANS -5 5 0 +ROTAT 0 0 0 +SCALE .01 10 10 + +OBJECT 4 +cube +material 2 +frame 0 +TRANS 5 5 0 +ROTAT 0 0 0 +SCALE .01 10 10 + +OBJECT 5 +cube.obj +material 6 +frame 0 +TRANS 0 0 0 +ROTAT 0 0 0 +SCALE 1 1 1 + +OBJECT 6 +cube +material 8 +frame 0 +TRANS 0 9 0 +ROTAT 0 0 90 +SCALE .3 3 3 \ No newline at end of file diff --git a/scenes/phongMatScene.txt b/scenes/phongMatScene.txt new file mode 100644 index 0000000..1180e74 --- /dev/null +++ b/scenes/phongMatScene.txt @@ -0,0 +1,191 @@ +MATERIAL 0 //white diffuse +RGB 1 1 1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 1 //red diffuse +RGB .63 .06 .04 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 2 //green diffuse +RGB .15 .48 .09 +SPECEX 50 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 3 //red glossy +RGB .63 .06 .04 +SPECEX 20 +SPECRGB 1 1 1 +REFL 1 +REFR 1 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 4 //white glossy +RGB 1 1 1 +SPECEX 25 +SPECRGB 1 1 1 +REFL 1 +REFR 1 +REFRIOR 2.2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 5 //glass +RGB 0 0 0 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 1 +REFRIOR 2.2 +SCATTER 0 +ABSCOEFF .02 5.1 5.7 +RSCTCOEFF 13 +EMITTANCE 0 + +MATERIAL 6 //green glossy +RGB .15 .48 .09 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 2.6 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 7 //light +RGB 1 1 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 1 + +MATERIAL 8 //light +RGB 1 1 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 10 + +CAMERA +RES 1024 720 +FOVY 25 +ITERATIONS 2000 +FILE test.bmp +frame 0 +EYE 0 4.5 15 +VIEW 0 0 -1 +UP 0 1 0 +FOCAL 12 +APERTURE 0.2 + +OBJECT 0 +cube +material 0 +frame 0 +TRANS 0 0 0 +ROTAT 0 0 90 +SCALE .01 10 10 + +OBJECT 1 +cube +material 0 +frame 0 +TRANS 0 5 -5 +ROTAT 0 90 0 +SCALE .01 10 10 + +OBJECT 2 +cube +material 0 +frame 0 +TRANS 0 10 0 +ROTAT 0 0 90 +SCALE .01 10 10 + +OBJECT 3 +cube +material 1 +frame 0 +TRANS -5 5 0 +ROTAT 0 0 0 +SCALE .01 10 10 + +OBJECT 4 +cube +material 2 +frame 0 +TRANS 5 5 0 +ROTAT 0 0 0 +SCALE .01 10 10 + +OBJECT 5 +sphere +material 4 +frame 0 +TRANS 0 2 0 +ROTAT 0 180 0 +SCALE 3 3 3 + +OBJECT 6 +sphere +material 3 +frame 0 +TRANS 2 5 2 +ROTAT 0 180 0 +SCALE 2.5 2.5 2.5 + +OBJECT 7 +sphere +material 6 +frame 0 +TRANS -2 5 -2 +ROTAT 0 180 0 +SCALE 3 3 3 + +OBJECT 8 +cube +material 8 +frame 0 +TRANS 0 9 0 +ROTAT 0 0 90 +SCALE .3 3 3 \ No newline at end of file diff --git a/scenes/reflectionTest.txt b/scenes/reflectionTest.txt new file mode 100644 index 0000000..0824f3d --- /dev/null +++ b/scenes/reflectionTest.txt @@ -0,0 +1,175 @@ +MATERIAL 0 //white diffuse +RGB 1 0.9 1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 1 //red diffuse +RGB 0.7 0.2 0.2 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 2 //green diffuse +RGB 0.2 0.8 0.1 +SPECEX 50 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 3 //blue diffuse +RGB 0.1 0.2 0.9 +SPECEX 20 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 4 //white reflective +RGB 1 1 1 +SPECEX 25 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 5 //red reflective +RGB 1 1 1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 1 +REFRIOR 2 +SCATTER 0 +ABSCOEFF .02 5.1 5.7 +RSCTCOEFF 13 +EMITTANCE 0 + +MATERIAL 6 //green reflective +RGB 0.3 0.8 0.2 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 2.6 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 7 //blue reflective +RGB 0.2 0.4 0.9 +SPECEX 0 +SPECRGB 0 0 0 +REFL 1 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 8 //light +RGB 1 1 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 10 + +CAMERA +RES 800 800 +FOVY 25 +ITERATIONS 2000 +FILE test.bmp +frame 0 +EYE 0 4.5 15 +VIEW 0 0 -1 +UP 0 1 0 +FOCAL 12 +APERTURE 0 + +OBJECT 0 //floor +cube +material 0 +frame 0 +TRANS 0 0 0 +ROTAT 0 0 90 +SCALE .01 10 10 + +OBJECT 1 //back wall +cube +material 0 +frame 0 +TRANS 0 5 -5 +ROTAT 0 90 0 +SCALE .01 10 10 + +OBJECT 2 //ceiling +cube +material 0 +frame 0 +TRANS 0 10 0 +ROTAT 0 0 90 +SCALE .01 10 10 + +OBJECT 3 //left wall +cube +material 3 +frame 0 +TRANS -5 5 0 +ROTAT 0 0 0 +SCALE .01 10 10 + +OBJECT 4 //right wall +cube +material 2 +frame 0 +TRANS 5 5 0 +ROTAT 0 0 0 +SCALE .01 10 10 + +OBJECT 5 +sphere +material 5 +frame 0 +TRANS 0 2 0 +ROTAT 0 180 0 +SCALE 3 3 3 + +OBJECT 6 +cube +material 8 +frame 0 +TRANS 0 9.5 0 +ROTAT 0 0 90 +SCALE .3 3 3 \ No newline at end of file diff --git a/scenes/sampleScene.txt b/scenes/sampleScene.txt index 52d079e..e2a65c8 100755 --- a/scenes/sampleScene.txt +++ b/scenes/sampleScene.txt @@ -1,5 +1,5 @@ MATERIAL 0 //white diffuse -RGB 0.9 0.9 0.9 +RGB 1 1 1 SPECEX 0 SPECRGB 1 1 1 REFL 0 @@ -35,7 +35,7 @@ RSCTCOEFF 0 EMITTANCE 0 MATERIAL 3 //red glossy -RGB .63 .26 .24 +RGB .63 .06 .04 SPECEX 0 SPECRGB 1 1 1 REFL 0 @@ -71,7 +71,7 @@ RSCTCOEFF 13 EMITTANCE 0 MATERIAL 6 //green glossy -RGB .35 .48 .29 +RGB .15 .48 .09 SPECEX 0 SPECRGB 1 1 1 REFL 0 @@ -115,10 +115,8 @@ frame 0 EYE 0 4.5 12 VIEW 0 0 -1 UP 0 1 0 -frame 1 -EYE 0 4.5 12 -VIEW 0 0 -1 -UP 0 1 0 +FOCAL 12 +APERTURE 0.3 OBJECT 0 cube @@ -127,10 +125,6 @@ frame 0 TRANS 0 0 0 ROTAT 0 0 90 SCALE .01 10 10 -frame 1 -TRANS 0 0 0 -ROTAT 0 0 90 -SCALE .01 10 10 OBJECT 1 cube @@ -139,10 +133,6 @@ frame 0 TRANS 0 5 -5 ROTAT 0 90 0 SCALE .01 10 10 -frame 1 -TRANS 0 5 -5 -ROTAT 0 90 0 -SCALE .01 10 10 OBJECT 2 cube @@ -151,10 +141,6 @@ frame 0 TRANS 0 10 0 ROTAT 0 0 90 SCALE .01 10 10 -frame 1 -TRANS 0 10 0 -ROTAT 0 0 90 -SCALE .01 10 10 OBJECT 3 cube @@ -163,10 +149,6 @@ frame 0 TRANS -5 5 0 ROTAT 0 0 0 SCALE .01 10 10 -frame 1 -TRANS -5 5 0 -ROTAT 0 0 0 -SCALE .01 10 10 OBJECT 4 cube @@ -175,10 +157,6 @@ frame 0 TRANS 5 5 0 ROTAT 0 0 0 SCALE .01 10 10 -frame 1 -TRANS 5 5 0 -ROTAT 0 0 0 -SCALE .01 10 10 OBJECT 5 sphere @@ -187,10 +165,6 @@ frame 0 TRANS 0 2 0 ROTAT 0 180 0 SCALE 3 3 3 -frame 1 -TRANS 0 2 0 -ROTAT 0 180 0 -SCALE 3 3 3 OBJECT 6 sphere @@ -199,10 +173,6 @@ frame 0 TRANS 2 5 2 ROTAT 0 180 0 SCALE 2.5 2.5 2.5 -frame 1 -TRANS 2 5 2 -ROTAT 0 180 0 -SCALE 2.5 2.5 2.5 OBJECT 7 sphere @@ -211,10 +181,6 @@ frame 0 TRANS -2 5 -2 ROTAT 0 180 0 SCALE 3 3 3 -frame 1 -TRANS -2 5 -2 -ROTAT 0 180 0 -SCALE 3 3 3 OBJECT 8 cube @@ -222,8 +188,4 @@ material 8 frame 0 TRANS 0 10 0 ROTAT 0 0 90 -SCALE .3 3 3 -frame 1 -TRANS 0 10 0 -ROTAT 0 0 90 -SCALE .3 3 3 +SCALE .3 3 3 \ No newline at end of file diff --git a/scenes/sphereScene.txt b/scenes/sphereScene.txt new file mode 100644 index 0000000..55d6509 --- /dev/null +++ b/scenes/sphereScene.txt @@ -0,0 +1,191 @@ +MATERIAL 0 //white diffuse +RGB 255 254 224 +SPECEX 20 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 1 //red diffuse +RGB 240 153 192 +SPECEX 30 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 2 //green diffuse +RGB 138 216 255 +SPECEX 10 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 3 //red glossy +RGB .63 .06 .04 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 4 //white glossy +RGB 1 1 1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 5 //glass +RGB 0 0 0 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 1 +REFRIOR 2.2 +SCATTER 0 +ABSCOEFF .02 5.1 5.7 +RSCTCOEFF 13 +EMITTANCE 0 + +MATERIAL 6 //green glossy +RGB .15 .48 .09 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 2.6 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 7 //light +RGB 1 1 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 1 + +MATERIAL 8 //light +RGB 1 1 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 15 + +CAMERA +RES 800 800 +FOVY 25 +ITERATIONS 1000 +FILE test.bmp +frame 0 +EYE 0 5 10 +VIEW 0 0 -1 +UP 0 1 0 +FOCAL 9 +APERTURE 0 + +OBJECT 0 +cube +material 0 //FLOOR +frame 0 +TRANS 0 0 0 +ROTAT 0 0 90 +SCALE .01 10 10 + +OBJECT 1 //BACK +cube +material 0 +frame 0 +TRANS 0 5 -5 +ROTAT 0 90 0 +SCALE .01 10 10 + +OBJECT 2 //CEILING +cube +material 4 +frame 0 +TRANS 0 10 0 +ROTAT 0 0 90 +SCALE .01 10 10 + +OBJECT 3 +cube +material 4 //right wall +frame 0 +TRANS -5 5 0 +ROTAT 0 0 0 +SCALE .01 10 10 + +OBJECT 4 //left wall +cube +material 4 +frame 0 +TRANS 5 5 0 +ROTAT 0 0 0 +SCALE .01 10 10 + +OBJECT 5 +sphere +material 1 +frame 0 +TRANS 0 2 0 +ROTAT 0 0 0 +SCALE 2.5 2.5 2.5 + +OBJECT 6 +sphere +material 2 +frame 0 +TRANS 2 5 2 +ROTAT 0 180 0 +SCALE 2.5 2.5 2.5 + +OBJECT 7 +sphere +material 2 +frame 0 +TRANS -2 5 -2 +ROTAT 0 180 0 +SCALE 3 3 3 + +OBJECT 8 +cube +material 8 +frame 0 +TRANS 0 10 2 +ROTAT 0 0 90 +SCALE .3 3 3 \ No newline at end of file diff --git a/src/image.cpp b/src/image.cpp index 67bf157..46b6235 100755 --- a/src/image.cpp +++ b/src/image.cpp @@ -38,7 +38,7 @@ image::~image(){ //------------------------ float image::applyGamma(float f){ - //apply gamma correction, use simple power law gamma for now. + //apply gamma correction, use simple power law gamma for now. TODO: sRGB return pow(f/float(gamma.divisor), gamma.gamma); } diff --git a/src/interactions.h b/src/interactions.h index 6561796..8c3f5f6 100755 --- a/src/interactions.h +++ b/src/interactions.h @@ -8,10 +8,56 @@ #include "intersections.h" +struct Fresnel { + float reflectionCoefficient; + float transmissionCoefficient; +}; + +struct AbsorptionAndScatteringProperties{ + glm::vec3 absorptionCoefficient; + float reducedScatteringCoefficient; +}; + //forward declaration +__host__ __device__ bool calculateScatterAndAbsorption(ray& r, float& depth, AbsorptionAndScatteringProperties& currentAbsorptionAndScattering, glm::vec3& unabsorbedColor, material m, float randomFloatForScatteringDistance, float randomFloat2, float randomFloat3); __host__ __device__ glm::vec3 getRandomDirectionInSphere(float xi1, float xi2); +__host__ __device__ glm::vec3 calculateTransmission(glm::vec3 absorptionCoefficient, float distance); +__host__ __device__ glm::vec3 calculateTransmissionDirection(glm::vec3 normal, glm::vec3 incident, float incidentIOR, float transmittedIOR); +__host__ __device__ glm::vec3 calculateReflectionDirection(glm::vec3 normal, glm::vec3 incident); +__host__ __device__ Fresnel calculateFresnel(glm::vec3 normal, glm::vec3 incident, float incidentIOR, float transmittedIOR, glm::vec3 reflectionDirection, glm::vec3 transmissionDirection); __host__ __device__ glm::vec3 calculateRandomDirectionInHemisphere(glm::vec3 normal, float xi1, float xi2); +//TODO (OPTIONAL): IMPLEMENT THIS FUNCTION +__host__ __device__ glm::vec3 calculateTransmission(glm::vec3 absorptionCoefficient, float distance) { + return glm::vec3(0,0,0); +} + +//TODO (OPTIONAL): IMPLEMENT THIS FUNCTION +__host__ __device__ bool calculateScatterAndAbsorption(ray& r, float& depth, AbsorptionAndScatteringProperties& currentAbsorptionAndScattering, + glm::vec3& unabsorbedColor, material m, float randomFloatForScatteringDistance, float randomFloat2, float randomFloat3){ + return false; +} + +//TODO (OPTIONAL): IMPLEMENT THIS FUNCTION +__host__ __device__ glm::vec3 calculateTransmissionDirection(glm::vec3 normal, glm::vec3 incident, float incidentIOR, float transmittedIOR) { + return glm::vec3(0,0,0); +} + +//TODO (OPTIONAL): IMPLEMENT THIS FUNCTION +__host__ __device__ glm::vec3 calculateReflectionDirection(glm::vec3 normal, glm::vec3 incident) { + //nothing fancy here + return glm::vec3(0,0,0); +} + +//TODO (OPTIONAL): IMPLEMENT THIS FUNCTION +__host__ __device__ Fresnel calculateFresnel(glm::vec3 normal, glm::vec3 incident, float incidentIOR, float transmittedIOR, glm::vec3 reflectionDirection, glm::vec3 transmissionDirection) { + Fresnel fresnel; + + fresnel.reflectionCoefficient = 1; + fresnel.transmissionCoefficient = 0; + return fresnel; +} + //LOOK: This function demonstrates cosine weighted random direction generation in a sphere! __host__ __device__ glm::vec3 calculateRandomDirectionInHemisphere(glm::vec3 normal, float xi1, float xi2) { @@ -47,4 +93,13 @@ __host__ __device__ glm::vec3 getRandomDirectionInSphere(float xi1, float xi2) { return glm::vec3(0,0,0); } +//TODO (PARTIALLY OPTIONAL): IMPLEMENT THIS FUNCTION +//returns 0 if diffuse scatter, 1 if reflected, 2 if transmitted. +__host__ __device__ int calculateBSDF(ray& r, glm::vec3 intersect, glm::vec3 normal, glm::vec3 emittedColor, + AbsorptionAndScatteringProperties& currentAbsorptionAndScattering, + glm::vec3& color, glm::vec3& unabsorbedColor, material m){ + + return 1; +}; + #endif diff --git a/src/intersections.h b/src/intersections.h index a6b9469..5d4433c 100755 --- a/src/intersections.h +++ b/src/intersections.h @@ -17,9 +17,8 @@ __host__ __device__ glm::vec3 getPointOnRay(ray r, float t); __host__ __device__ glm::vec3 multiplyMV(cudaMat4 m, glm::vec4 v); __host__ __device__ glm::vec3 getSignOfRay(ray r); __host__ __device__ glm::vec3 getInverseDirectionOfRay(ray r); -__host__ __device__ float boxIntersectionTest(staticGeom sphere, ray r, glm::vec3& intersectionPoint, glm::vec3& normal); -__host__ __device__ float boxIntersectionTest(glm::vec3 boxMin, glm::vec3 boxMax, staticGeom box, ray r, glm::vec3& intersectionPoint, glm::vec3& normal); -__host__ __device__ float sphereIntersectionTest(staticGeom sphere, ray r, glm::vec3& intersectionPoint, glm::vec3& normal); +__host__ __device__ float boxIntersectionTest(staticGeom sphere, ray& r, glm::vec3& intersectionPoint, glm::vec3& normal); +__host__ __device__ float sphereIntersectionTest(staticGeom sphere, ray& r, glm::vec3& intersectionPoint, glm::vec3& normal); __host__ __device__ glm::vec3 getRandomPointOnCube(staticGeom cube, float randomSeed); //Handy dandy little hashing function that provides seeds for random number generation @@ -69,112 +68,124 @@ __host__ __device__ glm::vec3 getSignOfRay(ray r){ return glm::vec3((int)(inv_direction.x < 0), (int)(inv_direction.y < 0), (int)(inv_direction.z < 0)); } -//Wrapper for cube intersection test for testing against unit cubes -__host__ __device__ float boxIntersectionTest(staticGeom box, ray r, glm::vec3& intersectionPoint, glm::vec3& normal){ - return boxIntersectionTest(glm::vec3(-.5,-.5,-.5), glm::vec3(.5,.5,.5), box, r, intersectionPoint, normal); -} - -//Cube intersection test, return -1 if no intersection, otherwise, distance to intersection -__host__ __device__ float boxIntersectionTest(glm::vec3 boxMin, glm::vec3 boxMax, staticGeom box, ray r, glm::vec3& intersectionPoint, glm::vec3& normal){ - glm::vec3 currentNormal = glm::vec3(0,0,0); - - ray ro = r; - - glm::vec3 iP0 = multiplyMV(box.inverseTransform,glm::vec4(r.origin, 1.0f)); - glm::vec3 iP1 = multiplyMV(box.inverseTransform,glm::vec4(r.origin+r.direction, 1.0f)); - glm::vec3 iV0 = iP1 - iP0; - - r.origin = iP0; - r.direction = glm::normalize(iV0); - - float tmin, tmax, tymin, tymax, tzmin, tzmax; - - glm::vec3 rsign = getSignOfRay(r); - glm::vec3 rInverseDirection = getInverseDirectionOfRay(r); - - if((int)rsign.x==0){ - tmin = (boxMin.x - r.origin.x) * rInverseDirection.x; - tmax = (boxMax.x - r.origin.x) * rInverseDirection.x; - }else{ - tmin = (boxMax.x - r.origin.x) * rInverseDirection.x; - tmax = (boxMin.x - r.origin.x) * rInverseDirection.x; - } - - if((int)rsign.y==0){ - tymin = (boxMin.y - r.origin.y) * rInverseDirection.y; - tymax = (boxMax.y - r.origin.y) * rInverseDirection.y; - }else{ - tymin = (boxMax.y - r.origin.y) * rInverseDirection.y; - tymax = (boxMin.y - r.origin.y) * rInverseDirection.y; - } - - if ( (tmin > tymax) || (tymin > tmax) ){ - return -1; - } - if (tymin > tmin){ - tmin = tymin; - } - if (tymax < tmax){ - tmax = tymax; - } - - if((int)rsign.z==0){ - tzmin = (boxMin.z - r.origin.z) * rInverseDirection.z; - tzmax = (boxMax.z - r.origin.z) * rInverseDirection.z; - }else{ - tzmin = (boxMax.z - r.origin.z) * rInverseDirection.z; - tzmax = (boxMin.z - r.origin.z) * rInverseDirection.z; - } - - if ( (tmin > tzmax) || (tzmin > tmax) ){ - return -1; - } - if (tzmin > tmin){ - tmin = tzmin; - } - if (tzmax < tmax){ - tmax = tzmax; - } - if(tmin<0){ - return -1; - } - glm::vec3 osintersect = r.origin + tmin*r.direction; - - if(abs(osintersect.x-abs(boxMax.x))<.001){ - currentNormal = glm::vec3(1,0,0); - }else if(abs(osintersect.y-abs(boxMax.y))<.001){ - currentNormal = glm::vec3(0,1,0); - }else if(abs(osintersect.z-abs(boxMax.z))<.001){ - currentNormal = glm::vec3(0,0,1); - }else if(abs(osintersect.x+abs(boxMin.x))<.001){ - currentNormal = glm::vec3(-1,0,0); - }else if(abs(osintersect.y+abs(boxMin.y))<.001){ - currentNormal = glm::vec3(0,-1,0); - }else if(abs(osintersect.z+abs(boxMin.z))<.001){ - currentNormal = glm::vec3(0,0,-1); - } +__host__ __device__ float triangleIntersectionTest(glm::vec3& p1, glm::vec3& p2, glm::vec3& p3, ray& r, glm::vec3& intersectionPoint, glm::vec3& normal){ - intersectionPoint = multiplyMV(box.transform, glm::vec4(osintersect, 1.0)); + return -1; +} - normal = multiplyMV(box.transform, glm::vec4(currentNormal,0.0)); - return glm::length(intersectionPoint-ro.origin); +//TODO: IMPLEMENT THIS FUNCTION +//Cube intersection test, return -1 if no intersection, otherwise, distance to intersection +__host__ __device__ float boxIntersectionTest(staticGeom box, ray& r, glm::vec3& intersectionPoint, glm::vec3& normal){ + + //transform ray from world to object space + glm::vec3 ro = multiplyMV(box.inverseTransform, glm::vec4(r.origin, 1.0f)); + glm::vec3 rd = glm::normalize(multiplyMV(box.inverseTransform, glm::vec4(r.direction, 0.0f))); + + ray rt; rt.origin = ro; rt.direction = rd; + + //since cube is in its own unit space, limit between +/- 0.5 + float bL = -0.5f; float bH = 0.5f; + + //check if rd[i] is parallel and outside of any pair of slabs + if(abs(rd.x) <= FLT_EPSILON && ro.x > bH && ro.x < bL) return -1; + if(abs(rd.y) <= FLT_EPSILON && ro.y > bH && ro.y < bL) return -1; + if(abs(rd.z) <= FLT_EPSILON && ro.z > bH && ro.z < bL) return -1; + + //find front and back t-values where ray intersect slab + + //x slabs + float tNear = (bL - ro.x)/rd.x; + float tFar = (bH - ro.x)/rd.x; + + if(tNear > tFar){ + float temp = tFar; + tFar = tNear; + tNear = temp; + } + if(tFar < 0) return -1; + + //y slabs + float tyMin = (bL - ro.y)/rd.y; + float tyMax = (bH - ro.y)/rd.y; + + if(tyMin > tyMax){ + float temp = tyMin; + tyMin = tyMax; + tyMax = temp; + } + if(tyMin > tNear) tNear = tyMin; + if(tyMax < tFar) tFar = tyMax; + + if(tNear > tFar || tFar < 0) return -1; + + //z slabs + float tzMin = (bL - ro.z)/rd.z; + float tzMax = (bH - ro.z)/rd.z; + + if(tzMin > tzMax){ + float temp = tzMin; + tzMin = tzMax; + tzMax = temp; + } + + if(tzMin > tNear) tNear = tzMin; + if(tzMax < tFar) tFar = tzMax; + + if(tNear > tFar || tFar < 0) return -1; + + //if passed all tests, tNear is the intersection + + //find point of intersection and get the normal in object space + glm::vec4 objIntersectionPoint = glm::vec4(getPointOnRay(rt, tNear), 1.0f); + glm::vec4 objNormal(0, 0, 0, 0); //normal in object space + + if(fabs(objIntersectionPoint.x - bH) <= 0.001f){ //if on +x plane, normal is 1 0 0 + objNormal[0] = 1.0f; + } + else if(fabs(objIntersectionPoint.x - bL) <= 0.001f){ //if on -x plane, normal is -1 0 0 + objNormal[0] = -1.0f; + } + else if(fabs(objIntersectionPoint.y - bH) <= 0.001f){ //if on +y plane, normal is 0 1 0 + objNormal[1] = 1.0f; + } + else if(fabs(objIntersectionPoint.y - bL) <= 0.001f){ //if on -y plane, normal is 0 -1 0 + objNormal[1] = -1.0f; + } + else if(fabs(objIntersectionPoint.z - bH) <= 0.001f){ //if on +z plane, normal is 0 0 1 + objNormal[2] = 1.0f; + } + else if(fabs(objIntersectionPoint.z - bL) <= 0.001f){ //if on -z plane, normal is 0 0 -1 + objNormal[2] = -1.0f; + } + + //find intersection point and transform back to world space + glm::vec3 realIntersectionPoint = multiplyMV(box.transform, objIntersectionPoint); + glm::vec3 realOrigin = multiplyMV(box.transform, glm::vec4(0,0,0,1)); + + intersectionPoint = realIntersectionPoint; + normal = glm::normalize(multiplyMV(box.transform, objNormal)); + + return glm::length(intersectionPoint - ro); } //LOOK: Here's an intersection test example from a sphere. Now you just need to figure out cube and, optionally, triangle. //Sphere intersection test, return -1 if no intersection, otherwise, distance to intersection -__host__ __device__ float sphereIntersectionTest(staticGeom sphere, ray r, glm::vec3& intersectionPoint, glm::vec3& normal){ +__host__ __device__ float sphereIntersectionTest(staticGeom sphere, ray& r, glm::vec3& intersectionPoint, glm::vec3& normal){ float radius = .5; - + + //transform ray to object space glm::vec3 ro = multiplyMV(sphere.inverseTransform, glm::vec4(r.origin,1.0f)); glm::vec3 rd = glm::normalize(multiplyMV(sphere.inverseTransform, glm::vec4(r.direction,0.0f))); ray rt; rt.origin = ro; rt.direction = rd; float vDotDirection = glm::dot(rt.origin, rt.direction); + + //discriminant float radicand = vDotDirection * vDotDirection - (glm::dot(rt.origin, rt.origin) - pow(radius, 2)); if (radicand < 0){ return -1; @@ -194,6 +205,7 @@ __host__ __device__ float sphereIntersectionTest(staticGeom sphere, ray r, glm:: t = max(t1, t2); } + //find intersection point and transform back to world space glm::vec3 realIntersectionPoint = multiplyMV(sphere.transform, glm::vec4(getPointOnRay(rt, t), 1.0)); glm::vec3 realOrigin = multiplyMV(sphere.transform, glm::vec4(0,0,0,1)); @@ -261,20 +273,28 @@ __host__ __device__ glm::vec3 getRandomPointOnCube(staticGeom cube, float random } +//TODO: IMPLEMENT THIS FUNCTION //Generates a random point on a given sphere __host__ __device__ glm::vec3 getRandomPointOnSphere(staticGeom sphere, float randomSeed){ - float radius=.5f; - thrust::default_random_engine rng(hash(randomSeed)); - thrust::uniform_real_distribution u01(-1,1); - thrust::uniform_real_distribution u02(0,TWO_PI); - - float theta = (float)u02(rng); - float cosphi = (float)u01(rng); - float sinphi = sqrt(1 - cosphi*cosphi); - glm::vec3 point = radius*glm::vec3(sinphi*cos(theta),sinphi*sin(theta),cosphi); - glm::vec3 randPoint = multiplyMV(sphere.transform, glm::vec4(point,1.0f)); - - return randPoint; + + //using trig method from wolfram alpha sphere point picking page + + thrust::default_random_engine rng(hash(randomSeed)); + thrust::uniform_real_distribution u01(-1, 1); + thrust::uniform_real_distribution u02(0, 2*PI); + + glm::vec3 point (0.5f, 0.5f, 0.5f); + + float z = (float)u01(rng); + float theta = (float)u02(rng); + + point.x = sqrt(1 - (z*z)) * cos(theta); + point.y = sqrt( 1 - (z*z)) * sin(theta); + point.z = z; + + glm::vec3 randPoint = multiplyMV(sphere.transform, glm::vec4(point,1.0f)); + + return randPoint; } #endif diff --git a/src/main.cpp b/src/main.cpp index 81836b1..05125ee 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -25,6 +25,10 @@ int main(int argc, char** argv){ bool loadedScene = false; finishedRender = false; + clearImage = false; + + totalTime = 0; + targetFrame = 0; singleFrameMode = false; @@ -53,6 +57,11 @@ int main(int argc, char** argv){ width = renderCam->resolution[0]; height = renderCam->resolution[1]; + oldMousePos = glm::vec2(0,0); + + startZoom = false; + startRotate = false; + if(targetFrame>=renderCam->frames){ cout << "Warning: Specified target frame is out of range, defaulting to frame 0." << endl; targetFrame = 0; @@ -90,6 +99,8 @@ int main(int argc, char** argv){ #else glutDisplayFunc(display); glutKeyboardFunc(keyboard); + glutMouseFunc(mouseClick); + glutMotionFunc(mouseMove); glutMainLoop(); #endif @@ -109,22 +120,14 @@ void runCuda(){ uchar4 *dptr=NULL; iterations++; cudaGLMapBufferObject((void**)&dptr, pbo); - - //pack geom and material arrays - geom* geoms = new geom[renderScene->objects.size()]; - material* materials = new material[renderScene->materials.size()]; - - for(int i=0; iobjects.size(); i++){ - geoms[i] = renderScene->objects[i]; - } - for(int i=0; imaterials.size(); i++){ - materials[i] = renderScene->materials[i]; - } - - + + //t = clock(); // execute the kernel - cudaRaytraceCore(dptr, renderCam, targetFrame, iterations, materials, renderScene->materials.size(), geoms, renderScene->objects.size() ); + cudaRaytraceCore(dptr, renderCam, targetFrame, iterations, materials, renderScene->materials.size(), geoms, renderScene->objects.size(), clearImage); + //t = clock() - t; + //cout<<(float)t/CLOCKS_PER_SEC<resolution.x; x++){ for(int y=0; yresolution.y; y++){ int index = x + (y * renderCam->resolution.x); - outputImage.writePixelRGB(renderCam->resolution.x-1-x,y,renderCam->image[index]); - } - } + outputImage.writePixelRGB(renderCam->resolution.x-1-x,y,renderCam->image[index]); + } + } gammaSettings gamma; gamma.applyGamma = true; gamma.gamma = 1.0; - gamma.divisor = 1.0; //renderCam->iterations; + gamma.divisor = renderCam->iterations; outputImage.setGammaSettings(gamma); string filename = renderCam->imageName; string s; @@ -155,7 +158,7 @@ void runCuda(){ outputImage.saveImageRGB(filename); cout << "Saved frame " << s << " to " << filename << endl; finishedRender = true; - if(singleFrameMode==true){ + if(singleFrameMode==true){ cudaDeviceReset(); exit(0); } @@ -175,6 +178,52 @@ void runCuda(){ } +glm::vec3 toSphereCoord(int sX, int sY){ + + float radius = min(width/2.0f, height/2.0f); //sphere that can fit into the window + float ptX = (sX - width/2.0f); + float ptY = (sY - height/2.0f); + float ptZ; + + float r = ptX*ptX + ptY*ptY; + + if(r > 1.0f){ + float s = 1.0f/sqrt(r); + ptX *= s; + ptY *= s; + ptZ = 0.0f; + } + else + ptZ = sqrt(1.0f - r); + + return glm::vec3(ptZ, ptY, ptZ); +} + +void rotateX(float x){ + //compute rotation matrix + glm::vec3 pos = renderCam->positions[targetFrame]; + glm::vec3 up = renderCam->ups[targetFrame]; + glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), x, up); + + //find new camera position + glm::vec4 tempPos (pos.x, pos.y, pos.z, 0.0f); + tempPos= rotation*tempPos; + renderCam->positions[targetFrame] = glm::vec3(tempPos.x, tempPos.y, tempPos.z); +} + +void rotateY(float y){ + //compute rotation matrix + glm::vec3 pos = renderCam->positions[targetFrame]; + glm::vec3 up = renderCam->ups[targetFrame]; + glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), y, glm::cross(up, pos)); + + //find new camera position + glm::vec4 tempPos (pos.x, pos.y, pos.z, 0.0f); + tempPos= rotation*tempPos; + renderCam->positions[targetFrame] = glm::vec3(tempPos.x, tempPos.y, tempPos.z); + +} + #ifdef __APPLE__ void display(){ @@ -197,8 +246,9 @@ void runCuda(){ } #else - + void display(){ + runCuda(); string title = "565Raytracer | " + utilityCore::convertIntToString(iterations) + " Iterations"; @@ -220,15 +270,104 @@ void runCuda(){ void keyboard(unsigned char key, int x, int y) { - std::cout << key << std::endl; + //std::cout << key << std::endl; + switch (key) { - case(27): - exit(1); - break; + + case(27): + exit(1); + break; + + case('a'): + rotateX(1.0f); + //renderCam->positions[targetFrame].x += 1.0f; + iterations = 0; + clearImage = true; + break; + + case('d'): + rotateX(-1.0f); + //renderCam->positions[targetFrame].x -= 1.0f; + iterations = 0; + clearImage = true; + break; + + case('w'): + rotateY(1.0f); + //renderCam->positions[targetFrame].y += 1.0f; + iterations = 0; + clearImage = true; + break; + + case('s'): + rotateY(-1.0f); + //renderCam->positions[targetFrame].y -= 1.0f; + iterations = 0; + clearImage = true; + break; + + } + + } + + void mouseClick(int button, int state, int x, int y){ + + switch (state){ + case(GLUT_UP): + startZoom = false; + startRotate=false; + clearImage = false; + break; + + case(GLUT_DOWN): + clearImage = true; + iterations = 0; + oldMousePos.x = x; + oldMousePos.y = y; + break; } + + switch (button){ + case(GLUT_LEFT_BUTTON): + startRotate = true; + break; + + case(GLUT_RIGHT_BUTTON): + startZoom = true; + break; + } + } + void mouseMove(int x, int y){ + if(startZoom){ + if(oldMousePos.x > x || oldMousePos.y > y) + renderCam->positions[targetFrame].z += 0.2f; + else if(oldMousePos.x < x || oldMousePos.y < y) + renderCam->positions[targetFrame].z -= 0.2f; + + } + else if(startRotate){ + if(oldMousePos.x > x) + rotateX(1.0f); + else if(oldMousePos.x < x) + rotateX(-1.0f); + + if(oldMousePos.y > y) + rotateY(1.0f); + else if(oldMousePos.y < y) + rotateY(-1.0f); + } + + oldMousePos.x = x; + oldMousePos.y = y; + clearImage = true; + iterations = 0; + + } + + #endif @@ -293,6 +432,12 @@ void initPBO(GLuint* pbo){ } } +void memCleanup(){ + delete[] geoms; + delete[] materials; + +} + void initCuda(){ // Use device with highest Gflops/s cudaGLSetGLDevice( compat_getMaxGflopsDeviceId() ); @@ -301,7 +446,22 @@ void initCuda(){ // Clean up on program exit atexit(cleanupCuda); + atexit(cudaFreeMemory); + atexit(memCleanup); + //pack geom and material arrays + geoms = new geom[renderScene->objects.size()]; + materials = new material[renderScene->materials.size()]; + + for(int i=0; iobjects.size(); i++){ + geoms[i] = renderScene->objects[i]; + } + for(int i=0; imaterials.size(); i++){ + materials[i] = renderScene->materials[i]; + } + //copy stuff to the gpu + cudaAllocMemory(renderCam, materials, renderScene->materials.size(), geoms, renderScene->objects.size()); + runCuda(); } @@ -395,4 +555,4 @@ void shut_down(int return_code){ glfwTerminate(); #endif exit(return_code); -} +} \ No newline at end of file diff --git a/src/main.h b/src/main.h index 0bab7cb..456da2f 100755 --- a/src/main.h +++ b/src/main.h @@ -30,6 +30,11 @@ #include "utilities.h" #include "scene.h" +#include "glm/glm.hpp" +#include "glm/gtc/quaternion.hpp" +#include "glm/gtc/matrix_transform.hpp" +#include + #if CUDA_VERSION >= 5000 #include #include @@ -48,6 +53,8 @@ using namespace std; scene* renderScene; camera* renderCam; +geom* geoms; +material* materials; int targetFrame; int iterations; bool finishedRender; @@ -81,11 +88,25 @@ int main(int argc, char** argv); void runCuda(); + +//------------------------------- +//---------CAMERA STUFF---------- +//------------------------------- +glm::vec2 oldMousePos; +bool startZoom; +bool startRotate; + +void rotateX(float x); +void rotateY(float y); +glm::vec3 toSphereCoord(int x, int y); + #ifdef __APPLE__ void display(); #else void display(); void keyboard(unsigned char key, int x, int y); + void mouseMove(int x, int y); + void mouseClick(int button, int state, int x, int y); #endif //------------------------------- @@ -98,12 +119,17 @@ void runCuda(); void init(int argc, char* argv[]); #endif +void memCleanup(); void initPBO(GLuint* pbo); void initCuda(); void initTextures(); void initVAO(); GLuint initShader(const char *vertexShaderPath, const char *fragmentShaderPath); +bool clearImage; +clock_t t; +float totalTime; + //------------------------------- //---------CLEANUP STUFF--------- //------------------------------- diff --git a/src/meshLoader.cpp b/src/meshLoader.cpp new file mode 100644 index 0000000..18038e9 --- /dev/null +++ b/src/meshLoader.cpp @@ -0,0 +1,89 @@ +#include "meshLoader.h" + +using namespace std; + +meshLoader::meshLoader(){ + +} + +meshLoader::~meshLoader(){ + verts.clear(); + faces.clear(); + normals.clear(); +} + +void meshLoader::loadObj(char* fileName){ + cout<<"loading "< +#include +#include +#include "glm/glm.hpp" + +class meshLoader{ + +public: + meshLoader(); + ~meshLoader(); + + void loadObj(char* fileName); + void printVerts(); + void printFaces(); + void printNormals(); + + std::vector verts; + std::vector faces; + std::vector normals; + +private: + +}; + +#endif \ No newline at end of file diff --git a/src/raytraceKernel.cu b/src/raytraceKernel.cu index 87a65a6..07ffca6 100755 --- a/src/raytraceKernel.cu +++ b/src/raytraceKernel.cu @@ -9,12 +9,32 @@ #include #include #include "sceneStructs.h" +#include "glm/glm.hpp" #include "utilities.h" #include "raytraceKernel.h" #include "intersections.h" #include "interactions.h" #include -#include "glm/glm.hpp" +#include "glm/gtc/matrix_transform.hpp" + +#if CUDA_VERSION >= 5000 + #include +#else + #include +#endif + + +//global variables +rayBounce* cudaRayPool; //for stream compaction, pool of rays that are still alive +rayBounce* cudaTempRayPool; //for switching and replacing rays in stream compaction +int* cudaCompactA; +int* cudaCompactB; + +glm::vec3* cudaimage; +staticGeom* cudageoms; +material* cudaMaterials; +int* cudaLights; +int numLights; void checkCUDAError(const char *msg) { cudaError_t err = cudaGetLastError(); @@ -35,44 +55,42 @@ __host__ __device__ glm::vec3 generateRandomNumberFromThread(glm::vec2 resolutio return glm::vec3((float) u01(rng), (float) u01(rng), (float) u01(rng)); } -//Kernel that does the initial raycast from the camera. -__host__ __device__ ray raycastFromCameraKernel(glm::vec2 resolution, float time, int x, int y, glm::vec3 eye, glm::vec3 view, glm::vec3 up, glm::vec2 fov){ - - int index = x + (y * resolution.x); - - thrust::default_random_engine rng(hash(index*time)); - thrust::uniform_real_distribution u01(0,1); - - //standard camera raycast stuff - glm::vec3 E = eye; - glm::vec3 C = view; - glm::vec3 U = up; - float fovx = fov.x; - float fovy = fov.y; - - float CD = glm::length(C); - - glm::vec3 A = glm::cross(C, U); - glm::vec3 B = glm::cross(A, C); - glm::vec3 M = E+C; - glm::vec3 H = (A*float(CD*tan(fovx*(PI/180))))/float(glm::length(A)); - glm::vec3 V = (B*float(CD*tan(-fovy*(PI/180))))/float(glm::length(B)); - - float sx = (x)/(resolution.x-1); - float sy = (y)/(resolution.y-1); - - glm::vec3 P = M + (((2*sx)-1)*H) + (((2*sy)-1)*V); - glm::vec3 PmE = P-E; - glm::vec3 R = E + (float(200)*(PmE))/float(glm::length(PmE)); - - glm::vec3 direction = glm::normalize(R); - //major performance cliff at this point, TODO: find out why! - ray r; - r.origin = eye; - r.direction = direction; - return r; +//TODO: IMPLEMENT THIS FUNCTION +//Function that does the initial raycast from the camera +__host__ __device__ ray raycastFromCameraKernel(glm::vec2 resolution, int x, int y, glm::vec3 eye, glm::vec3 view, glm::vec3 up, glm::vec2 fov){ + + ray r; + + //ray creation from camear stuff to be used in raycastFromCameraKernel + glm::vec3 M = eye + view; //center of screen + + //project screen to world space + glm::vec3 A = glm::cross(view, up); + glm::vec3 B = glm::cross(A, view); + + float C = glm::length(view); + + float phi = fov.y/180.0f * PI; //convert to radians + B = glm::normalize(B); + glm::vec3 V = C * tan(phi) * B; + + float theta = fov.x/180.0f * PI; + A = glm::normalize(A); + glm::vec3 H = C * tan(theta) * A; + + //find the world space coord of the pixel + float sx = (float)x / (resolution.x-1.0f); + float sy = (float)y / (resolution.y-1.0f); + + glm::vec3 P = M + H * (2.0f * sx - 1.0f) + V * (1.0f - 2.0f * sy); + + r.origin = eye; + r.direction = glm::normalize(P - eye); + + return r; } + //Kernel that blacks out a given image buffer __global__ void clearImage(glm::vec2 resolution, glm::vec3* image){ int x = (blockIdx.x * blockDim.x) + threadIdx.x; @@ -83,19 +101,21 @@ __global__ void clearImage(glm::vec2 resolution, glm::vec3* image){ } } -//Kernel that writes the image to the OpenGL PBO directly. -__global__ void sendImageToPBO(uchar4* PBOpos, glm::vec2 resolution, glm::vec3* image){ +//Kernel that writes the image to the OpenGL PBO directly. +__global__ void sendImageToPBO(uchar4* PBOpos, glm::vec2 resolution, glm::vec3* image, float iterations){ int x = (blockIdx.x * blockDim.x) + threadIdx.x; int y = (blockIdx.y * blockDim.y) + threadIdx.y; int index = x + (y * resolution.x); + //iterations = 1; + if(x<=resolution.x && y<=resolution.y){ - glm::vec3 color; - color.x = image[index].x*255.0; - color.y = image[index].y*255.0; - color.z = image[index].z*255.0; + glm::vec3 color; + color.x = image[index].x*255.0 / iterations; + color.y = image[index].y*255.0 / iterations; + color.z = image[index].z*255.0 / iterations; if(color.x>255){ color.x = 255; @@ -111,117 +131,570 @@ __global__ void sendImageToPBO(uchar4* PBOpos, glm::vec2 resolution, glm::vec3* // Each thread writes one pixel location in the texture (textel) PBOpos[index].w = 0; - PBOpos[index].x = color.x; + PBOpos[index].x = color.x; PBOpos[index].y = color.y; PBOpos[index].z = color.z; } } -//TODO: IMPLEMENT THIS FUNCTION -//Core raytracer kernel -__global__ void raytraceRay(glm::vec2 resolution, float time, float bounce, cameraData cam, int rayDepth, glm::vec3* colors, - staticGeom* geoms, int numberOfGeoms, material* materials, int numberOfMaterials){ - int x = (blockIdx.x * blockDim.x) + threadIdx.x; - int y = (blockIdx.y * blockDim.y) + threadIdx.y; - int index = x + (y * resolution.x); +//reflection +__host__ __device__ void reflect(glm::vec3& incoming, glm::vec3 normal, float nDotV){ + + incoming = glm::normalize(incoming - 2.0f*nDotV*normal); - ray r = raycastFromCameraKernel(resolution, time, x, y, cam.position, cam.view, cam.up, cam.fov); - - if((x<=resolution.x && y<=resolution.y)){ - - float MAX_DEPTH = 100000000000000000; - float depth = MAX_DEPTH; - - for(int i=0; i-EPSILON){ - MAX_DEPTH = depth; - colors[index] = materials[geoms[i].materialid].color; - } - } +} + +//refraction +__host__ __device__ void refract(glm::vec3 & incoming, glm::vec3 normal, float n1, float n2, bool fresnel, int index, int time){ + + float n = n1/n2; + float c1 = glm::dot(-incoming, normal); //cos of angle between normal and incident ray + float c2 = 1.0f - n*n*(1.0f - c1*c1); + + //total internal reflection + if(c2 < 0){ + //incoming = glm::normalize(incoming - 2.0f*glm::dot(incoming, normal)*normal); + //why is this working??? + reflect(incoming, normal, c1); + return; + } + //schlick's approximation + float r0 = (n1-n2)*(n1-n2)/(n1+n2)/(n1+n2); + float rTheta = r0 + (1.0f-r0)*pow((1.0f-c1), 5); + thrust::default_random_engine rng(hash(index*time)); + thrust::uniform_real_distribution u01(0,1); + + if(!fresnel || (float)u01(rng) > rTheta) + incoming = glm::normalize( n*incoming + (n*c1-sqrt(c2))*normal); + else + reflect(incoming, normal, -c1); - //colors[index] = generateRandomNumberFromThread(resolution, time, x, y); - } } +//Does intersection on all of the objects and returns length of closest intersection +__host__ __device__ float testGeomIntersection(staticGeom* geoms, int numberOfGeoms, ray& r, glm::vec3& intersectionPoint, glm::vec3& normal, int& objID){ + + float len = FLT_MAX; + float tempLen = -1; + glm::vec3 tempIntersection; + glm::vec3 tempNormal; + + //check for interesction + for(int geomInd = 0; geomInd= d){ + compactOut[index] = compactIn[index - d] + valIn; + } + else{ + compactOut[index] = valIn; + } + } + + //__syncthreads(); +} + +__global__ void buildRayPool(int* compactIn, rayBounce* rayTempPass, rayBounce* rayPass, int numRays){ + + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + + if(index < numRays){ + if(!rayTempPass[index].dead){ + rayPass[compactIn[index] - 1] = rayTempPass[index]; + } + } +} + +//pixel parallel pathtrace +__global__ void pathTrace(glm::vec2 resolution, cameraData cam, int maxDepth, int time, staticGeom* geoms, int numberOfGeoms, material* materials, + int numLights, int* lightID, glm::vec3* colors){ + + int x = (blockIdx.x * blockDim.x) + threadIdx.x; + int y = (blockIdx.y * blockDim.y) + threadIdx.y; + int index = x + (y * resolution.x); + + glm::vec3 intersection; + glm::vec3 normal; + glm::vec3 surfColor; + + int currDepth = 1; + + if((x<=resolution.x && y<=resolution.y)){ + + ray firstRay = raycastFromCameraKernel(resolution, x, y, cam.position, cam.view, cam.up, cam.fov); + glm::vec3 finalColor(1,1,1); + + while(currDepth <= maxDepth){ + + //do intersection test + int objID = -1; + float len = testGeomIntersection(geoms, numberOfGeoms, firstRay, intersection, normal, objID); + + //if no intersection, return + if(objID == -1){ + colors[index] += glm::vec3(0); //set to black + return; + } + + int matID = geoms[objID].materialid; + material firstMat = materials[matID]; + + //check if material is light + if(materials[matID].emittance > 0){ + colors[index] += finalColor * firstMat.color * firstMat.emittance; + return; + } + + float nDotv = glm::dot(firstRay.direction, normal); + if(materials[matID].hasReflective == 1){ + firstRay.direction = glm::normalize(firstRay.direction - 2.0f*glm::dot(firstRay.direction, normal)*normal); + } + + //pure refraction + else if(firstMat.hasRefractive == 1 && firstMat.hasReflective == 0){ + float n1 = 1.0f; + float n2 = firstMat.indexOfRefraction; + + //check if ray is inside the object + if(nDotv > 0){ + n1 = n2; + n2 = 1.0f; + normal *= -1.0f; //flip normal + } + + float n = n1/n2; + float c1 = glm::dot(-firstRay.direction, normal); + float c2 = 1.0f - n*n *(1.0f- c1*c1); + + if(c2 < 0) + firstRay.direction = glm::normalize(firstRay.direction - 2.0f*c1*normal); + else{ + firstRay.direction = glm::normalize( n*firstRay.direction + (n*c1-sqrt(c2))*normal); + } + } + else if(firstMat.hasRefractive == 1 && firstMat.hasReflective == 1){ + float n1 = 1.0f; + float n2 = firstMat.indexOfRefraction; + + //check if ray is inside the object + if(nDotv > 0){ + n1 = n2; + n2 = 1.0f; + normal *= -1.0f; //flip normal + } + + float n = n1/n2; + float c1 = glm::dot(-firstRay.direction, normal); + float c2 = 1.0f - n*n *(1.0f- c1*c1); + + if(c2 < 0) + firstRay.direction = glm::normalize(firstRay.direction + 2.0f*c1*normal); + else{ + //schlick's approximation + float r0 = pow((n1-n2)/(n1+n2), 2); + float rTheta = r0 +(1-r0)*pow((1.0f-c1), 5); + + thrust::default_random_engine rng(hash(index*time)); + thrust::uniform_real_distribution u01(0,1); + + if((float)u01(rng) > rTheta) + firstRay.direction = glm::normalize( n*firstRay.direction + (n*c1-sqrt(c2))*normal); + else + firstRay.direction = glm::normalize(firstRay.direction - 2.0f*glm::dot(firstRay.direction, normal)*normal); + } + } + else{ + //calculate diffuse direction + glm::vec3 seed = generateRandomNumberFromThread(resolution, time+1, x, y); + + if(time % 2 ==0) + firstRay.direction = calculateRandomDirectionInHemisphere(normal, seed.x, seed.y); + else + firstRay.direction = calculateRandomDirectionInHemisphere(normal, seed.y, seed.z); + } + + currDepth++; + + //offset a little to prevent self intersection + firstRay.origin = intersection + 0.001f * firstRay.direction; + + //store the color + finalColor *= firstMat.color; + } -//TODO: FINISH THIS FUNCTION -// Wrapper for the __global__ call that sets up the kernel calls and does a ton of memory management -void cudaRaytraceCore(uchar4* PBOpos, camera* renderCam, int frame, int iterations, material* materials, int numberOfMaterials, geom* geoms, int numberOfGeoms){ - - int traceDepth = 1; //determines how many bounces the raytracer traces - // set up crucial magic - int tileSize = 8; - dim3 threadsPerBlock(tileSize, tileSize); - dim3 fullBlocksPerGrid((int)ceil(float(renderCam->resolution.x)/float(tileSize)), (int)ceil(float(renderCam->resolution.y)/float(tileSize))); + } + +} + +//creates and stores first bounce rays, always at depth 1 +__global__ void createRay(glm::vec2 resolution, cameraData cam, int maxDepth, int time, staticGeom* geoms, int numberOfGeoms, material* materials, + int numLights, int* lightID, rayBounce* firstPass, int* compactIn, int numRays, glm::vec3* colors){ + + int x = (blockIdx.x * blockDim.x) + threadIdx.x; + int y = (blockIdx.y * blockDim.y) + threadIdx.y; + int index = x + (y * resolution.x); + + glm::vec3 intersection; + glm::vec3 normal; + + if(index < numRays){ + + ray firstRay = raycastFromCameraKernel(resolution, x, y, cam.position, cam.view, cam.up, cam.fov); + + //DOF and antialiasing setup + float focalLength = cam.focalLength; + float aperture = cam.aperture; + + glm::vec3 focalPoint = firstRay.origin + focalLength * firstRay.direction; + + //jitter camera + glm::vec3 jitterVal = 2.0f * aperture * generateRandomNumberFromThread(resolution, time, x, y); + jitterVal -= glm::vec3(aperture); + firstRay.origin += jitterVal; + + //find new direction + firstRay.direction = glm::normalize(focalPoint - firstRay.origin); + + //antialias sample per pixel + jitterVal = generateRandomNumberFromThread(resolution, time, x, y); + jitterVal -= glm::vec3(0.5f, 0.5f, 0.5f); + firstRay.direction += 0.0015f* jitterVal; + + //start building the first pool of rays + rayBounce firstBounce = rayBounce(); + firstBounce.pixID = index; + firstBounce.thisRay = firstRay; + firstBounce.currDepth = 1; + firstBounce.color = glm::vec3(1); + + //set all rays to alive + compactIn[index] = 1; + + //save first bounce + firstPass[index] = firstBounce; + } + +} + + +__global__ void rayParallelTrace(glm::vec2 resolution, int time, cameraData cam, int maxDepth, glm::vec3* colors, staticGeom* geoms, int numberOfGeoms, + material* materials, int numLights, int* lightID, int numRays, int* compactIn, rayBounce* rayPass){ + + //find the index in 1D + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + + if(index < numRays){ + + //get the bounce at this index + rayBounce currBounce = rayPass[index]; + currBounce.currDepth++; + + //get the ray information at this index + ray currRay = currBounce.thisRay; + glm::vec3 intersection; + glm::vec3 normal; + + //do intersection test + int objID = -1; + float len = testGeomIntersection(geoms, numberOfGeoms, currRay, intersection, normal, objID); + + //if no intersection, return + if(objID == -1){ + colors[currBounce.pixID] += glm::vec3(0,0,0); + currBounce.dead = true; + rayPass[index] = currBounce; + compactIn[index] = 0; + return; + } + + int matID = geoms[objID].materialid; + material currMat = materials[matID]; + + //check if material is light + if(currMat.emittance > 0){ + colors[currBounce.pixID] += currBounce.color * currMat.color * currMat.emittance; + currBounce.dead = true; + rayPass[index] = currBounce; + compactIn[index] = 0; + return; + } + + float nDotv = glm::dot(normal, currRay.direction); + //basic reflection + if(currMat.hasReflective == 1 && currMat.hasRefractive == 0){ + reflect(currRay.direction, normal, nDotv); + } + //refraction + else if(currMat.hasRefractive == 1){ + float n1 = 1.0f; + float n2 = currMat.indexOfRefraction; + + //check if ray is inside the object + if(nDotv > 0){ + n1 = n2; + n2 = 1.0f; + normal *= -1.0f; //flip normal + } + + //refract without using fresnel + if(currMat.hasReflective == 0) + refract(currRay.direction, normal, n1, n2, false, index, time); + else + //reract with fresnel flag = true + refract(currRay.direction, normal, n1, n2, true, index, time); + } + else{ + //find new direction + glm::vec3 seed = generateRandomNumberFromThread(resolution, time, 0.2f*index*currBounce.currDepth+1, threadIdx.x); + if(time % 2 ==0) + currRay.direction = calculateRandomDirectionInHemisphere(normal, seed.x, seed.y); + else + currRay.direction = calculateRandomDirectionInHemisphere(normal, seed.y, seed.z); + } + + currBounce.dead = false; + compactIn[index] = 1; + currBounce.color *= currMat.color; + currBounce.thisRay.direction = currRay.direction; + currBounce.thisRay.origin = intersection + 0.001f * currRay.direction; + + rayPass[index] = currBounce; + } + +} + + +//allocate memory on cuda +void cudaAllocMemory(camera* renderCam, material* materials, int numberOfMaterials, geom* geoms, int numberOfGeoms){ + + int size = (int)renderCam->resolution.x*(int)renderCam->resolution.y; + + cudaRayPool = NULL; + cudaMalloc((void**)&cudaRayPool, size*sizeof(rayBounce)); + + cudaTempRayPool = NULL; + cudaMalloc((void**)&cudaTempRayPool, size*sizeof(rayBounce)); + + cudaCompactA= NULL; + cudaMalloc((void**)&cudaCompactA, size*sizeof(int)); + + cudaCompactB= NULL; + cudaMalloc((void**)&cudaCompactB, size*sizeof(int)); - //send image to GPU - glm::vec3* cudaimage = NULL; - cudaMalloc((void**)&cudaimage, (int)renderCam->resolution.x*(int)renderCam->resolution.y*sizeof(glm::vec3)); - cudaMemcpy( cudaimage, renderCam->image, (int)renderCam->resolution.x*(int)renderCam->resolution.y*sizeof(glm::vec3), cudaMemcpyHostToDevice); + //send image to GPU + cudaimage = NULL; + cudaMalloc((void**)&cudaimage, (int)renderCam->resolution.x*(int)renderCam->resolution.y*sizeof(glm::vec3)); + cudaMemcpy( cudaimage, renderCam->image, (int)renderCam->resolution.x*(int)renderCam->resolution.y*sizeof(glm::vec3), cudaMemcpyHostToDevice); + + //package geometry and materials and sent to GPU + staticGeom* geomList = new staticGeom[numberOfGeoms]; + std::vector lightVec; + + //get geom from frame 0 + for(int i=0; i 0) + lightVec.push_back(i); + } + + cudageoms = NULL; + cudaMalloc((void**)&cudageoms, numberOfGeoms*sizeof(staticGeom)); + cudaMemcpy( cudageoms, geomList, numberOfGeoms*sizeof(staticGeom), cudaMemcpyHostToDevice); + + //copy materials to memory + cudaMaterials = NULL; + cudaMalloc((void**)&cudaMaterials, numberOfMaterials*sizeof(material)); + cudaMemcpy( cudaMaterials, materials, numberOfMaterials*sizeof(material), cudaMemcpyHostToDevice); + + //copy light ID to memeory + numLights = lightVec.size(); + + int* lightID = new int[numLights]; + for(int i = 0; i resolution; - cam.position = renderCam->positions[frame]; - cam.view = renderCam->views[frame]; - cam.up = renderCam->ups[frame]; - cam.fov = renderCam->fov; - - //kernel launches - for(int bounce = 1; bounce <= 1; ++bounce) - { - raytraceRay<<>>(renderCam->resolution, (float)iterations, (float)bounce, cam, traceDepth, cudaimage, cudageoms, numberOfGeoms, cudamaterials, numberOfMaterials); - } - sendImageToPBO<<>>(PBOpos, renderCam->resolution, cudaimage); + int traceDepth = 20; //determines how many bounces the raytracer traces + + // set up crucial magic + int tileSize = 8; + dim3 threadsPerBlock(tileSize, tileSize); //each block has 8 * 8 threads + dim3 fullBlocksPerGrid((int)ceil(float(renderCam->resolution.x)/float(tileSize)), (int)ceil(float(renderCam->resolution.y)/float(tileSize))); + + //package camera + cameraData cam; + cam.resolution = renderCam->resolution; + cam.position = renderCam->positions[frame]; + cam.view = renderCam->views[frame]; + cam.up = renderCam->ups[frame]; + cam.fov = renderCam->fov; + cam.focalLength = renderCam->focalLengths[frame]; + cam.aperture = renderCam->apertures[frame]; + + //start parallel raytrace + int imageSize = (int)renderCam->resolution.x * (int)renderCam->resolution.y; + int numRays = imageSize; + int tileSquare = tileSize * tileSize; + + dim3 threadsPerBlockRayPool(tileSquare); //each block has 8 * 8 threads + dim3 fullBlocksPerGridRayPool((int)ceil(float(numRays) / tileSquare)); //numRays/tilesquare blocks, decreases as there are less rays + + //clear image if camera has been moved + if(clear){ + clearImage<<>>(renderCam->resolution, cudaimage); + clear = false; + } + else{ + //pathTrace<<>>(cam.resolution, cam, traceDepth, iterations, cudageoms, numberOfGeoms, cudaMaterials, + // numLights, cudaLights, cudaimage); + //build rays for the first iteration + createRay<<>>(cam.resolution, cam, traceDepth, iterations, cudageoms, numberOfGeoms, cudaMaterials, + numLights, cudaLights, cudaRayPool, cudaCompactA, numRays, cudaimage); - //retrieve image from GPU - cudaMemcpy( renderCam->image, cudaimage, (int)renderCam->resolution.x*(int)renderCam->resolution.y*sizeof(glm::vec3), cudaMemcpyDeviceToHost); + //stream compact the temporary ray pool, then copy the result to the current ray pool + for(int currDepth = 1; currDepth <= traceDepth; ++currDepth){ + //do stream compaction using double buffers compact A and B, in 1D + cudaStreamCompaction(fullBlocksPerGridRayPool, threadsPerBlockRayPool, imageSize, traceDepth, numRays); + + if(numRays == 0) + break; - //free up stuff, or else we'll leak memory like a madman - cudaFree( cudaimage ); - cudaFree( cudageoms ); - cudaFree( cudamaterials ); - delete [] geomList; + //update the number of blocks you need + fullBlocksPerGridRayPool = dim3((int)ceil(float(numRays) / tileSquare)); - // make certain the kernel has completed - cudaThreadSynchronize(); + //raytrace with new number of blocks and rays + rayParallelTrace<<>>(cam.resolution, iterations, cam, traceDepth, cudaimage, cudageoms, numberOfGeoms, + cudaMaterials, numLights, cudaLights, numRays, cudaCompactA, cudaRayPool); + } + } + + sendImageToPBO<<>>(PBOpos, renderCam->resolution, cudaimage, (float)iterations); + + //retrieve image from GPU + cudaMemcpy( renderCam->image, cudaimage, imageSize*sizeof(glm::vec3), cudaMemcpyDeviceToHost); + + // make certain the kernel has completed + cudaThreadSynchronize(); + + checkCUDAError("Kernel failed!"); +} + + +void cudaStreamCompaction(dim3 fullBlocksPerGridRayPool, dim3 threadsPerBlockRayPool, int imageSize, int traceDepth, int& numRays){ + + int compactDepth = (int)ceil(log((float)numRays)/log(2.0f)); //total number of times you have to compact to get the final sums + int currCompactDepth = 0; + + for( int d = 1; d <= compactDepth; ++d){ + currCompactDepth = pow(2.0f, d-1); + + streamCompact<<>>(numRays, cudaCompactA, cudaCompactB, currCompactDepth); + int* tempCompact = cudaCompactA; + cudaCompactA = cudaCompactB; + cudaCompactB = tempCompact; + } + + checkCUDAError("compact failed!"); + + int newNumRays = 0; + + //swap pointers to the ray pool so you alwyas trace with cudaRayPool + rayBounce* tempRays = cudaRayPool; + cudaRayPool = cudaTempRayPool; + cudaTempRayPool = tempRays; + + cudaMemcpy(&newNumRays, &cudaCompactA[numRays-1], sizeof(int), cudaMemcpyDeviceToHost); + buildRayPool<<>>(cudaCompactA, cudaTempRayPool, cudaRayPool, numRays); + + //update the number of rays only after you have shifted and built the new pool + numRays = newNumRays; + //std::cout< #endif -void cudaRaytraceCore(uchar4* pos, camera* renderCam, int frame, int iterations, material* materials, int numberOfMaterials, geom* geoms, int numberOfGeoms); +//for first bounce +//void cudaAllocMemory(camera* renderCam, int frame, material* materials, int numberOfMaterials, geom* geoms, int numberOfGeoms); +void cudaAllocMemory(camera* renderCam, material* materials, int numberOfMaterials, geom* geoms, int numberOfGeoms); + +void cudaFreeMemory(); + +void cudaRaytraceCore(uchar4* pos, camera* renderCam, int frame, int iterations, material* materials, int numberOfMaterials, geom* geoms, int numberOfGeoms, bool& clear); + +void cudaStreamCompaction(dim3 rayPoolBlocks, dim3 rayPoolThreads, int imageSize, int tracDepth, int& numRays); #endif diff --git a/src/scene.cpp b/src/scene.cpp index 415d627..6e26598 100755 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -7,6 +7,7 @@ #include #include "scene.h" #include +#include "meshLoader.h" scene::scene(string filename){ cout << "Reading scene from " << filename << " ..." << endl; @@ -36,6 +37,10 @@ scene::scene(string filename){ int scene::loadObject(string objectid){ int id = atoi(objectid.c_str()); + + //for obj loading + meshLoader loader; + if(id!=objects.size()){ cout << "ERROR: OBJECT ID does not match expected number of objects" << endl; return -1; @@ -55,6 +60,7 @@ int scene::loadObject(string objectid){ newObject.type = CUBE; }else{ string objline = line; + std::cout< positions; vector views; vector ups; + vector focalLengths; + vector apertures; while (!line.empty() && fp_in.good()){ //check frame number @@ -172,7 +212,7 @@ int scene::loadCamera(){ } //load camera properties - for(int i=0; i<3; i++){ + for(int i=0; i<5; i++){ //glm::vec3 translation; glm::vec3 rotation; glm::vec3 scale; utilityCore::safeGetline(fp_in,line); tokens = utilityCore::tokenizeString(line); @@ -183,6 +223,13 @@ int scene::loadCamera(){ }else if(strcmp(tokens[0].c_str(), "UP")==0){ ups.push_back(glm::vec3(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()))); } + else if(strcmp(tokens[0].c_str(), "FOCAL") ==0){ + focalLengths.push_back(atof(tokens[1].c_str())); + } + else if(strcmp(tokens[0].c_str(), "APERTURE") ==0){ + apertures.push_back(atof(tokens[1].c_str())); + } + } frameCount++; @@ -194,10 +241,15 @@ int scene::loadCamera(){ newCamera.positions = new glm::vec3[frameCount]; newCamera.views = new glm::vec3[frameCount]; newCamera.ups = new glm::vec3[frameCount]; + newCamera.focalLengths = new float[frameCount]; + newCamera.apertures = new float[frameCount]; + for(int i=0; i tokens = utilityCore::tokenizeString(line); if(strcmp(tokens[0].c_str(), "RGB")==0){ glm::vec3 color( atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()) ); + if(color.x > 1) + color.x /=255.0f; + if(color.y > 1) + color.y /=255.0f; + if(color.z > 1) + color.z /=255.0f; newMaterial.color = color; }else if(strcmp(tokens[0].c_str(), "SPECEX")==0){ newMaterial.specularExponent = atof(tokens[1].c_str()); diff --git a/src/sceneStructs.h b/src/sceneStructs.h index b10f1cf..8278091 100755 --- a/src/sceneStructs.h +++ b/src/sceneStructs.h @@ -18,10 +18,20 @@ struct ray { glm::vec3 direction; }; +struct mesh{ + glm::vec3* faces; + glm::vec3* verts; + glm::vec3* normals; + + int numVerts; + int numFaces; +}; + struct geom { enum GEOMTYPE type; int materialid; int frames; + mesh objMesh; glm::vec3* translations; glm::vec3* rotations; glm::vec3* scales; @@ -32,6 +42,7 @@ struct geom { struct staticGeom { enum GEOMTYPE type; int materialid; + mesh objMesh; glm::vec3 translation; glm::vec3 rotation; glm::vec3 scale; @@ -45,6 +56,8 @@ struct cameraData { glm::vec3 view; glm::vec3 up; glm::vec2 fov; + float focalLength; + float aperture; }; struct camera { @@ -58,6 +71,9 @@ struct camera { glm::vec3* image; ray* rayList; std::string imageName; + + float* focalLengths; + float* apertures; }; struct material{ @@ -73,4 +89,14 @@ struct material{ float emittance; }; +//for caching first bounce +struct rayBounce{ + ray thisRay; + int pixID; //pixel id of ray + int currDepth; + bool dead; + glm::vec3 color; //current color of the ray, use to attenuate + +}; + #endif //CUDASTRUCTS_H diff --git a/src/utilities.h b/src/utilities.h index 84ec55f..e23ba1f 100755 --- a/src/utilities.h +++ b/src/utilities.h @@ -20,7 +20,7 @@ #define PI 3.1415926535897932384626422832795028841971 #define TWO_PI 6.2831853071795864769252867665590057683943 #define SQRT_OF_ONE_THIRD 0.5773502691896257645091487805019574556476 -#define NATURAL_E 2.7182818284590452353602874713526624977572 +#define E 2.7182818284590452353602874713526624977572 #define EPSILON .000000001 #define ZERO_ABSORPTION_EPSILON 0.00001 #define RAY_BIAS_AMOUNT 0.0002