diff --git a/DOF.0.bmp b/DOF.0.bmp new file mode 100644 index 0000000..15f512e Binary files /dev/null and b/DOF.0.bmp differ diff --git a/Obj.0.bmp b/Obj.0.bmp new file mode 100644 index 0000000..bd32e8e Binary files /dev/null and b/Obj.0.bmp differ diff --git a/Obj1.bmp b/Obj1.bmp new file mode 100644 index 0000000..d96ade8 Binary files /dev/null and b/Obj1.bmp differ diff --git a/README.md b/README.md index ae6088d..7ea4c7b 100644 --- a/README.md +++ b/README.md @@ -1,168 +1,98 @@ -CIS 565 Project3 : CUDA Pathtracer -=================== - -Fall 2014 - -Due Wed, 10/8 (submit without penalty until Sun, 10/12) - -## INTRODUCTION -In this project, you will implement a CUDA based pathtracer capable of -generating pathtraced rendered images extremely quickly. Building a pathtracer can be viewed as a generalization of building a raytracer, so for those of you who have taken 460/560, the basic concept should not be very new to you. For those of you that have not taken -CIS460/560, raytracing is a technique for generating images by tracing rays of -light through pixels in an image plane out into a scene and following the way -the rays of light bounce and interact with objects in the scene. More -information can be found here: -http://en.wikipedia.org/wiki/Ray_tracing_(graphics). Pathtracing is a generalization of this technique by considering more than just the contribution of direct lighting to a surface. - -Since in this class we are concerned with working in generating actual images -and less so with mundane tasks like file I/O, this project includes basecode -for loading a scene description file format, described below, and various other -things that generally make up the render "harness" that takes care of -everything up to the rendering itself. The core renderer is left for you to -implement. Finally, note that while this basecode is meant to serve as a -strong starting point for a CUDA pathtracer, you are not required to use this -basecode if you wish, and you may also change any part of the basecode -specification as you please, so long as the final rendered result is correct. - -## CONTENTS -The Project3 root directory contains the following subdirectories: - -* src/ contains the source code for the project. Both the Windows Visual Studio - solution and the OSX and Linux makefiles reference this folder for all - source; the base source code compiles on Linux, OSX and Windows without - modification. If you are building on OSX, be sure to uncomment lines 4 & 5 of - the CMakeLists.txt in order to make sure CMake builds against clang. -* data/scenes/ contains an example scene description file. -* renders/ contains an example render of the given example scene file. -* windows/ contains a Windows Visual Studio 2010 project and all dependencies - needed for building and running on Windows 7. If you would like to create a - Visual Studio 2012 or 2013 projects, there are static libraries that you can - use for GLFW that are in external/bin/GLFW (Visual Studio 2012 uses msvc110, - and Visual Studio 2013 uses msvc120) -* external/ contains all the header, static libraries and built binaries for - 3rd party libraries (i.e. glm, GLEW, GLFW) that we use for windowing and OpenGL - extensions - -## RUNNING THE CODE -The main function requires a scene description file (that is provided in data/scenes). -The main function reads in the scene file by an argument as such : -'scene=[sceneFileName]' - -If you are using Visual Studio, you can set this in the Debugging > Command Arguments section -in the Project properties. - -## REQUIREMENTS -In this project, you are given code for: - -* Loading, reading, and storing the scene scene description format -* Example functions that can run on both the CPU and GPU for generating random - numbers, spherical intersection testing, and surface point sampling on cubes -* A class for handling image operations and saving images -* Working code for CUDA-GL interop - -You will need to implement the following features: - -* Raycasting from a camera into a scene through a pixel grid -* Diffuse surfaces -* Perfect specular reflective surfaces -* Cube intersection testing -* Sphere surface point sampling -* Stream compaction optimization - -You are also required to implement at least 2 of the following features: - -* Texture mapping -* Bump mapping -* Depth of field -* Refraction, i.e. glass -* OBJ Mesh loading and rendering -* Interactive camera -* Motion blur -* Subsurface scattering - -The 'extra features' list is not comprehensive. If you have a particular feature -you would like to implement (e.g. acceleration structures, etc.) please contact us -first! - -For each 'extra feature' you must provide the following analysis : -* overview write up of the feature -* performance impact of the feature -* if you did something to accelerate the feature, why did you do what you did -* compare your GPU version to a CPU version of this feature (you do NOT need to - implement a CPU version) -* how can this feature be further optimized (again, not necessary to implement it, but - should give a roadmap of how to further optimize and why you believe this is the next - step) - -## BASE CODE TOUR -You will be working in three files: raytraceKernel.cu, intersections.h, and -interactions.h. Within these files, areas that you need to complete are marked -with a TODO comment. Areas that are useful to and serve as hints for optional -features are marked with TODO (Optional). Functions that are useful for -reference are marked with the comment LOOK. - -* raytraceKernel.cu contains the core raytracing CUDA kernel. You will need to - complete: - * cudaRaytraceCore() handles kernel launches and memory management; this - function already contains example code for launching kernels, - transferring geometry and cameras from the host to the device, and transferring - image buffers from the host to the device and back. You will have to complete - this function to support passing materials and lights to CUDA. - * raycastFromCameraKernel() is a function that you need to implement. This - function once correctly implemented should handle camera raycasting. - * raytraceRay() is the core raytracing CUDA kernel; all of your pathtracing - logic should be implemented in this CUDA kernel. raytraceRay() should - take in a camera, image buffer, geometry, materials, and lights, and should - trace a ray through the scene and write the resultant color to a pixel in the - image buffer. - -* intersections.h contains functions for geometry intersection testing and - point generation. You will need to complete: - * boxIntersectionTest(), which takes in a box and a ray and performs an - intersection test. This function should work in the same way as - sphereIntersectionTest(). - * getRandomPointOnSphere(), which takes in a sphere and returns a random - point on the surface of the sphere with an even probability distribution. - This function should work in the same way as getRandomPointOnCube(). You can - (although do not necessarily have to) use this to generate points on a sphere - to use a point lights, or can use this for area lighting. - -* interactions.h contains functions for ray-object interactions that define how - rays behave upon hitting materials and objects. You will need to complete: - * getRandomDirectionInSphere(), which generates a random direction in a - sphere with a uniform probability. This function works in a fashion - similar to that of calculateRandomDirectionInHemisphere(), which generates a - random cosine-weighted direction in a hemisphere. - * calculateBSDF(), which takes in an incoming ray, normal, material, and - other information, and returns an outgoing ray. You can either implement - this function for ray-surface interactions, or you can replace it with your own - function(s). - -You will also want to familiarize yourself with: - -* sceneStructs.h, which contains definitions for how geometry, materials, - lights, cameras, and animation frames are stored in the renderer. -* utilities.h, which serves as a kitchen-sink of useful functions - -## 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. - -## SCENE FORMAT -This project uses a custom scene description format. -Scene files are flat text files that describe all geometry, materials, -lights, cameras, render settings, and animation frames inside of the scene. -Items in the format are delimited by new lines, and comments can be added at -the end of each line preceded with a double-slash. +##GPU accelerated Pathtracer + +This is a standalone pathtracer implemented on the GPU using CUDA and OpenGL. +###Feature Highlights: +**Diffuse,Reflection, Refraction** + +**Global Illumination,Soft Shadow, Caustics, Color Bleeding** + +**Depth of Field** + +**Subsurface scattering** + +**Fresnel Coefficients for reflection/refraction** + +**Polygon Mesh Render** + +###Results (all 12,000 iterations): +![](std1.bmp) + +**Subsurface Scattering** +![](SSS.bmp) + +**Depth of Field** +![](DOF.0.bmp) + +**Polygon Mesh Support** +![](Obj1.bmp) + + +###Performance Features: + +**Ray Parallel** + +Threads run on rays rather than pixels. For each pathtrace kernel call, all rays are in the same depth, each thread traces one ray one more depth. The advantage is that for rendering +that requires very high depth, we could use stream compaction to remove "dead" rays during each kernel call to boost performance. + +**Stream Compaction of Rays** + +Used thrust::copy_if to do the compaction. Double buffer swapping is used to avoid copy data twice. + + +###Feature Implementation Explained: + +**softshadow,area light,color bleeding,global illumination** + +These features come "included" from the core pathtracing algorithm. For each ray, if it hits any surface it will bounce according to the BSDF funtion if returns a color when it hits light or doesn't +intersect any object or max depth is reached. + +**Diffuse,Reflection, Refraction, caustics ("free")** + +For diffuse surface, cosine weighted random Hemisphere sample is used to determine scattered direction. +Reflection and refraction uses Fresnel coefficients to determine proportion of reflected and refracted ray. + Assuming light unpolarized + + ![](fresnel1.bmp) + + use 1/2 (RS + RP) to get coefficient for reflection + +**Depth of Field** + +This is achieved by giving eye position an aperture instead of a single point and set image plane at focal length. Camera shoots out random rays within aperture of the eye position. + +**Subsurface Scattering** +It is a depth based subsurface scattering approximation. Rays hitting the surface will compute both entry and exit point on the object, then keep tracing from the exit point and the returned color +will be discounted by the distance between entry and exit point by an exponential function. Advantages of this implementation is: little performance impact, and good result of back lit situation. +Weakness: not physically accurate, and less convincing in front lit situation. + +**Polygon Mesh Rendering** +Implemented triangle intersection test function. Uses TinyObjLoader to read Obj file. + +**Super-Sampling Anti-Alisasing** +Jittered pixel position while shooting camera rays. + +###Performance Analysis +The data is produced from a scene in a cornell box and with three spheres, a pure diffuse, a reflective and a refractive one. Resolution was 1024 x 2014. +![](SCanalysis.bmp) + +From the graph we can see when depth is greater than 10, stream compaction begins to be in advantage. The higher the depth the more advantage stream compaction has. +This is probably because when depth is low, overhead and data copying time in stream compaction exceeds the time saved. In addition, I expect stream compaction to have even greater +advantage when the scene is more sparse, i.e. lots of camera rays do not hit anything. + +###Debug Images +Debug Intersection of objects and material association. +![](intersection.bmp) + +Debug surface normals of objects +![](debugNormal.bmp) + +Debug depth of the scene (for debugging depth of field and subsurface scattering) +![](depth.bmp) + + +###Scene Format +This project uses a custom scene description format. See data/scene/.. for example scenes. +If camera resolution is changed, width and height in init() function in main.cpp needs to be changed as well to properly initiate GUI window. Materials are defined in the following fashion: @@ -195,6 +125,8 @@ Cameras are defined in the following fashion: * ITERATIONS (float interations) //how many iterations to refine the image, only relevant for supersampled antialiasing, depth of field, area lights, and other distributed raytracing applications +* FOCALLEN (float distance) //how far is eye position to image plane (for depth of field effect) +* APER (float radius) //radius of the size of lens (for depth of field effect) * FILE (string filename) //file to output render to upon completion * frame (frame number) //start of a frame @@ -216,58 +148,3 @@ Objects are defined in the following fashion: * ROTAT (float rotationx) (float rotationy) (float rotationz) //rotation * SCALE (float scalex) (float scaley) (float scalez) //scale -An example scene file setting up two frames inside of a Cornell Box can be -found in the scenes/ directory. - -For meshes, note that the base code will only read in .obj files. For more -information on the .obj specification see http://en.wikipedia.org/wiki/Wavefront_.obj_file. - -An example of a mesh object is as follows: - -OBJECT 0 -mesh tetra.obj -material 0 -frame 0 -TRANS 0 5 -5 -ROTAT 0 90 0 -SCALE .01 10 10 - -Check the Google group for some sample .obj files of varying complexity. - -## THIRD PARTY CODE POLICY -* Use of any third-party code must be approved by asking on our 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. - -## SELF-GRADING -* On the submission date, email your grade, on a scale of 0 to 100, to Harmony, - harmoli+cis565@seas.upenn.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. - -## SUBMISSION -Please change the README to reflect the answers to the questions we have posed -above. Remember: -* this is a renderer, so include images that you've made! -* be sure to back your claims for optimization with numbers and comparisons -* if you reference any other material, please provide a link to it -* you wil not e graded on how fast your path tracer runs, but getting close to - real-time is always nice -* if you have a fast GPU renderer, it is good to show case this with a video to - show interactivity. If you do so, please include a link. - -Be sure to open a pull request and to send Harmony your grade and why you -believe this is the grade you should get. diff --git a/SCanalysis.bmp b/SCanalysis.bmp new file mode 100644 index 0000000..1e87cfd Binary files /dev/null and b/SCanalysis.bmp differ diff --git a/SSS.bmp b/SSS.bmp new file mode 100644 index 0000000..2aa284f Binary files /dev/null and b/SSS.bmp differ diff --git a/SSS1.bmp b/SSS1.bmp new file mode 100644 index 0000000..df90793 Binary files /dev/null and b/SSS1.bmp differ diff --git a/SSS2.bmp b/SSS2.bmp new file mode 100644 index 0000000..142b177 Binary files /dev/null and b/SSS2.bmp differ diff --git a/Thumbs.db b/Thumbs.db new file mode 100644 index 0000000..91cddc9 Binary files /dev/null and b/Thumbs.db differ diff --git a/data/scenes/sampleScene2.txt b/data/scenes/sampleScene2.txt new file mode 100644 index 0000000..c4e7113 --- /dev/null +++ b/data/scenes/sampleScene2.txt @@ -0,0 +1,200 @@ +MATERIAL 0 //white diffuse +RGB 1 1 1 +DIFFCOE 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 +DIFFCOE 1 +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 .15 .48 .09 +DIFFCOE 1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 3 //blue glossy +RGB 0.1 0.2 0.65 +DIFFCOE 1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 4 //glossy glass +RGB 0.9 0.75 0.9 +DIFFCOE 0.0 +SPECEX 0 +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 1 1 1 +DIFFCOE 0.1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 2.2 +SCATTER 0 +ABSCOEFF .02 5.1 5.7 +RSCTCOEFF 13 +EMITTANCE 0 + +MATERIAL 6 //yellow +RGB 0.8 0.7 0.2 +DIFFCOE 1 +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 //light +RGB 0.6 0.4 0.7 +DIFFCOE 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 8 //light +RGB 1 1 1 +DIFFCOE 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 40 + +CAMERA +RES 1024 1024 +FOVY 25 +ITERATIONS 12000 +FOCALLEN 12 +APER 0 +FILE std1.bmp +frame 0 +EYE 0 4.5 13 +VIEW 0 0 -1 +UP 0 1 0 + +OBJECT 0 +cube +material 0 +frame 0 +TRANS 0 0 0 +ROTAT 0 0 90 +SCALE 1 10 10 + +OBJECT 1 +cube +material 0 +frame 0 +TRANS 0 5 -5 +ROTAT 0 90 0 +SCALE 1 10 10 + +OBJECT 2 +cube +material 0 +frame 0 +TRANS 0 10 0 +ROTAT 0 0 90 +SCALE 1 10 10 + +OBJECT 3 +cube +material 1 +frame 0 +TRANS -5 5 0 +ROTAT 0 0 0 +SCALE 1 10 10 + +OBJECT 4 +cube +material 2 +frame 0 +TRANS 5 5 0 +ROTAT 0 0 0 +SCALE 1 10 10 + +OBJECT 5 +sphere +material 3 +frame 0 +TRANS 2.75 2 -1 +ROTAT 0 180 0 +SCALE 2 2 2 + +OBJECT 6 +sphere +material 4 +frame 0 +TRANS 1 2.25 2 +ROTAT 0 180 0 +SCALE 2.5 2.5 2.5 + +OBJECT 7 +sphere +material 6 +frame 0 +TRANS -2 3 -2 +ROTAT 0 180 0 +SCALE 2.5 2.5 2.5 + +OBJECT 8 +cube +material 8 +frame 0 +TRANS 0 9.62 0 +ROTAT 0 0 90 +SCALE .3 2 2 \ No newline at end of file diff --git a/data/scenes/sampleSceneDOF.txt b/data/scenes/sampleSceneDOF.txt new file mode 100644 index 0000000..ae31210 --- /dev/null +++ b/data/scenes/sampleSceneDOF.txt @@ -0,0 +1,229 @@ +MATERIAL 0 //white diffuse +RGB 1 1 1 +DIFFCOE 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 +DIFFCOE 1 +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 .15 .48 .09 +DIFFCOE 1 +SPECEX 0 +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 .85 0.9 0.1 +DIFFCOE 0.8 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 4 //glass +RGB 1 1 1 +DIFFCOE 0.05 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 1 +REFRIOR 2.2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 2 +EMITTANCE 0 + +MATERIAL 5 //Translucent +RGB 0.9 0.8 0.7 +DIFFCOE 0.2 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 1 +REFRIOR 2.2 +SCATTER 1 +ABSCOEFF 0 0 0 +RSCTCOEFF 2 +EMITTANCE 0 + +MATERIAL 6 //red d +RGB 0.9 0.2 0.1 +DIFFCOE 0.85 +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 +DIFFCOE 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 +DIFFCOE 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 20 + +MATERIAL 9 //blue diffuse +RGB .1 .15 .63 +DIFFCOE 0.85 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +CAMERA +RES 1024 1024 +FOVY 25 +ITERATIONS 20000 +FOCALLEN 10 +APER 0.19 +FILE DOF.bmp +frame 0 +EYE 0 3 13 +VIEW 0 0 -1 +UP 0 1 0 + +OBJECT 0 +cube +material 0 +frame 0 +TRANS 0 0 0 +ROTAT 0 0 90 +SCALE 1 20 20 + +OBJECT 1 +cube +material 0 +frame 0 +TRANS 0 5 -10 +ROTAT 0 90 0 +SCALE 1 20 20 + +OBJECT 2 +cube +material 0 +frame 0 +TRANS 0 10 0 +ROTAT 0 0 90 +SCALE 1 20 20 + +OBJECT 3 +cube +material 1 +frame 0 +TRANS -8 5 0 +ROTAT 0 0 0 +SCALE 1 20 20 + +OBJECT 4 +cube +material 2 +frame 0 +TRANS 8 5 0 +ROTAT 0 0 0 +SCALE 1 20 20 + +OBJECT 5 +sphere +material 3 +frame 0 +TRANS 0.5 2 3 +ROTAT 5 10 15 +SCALE 2 2 2 + +OBJECT 6 +sphere +material 6 +frame 0 +TRANS 2.75 1.5 6 +ROTAT 0 180 0 +SCALE 2 2 2 + +OBJECT 7 +sphere +material 9 +frame 0 +TRANS -4.25 2 -2.5 +ROTAT 0 180 0 +SCALE 2 2 2 + +OBJECT 8 +cube +material 8 +frame 0 +TRANS 0 9.63 0 +ROTAT 0 0 90 +SCALE .3 3.3 3.3 + +OBJECT 9 +sphere +material 4 +frame 0 +TRANS -1.25 2 0.3 +ROTAT 0 180 0 +SCALE 2 2 2 + +OBJECT 10 +sphere +material 5 +frame 0 +TRANS 3 3.5 -2.5 +ROTAT 0 180 0 +SCALE 2 2 2 \ No newline at end of file diff --git a/data/scenes/sampleSceneObj.txt b/data/scenes/sampleSceneObj.txt new file mode 100644 index 0000000..dd2efe8 --- /dev/null +++ b/data/scenes/sampleSceneObj.txt @@ -0,0 +1,221 @@ +MATERIAL 0 //white diffuse +RGB 1 1 1 +DIFFCOE 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 +DIFFCOE 1 +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 .15 .48 .09 +DIFFCOE 1 +SPECEX 0 +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 .85 0.9 0.1 +DIFFCOE 0.8 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 4 //glass +RGB 0.2 0.3 0.8 +DIFFCOE 0.6 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 2.2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 2 +EMITTANCE 0 + +MATERIAL 5 //Translucent +RGB 0.9 0.8 0.7 +DIFFCOE 0.9 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 2.2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 2 +EMITTANCE 0 + +MATERIAL 6 //red d +RGB 0.9 0.2 0.1 +DIFFCOE 0.85 +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 +DIFFCOE 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 +DIFFCOE 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 20 + +MATERIAL 9 //blue diffuse +RGB .1 .15 .63 +DIFFCOE 0.85 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +CAMERA +RES 1024 1024 +FOVY 25 +ITERATIONS 10000 +FOCALLEN 10 +APER 0 +FILE Obj.bmp +frame 0 +EYE 0 3 13 +VIEW 0 0 -1 +UP 0 1 0 + +OBJECT 0 +cube +material 0 +frame 0 +TRANS 0 0 0 +ROTAT 0 0 90 +SCALE 1 20 20 + +OBJECT 1 +cube +material 0 +frame 0 +TRANS 0 5 -10 +ROTAT 0 90 0 +SCALE 1 20 20 + +OBJECT 2 +cube +material 0 +frame 0 +TRANS 0 10 0 +ROTAT 0 0 90 +SCALE 1 20 20 + +OBJECT 3 +cube +material 1 +frame 0 +TRANS -8 5 0 +ROTAT 0 0 0 +SCALE 1 20 20 + +OBJECT 4 +cube +material 2 +frame 0 +TRANS 8 5 0 +ROTAT 0 0 0 +SCALE 1 20 20 + +OBJECT 5 +C:\Users\Zhen Gou\Documents\Visual Studio 2010\Projects\Project3-Pathtracer\data\mineMine.obj +material 4 +frame 0 +TRANS 0.5 2.5 -1 +ROTAT 0 -15 0 +SCALE 0.8 0.8 0.8 + +OBJECT 6 +sphere +material 6 +frame 0 +TRANS 2.75 1.5 6 +ROTAT 0 180 0 +SCALE 2 2 2 + +OBJECT 7 +cube +material 8 +frame 0 +TRANS 0 9.63 0 +ROTAT 0 0 90 +SCALE .3 3.3 3.3 + +OBJECT 8 +sphere +material 3 +frame 0 +TRANS -2 2 1 +ROTAT 0 180 0 +SCALE 2 2 2 + +OBJECT 9 +sphere +material 5 +frame 0 +TRANS 3.5 3.5 -2.5 +ROTAT 0 180 0 +SCALE 2 2 2 \ No newline at end of file diff --git a/data/scenes/sampleScene.txt b/data/scenes/sampleSceneObj2.txt similarity index 72% rename from data/scenes/sampleScene.txt rename to data/scenes/sampleSceneObj2.txt index 6a9f5cc..ede5d7f 100644 --- a/data/scenes/sampleScene.txt +++ b/data/scenes/sampleSceneObj2.txt @@ -1,5 +1,6 @@ MATERIAL 0 //white diffuse -RGB 1 1 1 +RGB 1 1 1 +DIFFCOE 1 SPECEX 0 SPECRGB 1 1 1 REFL 0 @@ -11,7 +12,8 @@ RSCTCOEFF 0 EMITTANCE 0 MATERIAL 1 //red diffuse -RGB .63 .06 .04 +RGB .63 .06 .04 +DIFFCOE 1 SPECEX 0 SPECRGB 1 1 1 REFL 0 @@ -23,7 +25,8 @@ RSCTCOEFF 0 EMITTANCE 0 MATERIAL 2 //green diffuse -RGB .15 .48 .09 +RGB .15 .48 .09 +DIFFCOE 1 SPECEX 0 SPECRGB 1 1 1 REFL 0 @@ -35,10 +38,11 @@ RSCTCOEFF 0 EMITTANCE 0 MATERIAL 3 //red glossy -RGB .63 .06 .04 +RGB .63 .06 .04 +DIFFCOE 0.8 SPECEX 0 SPECRGB 1 1 1 -REFL 0 +REFL 1 REFR 0 REFRIOR 2 SCATTER 0 @@ -46,20 +50,22 @@ ABSCOEFF 0 0 0 RSCTCOEFF 0 EMITTANCE 0 -MATERIAL 4 //white glossy -RGB 1 1 1 +MATERIAL 4 //Translucent +RGB 1 1 1 +DIFFCOE 0.05 SPECEX 0 SPECRGB 1 1 1 -REFL 0 -REFR 0 -REFRIOR 2 +REFL 1 +REFR 1 +REFRIOR 2.2 SCATTER 0 ABSCOEFF 0 0 0 -RSCTCOEFF 0 +RSCTCOEFF 2 EMITTANCE 0 MATERIAL 5 //glass RGB 0 0 0 +DIFFCOE 1 SPECEX 0 SPECRGB 1 1 1 REFL 0 @@ -71,7 +77,8 @@ RSCTCOEFF 13 EMITTANCE 0 MATERIAL 6 //green glossy -RGB .15 .48 .09 +RGB .15 .48 .09 +DIFFCOE 1 SPECEX 0 SPECRGB 1 1 1 REFL 0 @@ -83,7 +90,8 @@ RSCTCOEFF 0 EMITTANCE 0 MATERIAL 7 //light -RGB 1 1 1 +RGB 1 1 1 +DIFFCOE 1 SPECEX 0 SPECRGB 0 0 0 REFL 0 @@ -95,7 +103,8 @@ RSCTCOEFF 0 EMITTANCE 1 MATERIAL 8 //light -RGB 1 1 1 +RGB 1 1 1 +DIFFCOE 1 SPECEX 0 SPECRGB 0 0 0 REFL 0 @@ -104,15 +113,17 @@ REFRIOR 0 SCATTER 0 ABSCOEFF 0 0 0 RSCTCOEFF 0 -EMITTANCE 15 +EMITTANCE 40 CAMERA -RES 800 800 +RES 1024 1024 FOVY 25 -ITERATIONS 5000 +ITERATIONS 50000 +FOCALLEN 12 +APER 0 FILE test.bmp frame 0 -EYE 0 4.5 12 +EYE 0 4.5 13 VIEW 0 0 -1 UP 0 1 0 @@ -122,7 +133,7 @@ material 0 frame 0 TRANS 0 0 0 ROTAT 0 0 90 -SCALE .01 10 10 +SCALE 1 10 10 OBJECT 1 cube @@ -130,7 +141,7 @@ material 0 frame 0 TRANS 0 5 -5 ROTAT 0 90 0 -SCALE .01 10 10 +SCALE 1 10 10 OBJECT 2 cube @@ -138,7 +149,7 @@ material 0 frame 0 TRANS 0 10 0 ROTAT 0 0 90 -SCALE .01 10 10 +SCALE 1 10 10 OBJECT 3 cube @@ -146,7 +157,7 @@ material 1 frame 0 TRANS -5 5 0 ROTAT 0 0 0 -SCALE .01 10 10 +SCALE 1 10 10 OBJECT 4 cube @@ -154,21 +165,21 @@ material 2 frame 0 TRANS 5 5 0 ROTAT 0 0 0 -SCALE .01 10 10 +SCALE 1 10 10 OBJECT 5 -sphere +C:\Users\Zhen Gou\Documents\Visual Studio 2010\Projects\Project3-Pathtracer\data\diamondMZH.obj material 4 frame 0 -TRANS 0 2 0 -ROTAT 0 180 0 +TRANS 0 5 -2 +ROTAT 10 20 30 SCALE 3 3 3 OBJECT 6 sphere material 3 frame 0 -TRANS 2 5 2 +TRANS 2.25 2.5 2.5 ROTAT 0 180 0 SCALE 2.5 2.5 2.5 @@ -176,15 +187,14 @@ OBJECT 7 sphere material 6 frame 0 -TRANS -2 5 -2 +TRANS -2.25 2 -2.5 ROTAT 0 180 0 SCALE 3 3 3 - OBJECT 8 cube material 8 frame 0 -TRANS 0 10 0 +TRANS 0 9.63 0 ROTAT 0 0 90 -SCALE .3 3 3 \ No newline at end of file +SCALE .3 2 2 diff --git a/data/scenes/sampleSceneSSS.txt b/data/scenes/sampleSceneSSS.txt new file mode 100644 index 0000000..6df8fdf --- /dev/null +++ b/data/scenes/sampleSceneSSS.txt @@ -0,0 +1,208 @@ +MATERIAL 0 //white diffuse +RGB 1 1 1 +DIFFCOE 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 +DIFFCOE 1 +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 .15 .48 .09 +DIFFCOE 1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 3 //red diffuse +RGB 0.9 0.1 0.1 +DIFFCOE 1 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 0 +REFRIOR 2 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 0 + +MATERIAL 4 //Translucent +RGB 0.95 0.5 0.4 +DIFFCOE 0.05 +SPECEX 0 +SPECRGB 1 1 1 +REFL 1 +REFR 1 +REFRIOR 2.2 +SCATTER 1 +ABSCOEFF 0 0 0 +RSCTCOEFF 2 +EMITTANCE 0 + +MATERIAL 5 //glass +RGB 0 0 0 +DIFFCOE 1 +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 +RGB 0.2 0.6 0.2 +DIFFCOE 1 +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 +DIFFCOE 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 40 + +MATERIAL 8 //light +RGB 1 1 1 +DIFFCOE 1 +SPECEX 0 +SPECRGB 0 0 0 +REFL 0 +REFR 0 +REFRIOR 0 +SCATTER 0 +ABSCOEFF 0 0 0 +RSCTCOEFF 0 +EMITTANCE 8 + +CAMERA +RES 1024 1024 +FOVY 25 +ITERATIONS 20000 +FOCALLEN 12 +APER 0 +FILE SSSscene.bmp +frame 0 +EYE 0 4.5 13 +VIEW 0 0 -1 +UP 0 1 0 + +OBJECT 0 +cube +material 0 +frame 0 +TRANS 0 0 0 +ROTAT 0 0 90 +SCALE 1 10 10 + +OBJECT 1 +cube +material 0 +frame 0 +TRANS 0 5 -5 +ROTAT 0 90 0 +SCALE 1 10 10 + +OBJECT 2 +cube +material 0 +frame 0 +TRANS 0 10 0 +ROTAT 0 0 90 +SCALE 1 10 10 + +OBJECT 3 +cube +material 1 +frame 0 +TRANS -5 5 0 +ROTAT 0 0 0 +SCALE 1 10 10 + +OBJECT 4 +cube +material 2 +frame 0 +TRANS 5 5 0 +ROTAT 0 0 0 +SCALE 1 10 10 + +OBJECT 5 +sphere +material 4 +frame 0 +TRANS 0.25 3.5 0 +ROTAT 0 20 0 +SCALE 3.0 3.0 3.0 + +OBJECT 6 +sphere +material 3 +frame 0 +TRANS 2.25 2 -2 +ROTAT 0 180 0 +SCALE 3 3 3 + +OBJECT 7 +sphere +material 6 +frame 0 +TRANS -2.25 2 -2 +ROTAT 0 180 0 +SCALE 3 3 3 + +OBJECT 8 +cube +material 8 +frame 0 +TRANS 0 9.63 0 +ROTAT 0 0 90 +SCALE .3 2 2 + +OBJECT 9 +cube +material 7 +frame 0 +TRANS 0 4.5 -4.63 +ROTAT 0 90 0 +SCALE .3 2 2 \ No newline at end of file diff --git a/debugNormal.bmp b/debugNormal.bmp new file mode 100644 index 0000000..02e8cce Binary files /dev/null and b/debugNormal.bmp differ diff --git a/depth.bmp b/depth.bmp new file mode 100644 index 0000000..f02b930 Binary files /dev/null and b/depth.bmp differ diff --git a/fresnel1.bmp b/fresnel1.bmp new file mode 100644 index 0000000..6b4f4c3 Binary files /dev/null and b/fresnel1.bmp differ diff --git a/intersection.bmp b/intersection.bmp new file mode 100644 index 0000000..a58b9e6 Binary files /dev/null and b/intersection.bmp differ diff --git a/src/interactions.h b/src/interactions.h index 7bf6fab..44acf77 100644 --- a/src/interactions.h +++ b/src/interactions.h @@ -8,6 +8,9 @@ #include "intersections.h" +#define RAY_SPAWN_OFFSET 0.01f +#define SSS_REFLECT_COE 0.075f + struct Fresnel { float reflectionCoefficient; float transmissionCoefficient; @@ -49,16 +52,43 @@ __host__ __device__ glm::vec3 calculateReflectionDirection(glm::vec3 normal, glm 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; +//calculate fresnel +__host__ __device__ Fresnel calculateFresnel(glm::vec3 normal, glm::vec3 incident, float n1, float n2, glm::vec3 reflectionDirection, glm::vec3 transmissionDirection) { + Fresnel fresnel; + + //safety normalize + incident = glm::normalize(incident); + transmissionDirection = glm::normalize(transmissionDirection); + normal = glm::normalize(normal); + + float cosIncident = glm::dot(incident,-normal); + float cosTransmitted =glm::dot(transmissionDirection,-normal); + float sinIncident = sin(acos(cosIncident)); + + //case of total internal reflection + if(n2 < n1) + { + if( asin(sinIncident) > asin(n2/n1) ) + { + fresnel.reflectionCoefficient = 1.0f; + fresnel.transmissionCoefficient = 0.0f; + return fresnel; + } + } - fresnel.reflectionCoefficient = 1; - fresnel.transmissionCoefficient = 0; - return fresnel; + float RS = abs( (n1*cosIncident - n2* cosTransmitted)/(n1*cosIncident + n2* cosTransmitted)) + * abs( (n1*cosIncident - n2* cosTransmitted)/(n1*cosIncident + n2* cosTransmitted)); + + float RP = abs( (n1*cosTransmitted - n2* cosIncident)/(n1*cosTransmitted + n2* cosIncident)) + * abs( (n1*cosTransmitted - n2* cosIncident)/(n1*cosTransmitted + n2* cosIncident)); + + fresnel.reflectionCoefficient = (RS + RP) * 0.5f; + + fresnel.transmissionCoefficient = 1.0f - fresnel.reflectionCoefficient; + return fresnel; } -// LOOK: This function demonstrates cosine weighted random direction generation in a sphere! +//This function demonstrates cosine weighted random direction generation in a hemi-sphere! __host__ __device__ glm::vec3 calculateRandomDirectionInHemisphere(glm::vec3 normal, float xi1, float xi2) { // Crucial difference between this and calculateRandomDirectionInSphere: THIS IS COSINE WEIGHTED! @@ -87,20 +117,130 @@ __host__ __device__ glm::vec3 calculateRandomDirectionInHemisphere(glm::vec3 nor } // TODO: IMPLEMENT THIS FUNCTION -// Now that you know how cosine weighted direction generation works, try implementing -// non-cosine (uniform) weighted random direction generation. -// This should be much easier than if you had to implement calculateRandomDirectionInHemisphere. __host__ __device__ glm::vec3 getRandomDirectionInSphere(float xi1, float xi2) { - return glm::vec3(0,0,0); + + float theta1(xi1*2.0f*PI),theta2(xi2*2.0f*PI); + glm::vec3 V1,V2; + + //random vector on X-Z plane + V1 = glm::vec3(cos(theta1),0.0f,sin(theta1)); + + //random direction on plane containing V1 + V2 = cos(theta2) * V1; + V2.y = sin(theta2); + return V2; } // 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; +__host__ __device__ int calculateBSDF(ray& r, staticGeom * geoms, float randSeed,int intersectObjectID, glm::vec3 intersect, glm::vec3 normal, material mat){ + + thrust::default_random_engine eng(hash(randSeed)); + thrust::uniform_real_distribution distribution(0.0f,1.0f); + + float dice = (float)distribution((eng)); + //diffuse + if(dice 0.0f; + float n1 = isInsideOut ? mat.indexOfRefraction : 1.0f; + float n2 = isInsideOut ? 1.0f : mat.indexOfRefraction; + glm::vec3 reflectDir = glm::reflect(r.direction,normal); + glm::vec3 transmitDir = glm::refract(r.direction,(isInsideOut) ? -normal: normal,n1/n2); + + //Subsurface scattering + if(mat.hasScatter && !isInsideOut) + { + float newDice = (float)distribution((eng)); + //reflect + if(newDice < SSS_REFLECT_COE) + { + r.direction = reflectDir; + r.origin = intersect + RAY_SPAWN_OFFSET * ((isInsideOut) ? (-normal): normal) ; + r.color *= mat.specularColor; + return 1; + } + + //scatter + + ray temp; + temp.origin = intersect + RAY_SPAWN_OFFSET * (-normal); + temp.direction = r.direction; + //temp.direction = transmitDir; + + glm::vec3 exitNormal,exitPoint; + float travelDist = intersectionTest(geoms,intersectObjectID,temp,exitPoint,exitNormal); + + + if(travelDist < - EPSILON) printf("scatter fail"); + + + r.direction = calculateRandomDirectionInHemisphere(r.direction,(float)distribution((eng)),(float)distribution(eng)); + r.origin = exitPoint + RAY_SPAWN_OFFSET * exitNormal; + r.color *= mat.color * powf(exp(-0.5f*travelDist),mat.reducedScatterCoefficient); + + + return 2; + } + + + //reflect only + if(mat.hasReflective && !mat.hasRefractive) + { + r.direction = reflectDir; + r.origin = intersect + RAY_SPAWN_OFFSET * ((isInsideOut) ? (-normal): normal) ; + r.color *= mat.specularColor; + return 1; + } + + //refract only + else if(!mat.hasReflective && mat.hasRefractive) + { + r.direction = transmitDir; + r.origin = intersect + RAY_SPAWN_OFFSET * ((!isInsideOut) ? (-normal): normal); + r.color *= mat.color; + return 2; + } + + else if(!mat.hasReflective && !mat.hasRefractive) + { + return 0; + } + + //both refract and reflect, using fresnel + else + { + Fresnel fres = calculateFresnel(normal,r.direction,n1,n2,reflectDir,transmitDir); + + //to reflect + if( dice < (1.0f -mat.diffuseCoe) * fres.reflectionCoefficient + mat.diffuseCoe) + { + r.direction = reflectDir; + r.origin = intersect + RAY_SPAWN_OFFSET * ((isInsideOut) ? (-normal): normal) ; + r.color *= mat.specularColor; + return 1; + } + + //transmit + else + { + r.direction = transmitDir; + r.origin = intersect + RAY_SPAWN_OFFSET * ((!isInsideOut) ? (-normal): normal); + r.color *= mat.color; + return 2; + } + } + } + return 0; }; #endif diff --git a/src/intersections.h b/src/intersections.h index c9eafb6..5b1d066 100644 --- a/src/intersections.h +++ b/src/intersections.h @@ -13,13 +13,17 @@ #include "cudaMat4.h" #include "utilities.h" +#define FAR_CLIPPING_DISTANCE 999999999.0f + // Some forward declarations __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 triangleIntersectionTest(staticGeom geom, 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__ float intersectionTest(staticGeom * geoms,int num,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,11 +73,232 @@ __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)); } -// TODO: IMPLEMENT THIS FUNCTION +//intersection test against all geometries in the scene +__host__ __device__ float intersectionTest(staticGeom * geoms,int num, ray r, glm::vec3& intersectionPoint, glm::vec3& normal, int & hitMaterialID, int & hitObjID) +{ + float hitDist(FAR_CLIPPING_DISTANCE); + bool hitSomething = false; + + //loop through all geometries + for(int i=0;i 0.0f && d < hitDist) + { + hitSomething = true; + hitDist = d; + + intersectionPoint = interPt; + normal = interNorm; + hitMaterialID = geoms[i].materialid; + hitObjID = i; + } + } + + return (hitSomething) ? hitDist: -1.0f; +} + +//single geometry intersection test given geometry ID +__host__ __device__ float intersectionTest(staticGeom * geoms, int ID, ray r, glm::vec3& intersectionPoint, glm::vec3& normal) +{ + float hitDist(FAR_CLIPPING_DISTANCE); + bool hitSomething = false; + + glm::vec3 interPt(0.0f); + glm::vec3 interNorm(0.0f); + float d(0.0f); + + if(geoms[ID].type == SPHERE) d = sphereIntersectionTest(geoms[ID], r,interPt, interNorm); + else if(geoms[ID].type == CUBE) d = boxIntersectionTest(geoms[ID], r,interPt, interNorm); + else if(geoms[ID].type == MESH) d = triangleIntersectionTest(geoms[ID], r,interPt, interNorm); + + if(d > 0.0f && d < hitDist) + { + hitSomething = true; + hitDist = d; + + intersectionPoint = interPt; + normal = interNorm; + } + + return (hitSomething) ? hitDist: -1.0f; +} + +//tirangle intersection test, return -1 if no intersection, otherwise, distance to intersection +__host__ __device__ float triangleIntersectionTest(staticGeom geom, ray r, glm::vec3& intersectionPoint, glm::vec3& normal){ + + glm::vec3 ro = multiplyMV(geom.inverseTransform, glm::vec4(r.origin,1.0f)); + glm::vec3 rd = glm::normalize(multiplyMV(geom.inverseTransform, glm::vec4(r.direction,0.0f))); + //triangle tri = geom.m_triangle; + triangle tri = geom.m_triangle; + //transform ray + ray rt; + rt.origin = ro; + rt.direction = rd; + + + //plane intersection + glm::vec3 U=tri.p1-tri.p0; glm::vec3 V=tri.p2-tri.p0; glm::vec3 N=glm::cross(U,V); + float D = - glm::dot(tri.p1,N); + float t =-(glm::dot(ro,N)+D)/(glm::dot(rd,N)); + if (t < 0.0f) return -1; + glm::vec3 Intersect=ro+t*rd; + + //check whether inside + glm::vec3 list[4]={tri.p0,tri.p1,tri.p2,tri.p0}; + glm::vec3 testU=tri.p1-tri.p0; glm::vec3 testV=tri.p2-tri.p1; glm::vec3 testN=glm::cross(U,V); glm::vec3 P2P0=ro-Intersect; + bool counterClockwise=true; + + if(glm::dot(testN,P2P0)<0) counterClockwise=false; + + glm::vec3 V1,V2,N1; + + for (int i=0;i<3;++i) + { + V1=list[i]-ro; + V2=list[i+1]-ro; + N1=glm::cross(V2,V1); + if(!counterClockwise) N1=glm::cross(V1,V2); + if((glm::dot(Intersect,N1)-glm::dot(ro,N1))<-EPSILON) return -1; + } + + intersectionPoint = multiplyMV(geom.transform,glm::vec4(Intersect,1.0f)); + normal = multiplyMV(geom.transform, glm::vec4(tri.normal,0.0f)); + + return t; + + + /* half plane method, had bug, not used + //test if on inner of edge 1 + glm::vec3 edge1 = tri.p1 - tri.p0; + if(glm::dot(glm::cross(edge1,Intersect - tri.p0),tri.normal) < 0.0f) return -1; + + //test if on inner of edge 2 + glm::vec3 edge2 = tri.p2 - tri.p1; + if(glm::dot(glm::cross(edge2,Intersect - tri.p1),tri.normal) < 0.0f) return -1; + + //test if on inner of edge 3 + glm::vec3 edge3 = tri.p0 - tri.p2; + if(glm::dot(glm::cross(edge3,Intersect - tri.p2),tri.normal) < 0.0f) return -1; + + intersectionPoint = multiplyMV(geom.transform,glm::vec4(Intersect,1.0f)); + normal = multiplyMV(geom.transform, glm::vec4(tri.normal,0.0f)); + + return glm::length(intersectionPoint - r.origin); + */ +} + // 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){ + const float INFINITY_DISTANCE = 999999999.0f; - return -1; + 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))); + + //transform ray + ray rt; + rt.origin = ro; + rt.direction = rd; + + float Tnear(-INFINITY_DISTANCE) , Tfar(INFINITY_DISTANCE); + //intersect X slab + { + float dx = rd.x; + float ox = ro.x; + //parallel case + if(dx == 0.0f) + { + if(abs(ox) > 0.5f) return -1; + } + float t1,t2; + + t1 = (-0.5f - ox)/(dx); + t2 = (0.5f - ox)/(dx); + + if(min(t1,t2)>Tnear) Tnear=min(t1,t2); + + if(max(t1,t2)Tfar) return -1; + + if(Tfar<0) return -1; + + } + + //intersect Y slab + { + float dy = rd.y; + float oy = ro.y; + //parallel case + if(dy == 0.0f) + { + if(abs(oy) > 0.5f) return -1; + } + float t1,t2; + + t1 = (-0.5f - oy)/(dy); + t2 = (0.5f - oy)/(dy); + + if(min(t1,t2)>Tnear) Tnear=min(t1,t2); + + if(max(t1,t2)Tfar) return -1; + + if(Tfar<0) return -1; + + } + + //intersect Z slab + { + float dz = rd.z; + float oz = ro.z; + //parallel case + if(dz == 0.0f) + { + if(abs(oz) > 0.5f) return -1; + } + float t1,t2; + + t1 = (-0.5f - oz)/(dz); + t2 = (0.5f - oz)/(dz); + + if(min(t1,t2)>Tnear) Tnear=min(t1,t2); + + if(max(t1,t2)Tfar) return -1; + + if(Tfar<0) return -1; + + } + + + glm::vec3 intPt, intNorm; + intPt = getPointOnRay(rt,Tnear); + + if(intPt.y >= 0.5f - EPSILON) intNorm = glm::vec3(0.0f,1.0f,0.0f); + + else if(intPt.y <= -0.5f + EPSILON) intNorm = glm::vec3(0.0,-1.0f,0.0f); + + else if(intPt.x >= 0.5f - EPSILON) intNorm = glm::vec3(1.0f,0.0f,0.0f); + + else if(intPt.x <= -0.5f + EPSILON) intNorm = glm::vec3(-1.0f,0.0f,0.0f); + + else if(intPt.z >= 0.5f - EPSILON) intNorm = glm::vec3(0.0f,0.0f,1.0f); + + else if(intPt.z <= -0.5f + EPSILON) intNorm = glm::vec3(0.0f,0.0f,-1.0f); + + intersectionPoint = multiplyMV(box.transform, glm::vec4(intPt,1.0f)); + normal = glm::normalize(multiplyMV(box.transform, glm::vec4(intNorm,0.0f))); + return glm::length(intersectionPoint - r.origin); } // LOOK: Here's an intersection test example from a sphere. Now you just need to figure out cube and, optionally, triangle. @@ -128,7 +353,6 @@ __host__ __device__ glm::vec3 getRadiuses(staticGeom geom){ return glm::vec3(xradius, yradius, zradius); } -// LOOK: Example for generating a random point on an object using thrust. // Generates a random point on a given cube __host__ __device__ glm::vec3 getRandomPointOnCube(staticGeom cube, float randomSeed){ @@ -174,7 +398,6 @@ __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){ diff --git a/src/main.cpp b/src/main.cpp index b002500..b612fc0 100755 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,8 +6,13 @@ // Yining Karl Li's TAKUA Render, a massively parallel pathtracing renderer: http://www.yiningkarlli.com #include "main.h" +#include "utilities.h" +#include "tiny_obj_loader.h" +#include #define GLEW_STATIC +#define NUM_OF_CAMERA_RAY_PER_PIXEL 1 + //------------------------------- //-------------MAIN-------------- //------------------------------- @@ -52,10 +57,23 @@ int main(int argc, char** argv){ // Initialize CUDA and GL components if (init(argc, argv)) { + //initialize raypool + rayPoolSize = renderCam->resolution.x * renderCam->resolution.y; + cudaMalloc((void **)&rayPoolA,rayPoolSize * sizeof(ray)); + cudaMalloc((void **)&rayPoolB,rayPoolSize * sizeof(ray)); + + // init image to GPU + cudaMalloc((void**)&cudaImageBuffer, (int)renderCam->resolution.x*(int)renderCam->resolution.y*sizeof(glm::vec3)); + cudaMemcpy( cudaImageBuffer, renderCam->image, (int)renderCam->resolution.x*(int)renderCam->resolution.y*sizeof(glm::vec3), cudaMemcpyHostToDevice); + // GLFW main loop mainLoop(); } + + cudaFree(rayPoolA); + cudaFree(rayPoolB); + cudaFree(cudaImageBuffer); return 0; } @@ -86,75 +104,77 @@ void mainLoop() { void runCuda(){ - // Map OpenGL buffer object for writing from CUDA on a single GPU - // No data is moved (Win & Linux). When mapped to CUDA, OpenGL should not use this buffer - - if(iterations < renderCam->iterations){ - uchar4 *dptr=NULL; - iterations++; - cudaGLMapBufferObject((void**)&dptr, pbo); + // Map OpenGL buffer object for writing from CUDA on a single GPU + // No data is moved (Win & Linux). When mapped to CUDA, OpenGL should not use this buffer + if(iterations < renderCam->iterations){ + 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()]; + // pack geom and material arrays + geom* geoms = new geom[renderScene->objects.size()]; + material* materials = new material[renderScene->materials.size()]; - for (int i=0; i < renderScene->objects.size(); i++) { - geoms[i] = renderScene->objects[i]; - } - for (int i=0; i < renderScene->materials.size(); i++) { - materials[i] = renderScene->materials[i]; - } - - // execute the kernel - cudaRaytraceCore(dptr, renderCam, targetFrame, iterations, materials, renderScene->materials.size(), geoms, renderScene->objects.size() ); + for (int i=0; i < renderScene->objects.size(); i++) { + geoms[i] = renderScene->objects[i]; + } + for (int i=0; i < renderScene->materials.size(); i++) { + materials[i] = renderScene->materials[i]; + } + float startTime = utilityCore::getCurrentTime(); + // execute the kernel + cudaRaytraceCore(dptr,cudaImageBuffer, renderCam, rayPoolA, rayPoolB, rayPoolSize, targetFrame, iterations, materials, renderScene->materials.size(), geoms, renderScene->objects.size() ); + float endTime = utilityCore::getCurrentTime(); + float len = endTime - startTime; + if(iterations%10 ==0) cout<<1000.0f*len<<" ms"<resolution.x, renderCam->resolution.y); - - for (int x=0; x < renderCam->resolution.x; x++) { - for (int y=0; y < renderCam->resolution.y; y++) { - int index = x + (y * renderCam->resolution.x); - outputImage.writePixelRGB(renderCam->resolution.x-1-x,y,renderCam->image[index]); - } - } + // unmap buffer object + cudaGLUnmapBufferObject(pbo); + } else { + + if (!finishedRender) { + // output image file + image outputImage(renderCam->resolution.x, renderCam->resolution.y); + + for (int x=0; x < renderCam->resolution.x; x++) { + for (int y=0; y < renderCam->resolution.y; y++) { + int index = x + (y * renderCam->resolution.x); + outputImage.writePixelRGB(renderCam->resolution.x-1-x,y,renderCam->image[index]/(float)renderCam->iterations); + } + } - gammaSettings gamma; - gamma.applyGamma = true; - gamma.gamma = 1.0; - gamma.divisor = 1.0; - outputImage.setGammaSettings(gamma); - string filename = renderCam->imageName; - string s; - stringstream out; - out << targetFrame; - s = out.str(); - utilityCore::replaceString(filename, ".bmp", "."+s+".bmp"); - utilityCore::replaceString(filename, ".png", "."+s+".png"); - outputImage.saveImageRGB(filename); - cout << "Saved frame " << s << " to " << filename << endl; - finishedRender = true; - if (singleFrameMode==true) { - cudaDeviceReset(); - exit(0); - } - } - if (targetFrame < renderCam->frames-1) { - - // clear image buffer and move onto next frame - targetFrame++; - iterations = 0; - for(int i=0; iresolution.x*renderCam->resolution.y; i++){ - renderCam->image[i] = glm::vec3(0,0,0); - } - cudaDeviceReset(); - finishedRender = false; - } - } + gammaSettings gamma; + gamma.applyGamma = true; + gamma.gamma = 1.0; + gamma.divisor = 1.0; + outputImage.setGammaSettings(gamma); + string filename = renderCam->imageName; + string s; + stringstream out; + out << targetFrame; + s = out.str(); + utilityCore::replaceString(filename, ".bmp", "."+s+".bmp"); + utilityCore::replaceString(filename, ".png", "."+s+".png"); + outputImage.saveImageRGB(filename); + cout << "Saved frame " << s << " to " << filename << endl; + finishedRender = true; + if (singleFrameMode==true) { + cudaDeviceReset(); + exit(0); + } + } + if (targetFrame < renderCam->frames-1) { + + // clear image buffer and move onto next frame + targetFrame++; + iterations = 0; + for(int i=0; iresolution.x*renderCam->resolution.y; i++){ + renderCam->image[i] = glm::vec3(0,0,0); + } + cudaDeviceReset(); + finishedRender = false; + } + } } //------------------------------- @@ -168,8 +188,8 @@ bool init(int argc, char* argv[]) { return false; } - width = 800; - height = 800; + width = 1024; + height = 1024; window = glfwCreateWindow(width, height, "CIS 565 Pathtracer", NULL, NULL); if (!window){ glfwTerminate(); diff --git a/src/main.h b/src/main.h index 8bd2aed..ea76b09 100644 --- a/src/main.h +++ b/src/main.h @@ -39,6 +39,10 @@ int targetFrame; int iterations; bool finishedRender; bool singleFrameMode; +glm::vec3 * cudaImageBuffer; +ray * rayPoolA; +ray * rayPoolB; +int rayPoolSize; //------------------------------- //------------GL STUFF----------- diff --git a/src/raytraceKernel.cu b/src/raytraceKernel.cu index 9c7bc7d..2a2d3e1 100644 --- a/src/raytraceKernel.cu +++ b/src/raytraceKernel.cu @@ -9,6 +9,10 @@ #include #include +#include +#include +#include + #include "sceneStructs.h" #include "glm/glm.hpp" #include "utilities.h" @@ -16,6 +20,18 @@ #include "intersections.h" #include "interactions.h" +//Render Settings +#define TRACE_DEPTH 10 +#define RAY_STREAM_COMPACTION_ON 0 +#define ENABLE_ANTIALIASING 1 + +//debug setting +#define DEBUG_NORMAL 0 +#define DEBUG_DEPTH 0 +#define DEBUG_INTERSECTION 0 +#define DEBUG_MAX_DISTANCE 20 + +//report kernel failure void checkCUDAError(const char *msg) { cudaError_t err = cudaGetLastError(); if( cudaSuccess != err) { @@ -24,7 +40,25 @@ void checkCUDAError(const char *msg) { } } -// LOOK: This function demonstrates how to use thrust for random number generation on the GPU! +//predicate function for thrust copy_if +struct rayIsActive +{ + __host__ __device__ bool operator()(const ray r) + { + return r.isActive; + } +}; + +//compact rays, remove inactive rays +int streamCompactRays(ray * in, ray * out, int N) +{ + thrust::device_ptr input(in); + thrust::device_ptr output(out); + int ret = thrust::count_if(input,input + N,rayIsActive()); + thrust::copy_if(input,input+N,output,rayIsActive()); + return ret; +} + // Function that generates static. __host__ __device__ glm::vec3 generateRandomNumberFromThread(glm::vec2 resolution, float time, int x, int y){ int index = x + (y * resolution.x); @@ -35,13 +69,54 @@ __host__ __device__ glm::vec3 generateRandomNumberFromThread(glm::vec2 resolutio return glm::vec3((float) u01(rng), (float) u01(rng), (float) u01(rng)); } -// TODO: IMPLEMENT THIS FUNCTION -// Function 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){ - ray r; - r.origin = glm::vec3(0,0,0); - r.direction = glm::vec3(0,0,-1); - return r; +// Function that does the initial raycast from the camera with DOF and AA capability +//ASSUMING VIEW AND UP vector are all normalized AND FOV ARE IN RADIAN +__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, float focalLen, float aperture){ + + float xx = x; + float yy = y; + + ray r; + r.origin = eye; + + float halfResX = (float)resolution.x / 2.0f; + float halfResY = (float)resolution.y / 2.0f; + + glm::vec3 Pcenter = eye + view; + glm::vec3 right = glm::cross(view,up); + + glm::vec3 Vy = tan(fov.y) * up; + glm::vec3 Vx = tan(fov.x) * right; + //if non-pin hole camera + if(aperture > EPSILON) + { + thrust::default_random_engine rng(hash(time+1.0f)); + thrust::uniform_real_distribution un11(-1,1); + r.origin += un11(rng) * aperture * up; + r.origin += un11(rng) * aperture * right; + + Pcenter = eye + focalLen * view; + Vy = tan(fov.y) * up * focalLen; + Vx = tan(fov.x) * right * focalLen; + } + + //if aa, jitter pixel position +#if(ENABLE_ANTIALIASING) + { + thrust::default_random_engine rng(hash((time+1.0f)* xx * yy)); + thrust::uniform_real_distribution u01(0.0f,1.0f); + xx += u01(rng); + yy += u01(rng); + } +#endif + + + glm::vec2 normalizedPos = glm::vec2((xx -halfResX)/halfResX,(halfResY - yy)/halfResY); + glm::vec3 posOnImagePlane = Pcenter + normalizedPos.y * Vy + normalizedPos.x * Vx; + + r.direction = glm::normalize(posOnImagePlane - r.origin); + + return r; } //Kernel that blacks out a given image buffer @@ -55,7 +130,7 @@ __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){ +__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; @@ -64,9 +139,9 @@ __global__ void sendImageToPBO(uchar4* PBOpos, glm::vec2 resolution, glm::vec3* 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; + 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; @@ -88,78 +163,174 @@ __global__ void sendImageToPBO(uchar4* PBOpos, glm::vec2 resolution, glm::vec3* } } -// TODO: IMPLEMENT THIS FUNCTION -// Core raytracer kernel -__global__ void raytraceRay(glm::vec2 resolution, float time, cameraData cam, int rayDepth, glm::vec3* colors, - staticGeom* geoms, int numberOfGeoms){ +__global__ void pathtraceRays(ray * raypool,glm::vec3* colors, int N, float iterations, int depth, staticGeom* geoms, int numOfGeoms, material * materials, int numOfMats) +{ - int x = (blockIdx.x * blockDim.x) + threadIdx.x; - int y = (blockIdx.y * blockDim.y) + threadIdx.y; - int index = x + (y * resolution.x); + int index = (blockIdx.x * blockDim.x) + threadIdx.x; + + if(index < N) + { + if(!raypool[index].isActive) return; //isActive can be removed when stream compaction is done + + float randSeed = ((float)iterations/10.0f + 1.0f) * ((float) index + 1.0f) * ((float)depth + 1.0f); + + //gather hit info + glm::vec3 intersectionPoint; + glm::vec3 normal; + int hitMatID,hitObjID; + float hitDistance; + hitDistance = intersectionTest(geoms,numOfGeoms, raypool[index], intersectionPoint, normal, hitMatID,hitObjID); - if((x<=resolution.x && y<=resolution.y)){ + //if hit nothing + if(hitDistance < 0.0f || hitDistance >= FAR_CLIPPING_DISTANCE) + { + raypool[index].isActive = false; + return; + } - colors[index] = generateRandomNumberFromThread(resolution, time, x, y); - } + material hitMaterial = materials[hitMatID]; +#if(DEBUG_DEPTH) + colors[raypool[index].pixelIndex] = glm::vec3(hitDistance/DEBUG_MAX_DISTANCE); + return; +#endif + +#if(DEBUG_NORMAL) + colors[raypool[index].pixelIndex] = normal; + return; +#endif + +#if(DEBUG_INTERSECTION) + colors[raypool[index].pixelIndex] = hitMaterial.color; + return; +#endif + + //if hit light + if(hitMaterial.emittance > EPSILON) + { + //colors[raypool[index].pixelIndex] = colors[raypool[index].pixelIndex] + hitMaterial.emittance * hitMaterial.color*raypool[index].color /((float)iterations); + colors[raypool[index].pixelIndex] += hitMaterial.emittance * hitMaterial.color*raypool[index].color; + raypool[index].isActive = false; + return; + } + + else + { + //BSDF handles ray interaction with surface + calculateBSDF(raypool[index],geoms,randSeed, hitObjID, intersectionPoint, normal, hitMaterial); + } + + } +} + +//generate rays from camera +__global__ void generateInitialCamRays(ray * pool,glm::vec2 resolution, float iter, cameraData cam) +{ + int x = (blockIdx.x * blockDim.x) + threadIdx.x; + int y = (blockIdx.y * blockDim.y) + threadIdx.y; + int index = x + (y * resolution.x); + ray R = raycastFromCameraKernel(resolution, iter, x, y,cam.position, cam.view, cam.up, cam.fov * ((float)PI / 180.0f), cam.focalLen, cam.aperture); + R.color = glm::vec3(1.0f); + R.isActive = true; + R.pixelIndex = x + (resolution.x * y); + pool[index] = R; } -// 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 +void cudaRaytraceCore(uchar4* PBOpos, glm::vec3 * cudaimage,camera* renderCam, ray * rayPoolA, ray * rayPoolB,int poolSize, int frame, int iterations, material* materials, int numberOfMaterials, geom* geoms, int numberOfGeoms){ + + //MEMORY MANAGEMENT//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // package geometry and materials and sent to GPU + staticGeom* geomList = new staticGeom[numberOfGeoms]; + for(int i=0; iresolution.x)/float(tileSize)), (int)ceil(float(renderCam->resolution.y)/float(tileSize))); - - // 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); - - // package geometry and materials and sent to GPU - staticGeom* geomList = new staticGeom[numberOfGeoms]; - for(int i=0; iresolution; - cam.position = renderCam->positions[frame]; - cam.view = renderCam->views[frame]; - cam.up = renderCam->ups[frame]; - cam.fov = renderCam->fov; + // 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.focalLen = renderCam->focalLen; + cam.aperture = renderCam->aperture; + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + //kernel config + int tileSize = 32; + int blockSize = 128; + dim3 threadsPerBlock(tileSize, tileSize); + dim3 fullBlocksPerGrid((int)ceil(float(renderCam->resolution.x)/float(tileSize)), (int)ceil(float(renderCam->resolution.y)/float(tileSize))); + + //flood raypool with init cam rays + generateInitialCamRays<<>>(rayPoolA,renderCam->resolution, float(iterations), cam); - // kernel launches - raytraceRay<<>>(renderCam->resolution, (float)iterations, cam, traceDepth, cudaimage, cudageoms, numberOfGeoms); - sendImageToPBO<<>>(PBOpos, renderCam->resolution, cudaimage); + //trace rays +#if RAY_STREAM_COMPACTION_ON + int rayCount = poolSize; + for(int i = 0;i < TRACE_DEPTH; i++) + { + if(rayCount< 1) break; + if(i % 2 == 0) + { + pathtraceRays<<>>(rayPoolA,cudaimage,rayCount, iterations, i, cudageoms, numberOfGeoms,cudamaterials, numberOfMaterials); + rayCount = streamCompactRays(rayPoolA,rayPoolB,rayCount); + } + else + { + pathtraceRays<<>>(rayPoolB,cudaimage,rayCount, iterations, i, cudageoms, numberOfGeoms,cudamaterials, numberOfMaterials); + rayCount = streamCompactRays(rayPoolB,rayPoolA,rayCount); + } - // retrieve image from GPU - cudaMemcpy( renderCam->image, cudaimage, (int)renderCam->resolution.x*(int)renderCam->resolution.y*sizeof(glm::vec3), cudaMemcpyDeviceToHost); + } - // free up stuff, or else we'll leak memory like a madman - cudaFree( cudaimage ); - cudaFree( cudageoms ); - delete geomList; - // make certain the kernel has completed - cudaThreadSynchronize(); +#else + for(int i = 0;i < TRACE_DEPTH; i++) + { + pathtraceRays<<>>(rayPoolA,cudaimage,poolSize, iterations, i, cudageoms, numberOfGeoms,cudamaterials, numberOfMaterials); + } +#endif - checkCUDAError("Kernel failed!"); + + + sendImageToPBO<<>>(PBOpos, renderCam->resolution, cudaimage,(float) iterations); + + + + + // retrieve image from GPU + cudaMemcpy( renderCam->image, cudaimage, (int)renderCam->resolution.x*(int)renderCam->resolution.y*sizeof(glm::vec3), cudaMemcpyDeviceToHost); + + // free up stuff, or else we'll leak memory like a madman + cudaFree( cudageoms ); + cudaFree( cudamaterials); + delete geomList; + + // make certain the kernel has completed + cudaThreadSynchronize(); + + checkCUDAError("Kernel failed!"); + } + + diff --git a/src/raytraceKernel.h b/src/raytraceKernel.h index 984e89f..fdb3275 100755 --- a/src/raytraceKernel.h +++ b/src/raytraceKernel.h @@ -14,6 +14,7 @@ #include #include "sceneStructs.h" -void cudaRaytraceCore(uchar4* pos, camera* renderCam, int frame, int iterations, material* materials, int numberOfMaterials, geom* geoms, int numberOfGeoms); +void cudaRaytraceCore(uchar4* PBOpos, glm::vec3 * image, camera* renderCam, ray * rayPoolA,ray * rayPoolB, int poolSize, int frame, int iterations, material* materials, int numberOfMaterials, geom* geoms, int numberOfGeoms); + #endif diff --git a/src/scene.cpp b/src/scene.cpp index 4cbe216..75bd3a3 100644 --- a/src/scene.cpp +++ b/src/scene.cpp @@ -7,6 +7,7 @@ #include #include "scene.h" #include +#include "tiny_obj_loader.h" scene::scene(string filename){ cout << "Reading scene from " << filename << " ..." << endl; @@ -36,80 +37,113 @@ scene::scene(string filename){ int scene::loadObject(string objectid){ int id = atoi(objectid.c_str()); - if(id!=objects.size()){ - cout << "ERROR: OBJECT ID does not match expected number of objects" << endl; - return -1; - }else{ - cout << "Loading Object " << id << "..." << endl; - geom newObject; - string line; + cout << "Loading Object " << id << "..." << endl; + geom newObject; + string line; + std::vector triangleList; //for loading meshes - //load object type - utilityCore::safeGetline(fp_in,line); - if (!line.empty() && fp_in.good()){ - if(strcmp(line.c_str(), "sphere")==0){ - cout << "Creating new sphere..." << endl; - newObject.type = SPHERE; - }else if(strcmp(line.c_str(), "cube")==0){ - cout << "Creating new cube..." << endl; - newObject.type = CUBE; + //load object type + utilityCore::safeGetline(fp_in,line); + if (!line.empty() && fp_in.good()){ + if(strcmp(line.c_str(), "sphere")==0){ + cout << "Creating new sphere..." << endl; + newObject.type = SPHERE; + }else if(strcmp(line.c_str(), "cube")==0){ + cout << "Creating new cube..." << endl; + newObject.type = CUBE; + }else{ + string objline = line; + string name; + string extension; + istringstream liness(objline); + getline(liness, name, '.'); + getline(liness, extension, '.'); + if(strcmp(extension.c_str(), "obj")==0){ + cout << "Creating new mesh..." << endl; + cout << "Reading mesh from " << line << "... " << endl; + newObject.type = MESH; + + /* + triangle tri; + tri.p0 = glm::vec3(0.5f,-0.5f,0.0f); + tri.p1 = glm::vec3(-0.5f,-0.5f,0.0f); + tri.p2 = glm::vec3(0.0f,0.5f,0.0f); + tri.normal =glm::vec3(0.0f,0.0f,1.0f); + newObject.m_triangle = tri; + */ + + //TinyOBJ to load OBJ file + std::string inputfile = objline; + std::vector shapes; + std::string err = tinyobj::LoadObj(shapes, inputfile.c_str()); + if (!err.empty()) { + std::cerr << err << std::endl; + cin.get(); + exit(1); + } + + //assuming only one shape in obj + for(int i=0;i tokens = utilityCore::tokenizeString(line); - newObject.materialid = atoi(tokens[1].c_str()); - cout << "Connecting Object " << objectid << " to Material " << newObject.materialid << "..." << endl; - } + vector tokens = utilityCore::tokenizeString(line); + newObject.materialid = atoi(tokens[1].c_str()); + cout << "Connecting Object " << objectid << " to Material " << newObject.materialid << "..." << endl; + } //load frames - int frameCount = 0; - utilityCore::safeGetline(fp_in,line); + int frameCount = 0; + utilityCore::safeGetline(fp_in,line); vector translations; vector scales; vector rotations; - while (!line.empty() && fp_in.good()){ - - //check frame number - vector tokens = utilityCore::tokenizeString(line); - if(strcmp(tokens[0].c_str(), "frame")!=0 || atoi(tokens[1].c_str())!=frameCount){ - cout << "ERROR: Incorrect frame count!" << endl; - return -1; - } + while (!line.empty() && fp_in.good()){ - //load tranformations - for(int i=0; i<3; i++){ - glm::vec3 translation; glm::vec3 rotation; glm::vec3 scale; - utilityCore::safeGetline(fp_in,line); - tokens = utilityCore::tokenizeString(line); - if(strcmp(tokens[0].c_str(), "TRANS")==0){ - translations.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(), "ROTAT")==0){ - rotations.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(), "SCALE")==0){ - scales.push_back(glm::vec3(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()))); - } - } + //check frame number + vector tokens = utilityCore::tokenizeString(line); + if(strcmp(tokens[0].c_str(), "frame")!=0 || atoi(tokens[1].c_str())!=frameCount){ + cout << "ERROR: Incorrect frame count!" << endl; + return -1; + } - frameCount++; + //load tranformations + for(int i=0; i<3; i++){ + glm::vec3 translation; glm::vec3 rotation; glm::vec3 scale; utilityCore::safeGetline(fp_in,line); + tokens = utilityCore::tokenizeString(line); + if(strcmp(tokens[0].c_str(), "TRANS")==0){ + translations.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(), "ROTAT")==0){ + rotations.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(), "SCALE")==0){ + scales.push_back(glm::vec3(atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()))); + } + } + + frameCount++; + utilityCore::safeGetline(fp_in,line); } //move frames into CUDA readable arrays @@ -126,12 +160,23 @@ int scene::loadObject(string objectid){ newObject.transforms[i] = utilityCore::glmMat4ToCudaMat4(transform); newObject.inverseTransforms[i] = utilityCore::glmMat4ToCudaMat4(glm::inverse(transform)); } - - objects.push_back(newObject); - + + //add normal object + if(newObject.type != MESH) objects.push_back(newObject); + + //add triangles for mesh + else + { + for(int i = 0;i tokens = utilityCore::tokenizeString(line); @@ -150,6 +195,10 @@ int scene::loadCamera(){ fovy = atof(tokens[1].c_str()); }else if(strcmp(tokens[0].c_str(), "ITERATIONS")==0){ newCamera.iterations = atoi(tokens[1].c_str()); + }else if(strcmp(tokens[0].c_str(), "FOCALLEN")==0){ + newCamera.focalLen = atof(tokens[1].c_str()); + }else if(strcmp(tokens[0].c_str(), "APER")==0){ + newCamera.aperture = atof(tokens[1].c_str()); }else if(strcmp(tokens[0].c_str(), "FILE")==0){ newCamera.imageName = tokens[1]; } @@ -229,15 +278,18 @@ int scene::loadMaterial(string materialid){ material newMaterial; //load static properties - for(int i=0; i<10; i++){ + for(int i=0; i<11; i++){ string line; utilityCore::safeGetline(fp_in,line); vector 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()) ); newMaterial.color = color; - }else if(strcmp(tokens[0].c_str(), "SPECEX")==0){ - newMaterial.specularExponent = atof(tokens[1].c_str()); + } + else if(strcmp(tokens[0].c_str(), "SPECEX")==0){ + newMaterial.specularExponent = atof(tokens[1].c_str()); + }else if(strcmp(tokens[0].c_str(), "DIFFCOE")==0){ + newMaterial.diffuseCoe = atof(tokens[1].c_str()); }else if(strcmp(tokens[0].c_str(), "SPECRGB")==0){ glm::vec3 specColor( atof(tokens[1].c_str()), atof(tokens[2].c_str()), atof(tokens[3].c_str()) ); newMaterial.specularColor = specColor; diff --git a/src/sceneStructs.h b/src/sceneStructs.h index 5e0c853..1b9ec0f 100644 --- a/src/sceneStructs.h +++ b/src/sceneStructs.h @@ -10,16 +10,28 @@ #include "cudaMat4.h" #include #include +#include enum GEOMTYPE{ SPHERE, CUBE, MESH }; struct ray { glm::vec3 origin; glm::vec3 direction; + glm::vec3 color; + bool isActive; + int pixelIndex; +}; + +struct triangle{ + glm::vec3 p0; + glm::vec3 p1; + glm::vec3 p2; + glm::vec3 normal; }; struct geom { enum GEOMTYPE type; + triangle m_triangle; int materialid; int frames; glm::vec3* translations; @@ -27,10 +39,12 @@ struct geom { glm::vec3* scales; cudaMat4* transforms; cudaMat4* inverseTransforms; + }; struct staticGeom { enum GEOMTYPE type; + triangle m_triangle; int materialid; glm::vec3 translation; glm::vec3 rotation; @@ -45,6 +59,8 @@ struct cameraData { glm::vec3 view; glm::vec3 up; glm::vec2 fov; + float focalLen; + float aperture; }; struct camera { @@ -55,6 +71,8 @@ struct camera { int frames; glm::vec2 fov; unsigned int iterations; + float focalLen; + float aperture; glm::vec3* image; ray* rayList; std::string imageName; @@ -62,6 +80,7 @@ struct camera { struct material{ glm::vec3 color; + float diffuseCoe; float specularExponent; glm::vec3 specularColor; float hasReflective; diff --git a/src/tiny_obj_loader.cc b/src/tiny_obj_loader.cc new file mode 100644 index 0000000..0ddca38 --- /dev/null +++ b/src/tiny_obj_loader.cc @@ -0,0 +1,663 @@ +// +// Copyright 2012-2013, Syoyo Fujita. +// +// Licensed under 2-clause BSD liecense. +// + +// +// version 0.9.6: Support Ni(index of refraction) mtl parameter. +// Parse transmittance material parameter correctly. +// version 0.9.5: Parse multiple group name. +// Add support of specifying the base path to load material file. +// version 0.9.4: Initial suupport of group tag(g) +// version 0.9.3: Fix parsing triple 'x/y/z' +// version 0.9.2: Add more .mtl load support +// version 0.9.1: Add initial .mtl load support +// version 0.9.0: Initial +// + + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "tiny_obj_loader.h" + +namespace tinyobj { + +struct vertex_index { + int v_idx, vt_idx, vn_idx; + vertex_index() {}; + vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}; + vertex_index(int vidx, int vtidx, int vnidx) : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}; + +}; +// for std::map +static inline bool operator<(const vertex_index& a, const vertex_index& b) +{ + if (a.v_idx != b.v_idx) return (a.v_idx < b.v_idx); + if (a.vn_idx != b.vn_idx) return (a.vn_idx < b.vn_idx); + if (a.vt_idx != b.vt_idx) return (a.vt_idx < b.vt_idx); + + return false; +} + +struct obj_shape { + std::vector v; + std::vector vn; + std::vector vt; +}; + +static inline bool isSpace(const char c) { + return (c == ' ') || (c == '\t'); +} + +static inline bool isNewLine(const char c) { + return (c == '\r') || (c == '\n') || (c == '\0'); +} + +// Make index zero-base, and also support relative index. +static inline int fixIndex(int idx, int n) +{ + int i; + + if (idx > 0) { + i = idx - 1; + } else if (idx == 0) { + i = 0; + } else { // negative value = relative + i = n + idx; + } + return i; +} + +static inline std::string parseString(const char*& token) +{ + std::string s; + int b = strspn(token, " \t"); + int e = strcspn(token, " \t\r"); + s = std::string(&token[b], &token[e]); + + token += (e - b); + return s; +} + +static inline float parseFloat(const char*& token) +{ + token += strspn(token, " \t"); + float f = (float)atof(token); + token += strcspn(token, " \t\r"); + return f; +} + +static inline void parseFloat2( + float& x, float& y, + const char*& token) +{ + x = parseFloat(token); + y = parseFloat(token); +} + +static inline void parseFloat3( + float& x, float& y, float& z, + const char*& token) +{ + x = parseFloat(token); + y = parseFloat(token); + z = parseFloat(token); +} + + +// Parse triples: i, i/j/k, i//k, i/j +static vertex_index parseTriple( + const char* &token, + int vsize, + int vnsize, + int vtsize) +{ + vertex_index vi(-1); + + vi.v_idx = fixIndex(atoi(token), vsize); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return vi; + } + token++; + + // i//k + if (token[0] == '/') { + token++; + vi.vn_idx = fixIndex(atoi(token), vnsize); + token += strcspn(token, "/ \t\r"); + return vi; + } + + // i/j/k or i/j + vi.vt_idx = fixIndex(atoi(token), vtsize); + token += strcspn(token, "/ \t\r"); + if (token[0] != '/') { + return vi; + } + + // i/j/k + token++; // skip '/' + vi.vn_idx = fixIndex(atoi(token), vnsize); + token += strcspn(token, "/ \t\r"); + return vi; +} + +static unsigned int +updateVertex( + std::map& vertexCache, + std::vector& positions, + std::vector& normals, + std::vector& texcoords, + const std::vector& in_positions, + const std::vector& in_normals, + const std::vector& in_texcoords, + const vertex_index& i) +{ + const std::map::iterator it = vertexCache.find(i); + + if (it != vertexCache.end()) { + // found cache + return it->second; + } + + assert(in_positions.size() > (3*i.v_idx+2)); + + positions.push_back(in_positions[3*i.v_idx+0]); + positions.push_back(in_positions[3*i.v_idx+1]); + positions.push_back(in_positions[3*i.v_idx+2]); + + if (i.vn_idx >= 0) { + normals.push_back(in_normals[3*i.vn_idx+0]); + normals.push_back(in_normals[3*i.vn_idx+1]); + normals.push_back(in_normals[3*i.vn_idx+2]); + } + + if (i.vt_idx >= 0) { + texcoords.push_back(in_texcoords[2*i.vt_idx+0]); + texcoords.push_back(in_texcoords[2*i.vt_idx+1]); + } + + unsigned int idx = positions.size() / 3 - 1; + vertexCache[i] = idx; + + return idx; +} + +static bool +exportFaceGroupToShape( + shape_t& shape, + const std::vector in_positions, + const std::vector in_normals, + const std::vector in_texcoords, + const std::vector >& faceGroup, + const material_t material, + const std::string name) +{ + if (faceGroup.empty()) { + return false; + } + + // Flattened version of vertex data + std::vector positions; + std::vector normals; + std::vector texcoords; + std::map vertexCache; + std::vector indices; + + // Flatten vertices and indices + for (size_t i = 0; i < faceGroup.size(); i++) { + const std::vector& face = faceGroup[i]; + + vertex_index i0 = face[0]; + vertex_index i1(-1); + vertex_index i2 = face[1]; + + size_t npolys = face.size(); + + // Polygon -> triangle fan conversion + for (size_t k = 2; k < npolys; k++) { + i1 = i2; + i2 = face[k]; + + unsigned int v0 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i0); + unsigned int v1 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i1); + unsigned int v2 = updateVertex(vertexCache, positions, normals, texcoords, in_positions, in_normals, in_texcoords, i2); + + indices.push_back(v0); + indices.push_back(v1); + indices.push_back(v2); + } + + } + + // + // Construct shape. + // + shape.name = name; + shape.mesh.positions.swap(positions); + shape.mesh.normals.swap(normals); + shape.mesh.texcoords.swap(texcoords); + shape.mesh.indices.swap(indices); + + shape.material = material; + + return true; + +} + + +void InitMaterial(material_t& material) { + material.name = ""; + material.ambient_texname = ""; + material.diffuse_texname = ""; + material.specular_texname = ""; + material.normal_texname = ""; + for (int i = 0; i < 3; i ++) { + material.ambient[i] = 0.f; + material.diffuse[i] = 0.f; + material.specular[i] = 0.f; + material.transmittance[i] = 0.f; + material.emission[i] = 0.f; + } + material.shininess = 1.f; +} + +std::string LoadMtl ( + std::map& material_map, + const char* filename, + const char* mtl_basepath) +{ + material_map.clear(); + std::stringstream err; + + std::string filepath; + + if (mtl_basepath) { + filepath = std::string(mtl_basepath) + std::string(filename); + } else { + filepath = std::string(filename); + } + + std::ifstream ifs(filepath.c_str()); + if (!ifs) { + err << "Cannot open file [" << filepath << "]" << std::endl; + return err.str(); + } + + material_t material; + + int maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. + while (ifs.peek() != -1) { + ifs.getline(&buf[0], maxchars); + + std::string linebuf(&buf[0]); + + // Trim newline '\r\n' or '\r' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char* token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // new mtl + if ((0 == strncmp(token, "newmtl", 6)) && isSpace((token[6]))) { + // flush previous material. + material_map.insert(std::pair(material.name, material)); + + // initial temporary material + InitMaterial(material); + + // set new mtl name + char namebuf[4096]; + token += 7; + sscanf(token, "%s", namebuf); + material.name = namebuf; + continue; + } + + // ambient + if (token[0] == 'K' && token[1] == 'a' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.ambient[0] = r; + material.ambient[1] = g; + material.ambient[2] = b; + continue; + } + + // diffuse + if (token[0] == 'K' && token[1] == 'd' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.diffuse[0] = r; + material.diffuse[1] = g; + material.diffuse[2] = b; + continue; + } + + // specular + if (token[0] == 'K' && token[1] == 's' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.specular[0] = r; + material.specular[1] = g; + material.specular[2] = b; + continue; + } + + // transmittance + if (token[0] == 'K' && token[1] == 't' && isSpace((token[2]))) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.transmittance[0] = r; + material.transmittance[1] = g; + material.transmittance[2] = b; + continue; + } + + // ior(index of refraction) + if (token[0] == 'N' && token[1] == 'i' && isSpace((token[2]))) { + token += 2; + material.ior = parseFloat(token); + continue; + } + + // emission + if(token[0] == 'K' && token[1] == 'e' && isSpace(token[2])) { + token += 2; + float r, g, b; + parseFloat3(r, g, b, token); + material.emission[0] = r; + material.emission[1] = g; + material.emission[2] = b; + continue; + } + + // shininess + if(token[0] == 'N' && token[1] == 's' && isSpace(token[2])) { + token += 2; + material.shininess = parseFloat(token); + continue; + } + + // ambient texture + if ((0 == strncmp(token, "map_Ka", 6)) && isSpace(token[6])) { + token += 7; + material.ambient_texname = token; + continue; + } + + // diffuse texture + if ((0 == strncmp(token, "map_Kd", 6)) && isSpace(token[6])) { + token += 7; + material.diffuse_texname = token; + continue; + } + + // specular texture + if ((0 == strncmp(token, "map_Ks", 6)) && isSpace(token[6])) { + token += 7; + material.specular_texname = token; + continue; + } + + // normal texture + if ((0 == strncmp(token, "map_Ns", 6)) && isSpace(token[6])) { + token += 7; + material.normal_texname = token; + continue; + } + + // unknown parameter + const char* _space = strchr(token, ' '); + if(!_space) { + _space = strchr(token, '\t'); + } + if(_space) { + int len = _space - token; + std::string key(token, len); + std::string value = _space + 1; + material.unknown_parameter.insert(std::pair(key, value)); + } + } + // flush last material. + material_map.insert(std::pair(material.name, material)); + + return err.str(); +} + +std::string +LoadObj( + std::vector& shapes, + const char* filename, + const char* mtl_basepath) +{ + + shapes.clear(); + + std::stringstream err; + + std::ifstream ifs(filename); + if (!ifs) { + err << "Cannot open file [" << filename << "]" << std::endl; + return err.str(); + } + + std::vector v; + std::vector vn; + std::vector vt; + std::vector > faceGroup; + std::string name; + + // material + std::map material_map; + material_t material; + + int maxchars = 8192; // Alloc enough size. + std::vector buf(maxchars); // Alloc enough size. + while (ifs.peek() != -1) { + ifs.getline(&buf[0], maxchars); + + std::string linebuf(&buf[0]); + + // Trim newline '\r\n' or '\r' + if (linebuf.size() > 0) { + if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); + } + if (linebuf.size() > 0) { + if (linebuf[linebuf.size()-1] == '\n') linebuf.erase(linebuf.size()-1); + } + + // Skip if empty line. + if (linebuf.empty()) { + continue; + } + + // Skip leading space. + const char* token = linebuf.c_str(); + token += strspn(token, " \t"); + + assert(token); + if (token[0] == '\0') continue; // empty line + + if (token[0] == '#') continue; // comment line + + // vertex + if (token[0] == 'v' && isSpace((token[1]))) { + token += 2; + float x, y, z; + parseFloat3(x, y, z, token); + v.push_back(x); + v.push_back(y); + v.push_back(z); + continue; + } + + // normal + if (token[0] == 'v' && token[1] == 'n' && isSpace((token[2]))) { + token += 3; + float x, y, z; + parseFloat3(x, y, z, token); + vn.push_back(x); + vn.push_back(y); + vn.push_back(z); + continue; + } + + // texcoord + if (token[0] == 'v' && token[1] == 't' && isSpace((token[2]))) { + token += 3; + float x, y; + parseFloat2(x, y, token); + vt.push_back(x); + vt.push_back(y); + continue; + } + + // face + if (token[0] == 'f' && isSpace((token[1]))) { + token += 2; + token += strspn(token, " \t"); + + std::vector face; + while (!isNewLine(token[0])) { + vertex_index vi = parseTriple(token, v.size() / 3, vn.size() / 3, vt.size() / 2); + face.push_back(vi); + int n = strspn(token, " \t\r"); + token += n; + } + + faceGroup.push_back(face); + + continue; + } + + // use mtl + if ((0 == strncmp(token, "usemtl", 6)) && isSpace((token[6]))) { + + char namebuf[4096]; + token += 7; + sscanf(token, "%s", namebuf); + + if (material_map.find(namebuf) != material_map.end()) { + material = material_map[namebuf]; + } else { + // { error!! material not found } + InitMaterial(material); + } + continue; + + } + + // load mtl + if ((0 == strncmp(token, "mtllib", 6)) && isSpace((token[6]))) { + char namebuf[4096]; + token += 7; + sscanf(token, "%s", namebuf); + + std::string err_mtl = LoadMtl(material_map, namebuf, mtl_basepath); + if (!err_mtl.empty()) { + faceGroup.clear(); // for safety + return err_mtl; + } + continue; + } + + // group name + if (token[0] == 'g' && isSpace((token[1]))) { + + // flush previous face group. + shape_t shape; + bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, material, name); + if (ret) { + shapes.push_back(shape); + } + + faceGroup.clear(); + + std::vector names; + while (!isNewLine(token[0])) { + std::string str = parseString(token); + names.push_back(str); + token += strspn(token, " \t\r"); // skip tag + } + + assert(names.size() > 0); + + // names[0] must be 'g', so skipt 0th element. + if (names.size() > 1) { + name = names[1]; + } else { + name = ""; + } + + continue; + } + + // object name + if (token[0] == 'o' && isSpace((token[1]))) { + + // flush previous face group. + shape_t shape; + bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, material, name); + if (ret) { + shapes.push_back(shape); + } + + faceGroup.clear(); + + // @todo { multiple object name? } + char namebuf[4096]; + token += 2; + sscanf(token, "%s", namebuf); + name = std::string(namebuf); + + + continue; + } + + // Ignore unknown command. + } + + shape_t shape; + bool ret = exportFaceGroupToShape(shape, v, vn, vt, faceGroup, material, name); + if (ret) { + shapes.push_back(shape); + } + faceGroup.clear(); // for safety + + return err.str(); +} + + +}; \ No newline at end of file diff --git a/src/tiny_obj_loader.h b/src/tiny_obj_loader.h new file mode 100644 index 0000000..62ebe6e --- /dev/null +++ b/src/tiny_obj_loader.h @@ -0,0 +1,61 @@ +// +// Copyright 2012-2013, Syoyo Fujita. +// +// Licensed under 2-clause BSD liecense. +// +#ifndef _TINY_OBJ_LOADER_H +#define _TINY_OBJ_LOADER_H + +#include +#include +#include + +namespace tinyobj { + +typedef struct +{ + std::string name; + + float ambient[3]; + float diffuse[3]; + float specular[3]; + float transmittance[3]; + float emission[3]; + float shininess; + float ior; // index of refraction + + std::string ambient_texname; + std::string diffuse_texname; + std::string specular_texname; + std::string normal_texname; + std::map unknown_parameter; +} material_t; + +typedef struct +{ + std::vector positions; + std::vector normals; + std::vector texcoords; + std::vector indices; +} mesh_t; + +typedef struct +{ + std::string name; + material_t material; + mesh_t mesh; +} shape_t; + +/// Loads .obj from a file. +/// 'shapes' will be filled with parsed shape data +/// The function returns error string. +/// Returns empty string when loading .obj success. +/// 'mtl_basepath' is optional, and used for base path for .mtl file. +std::string LoadObj( + std::vector& shapes, // [output] + const char* filename, + const char* mtl_basepath = NULL); + +}; + +#endif // _TINY_OBJ_LOADER_H \ No newline at end of file diff --git a/src/utilities.cpp b/src/utilities.cpp index a8e5d90..4f30640 100755 --- a/src/utilities.cpp +++ b/src/utilities.cpp @@ -72,9 +72,9 @@ void utilityCore::printCudaMat4(cudaMat4 m){ glm::mat4 utilityCore::buildTransformationMatrix(glm::vec3 translation, glm::vec3 rotation, glm::vec3 scale){ glm::mat4 translationMat = glm::translate(glm::mat4(), translation); - glm::mat4 rotationMat = glm::rotate(glm::mat4(), rotation.x, glm::vec3(1,0,0)); - rotationMat = rotationMat*glm::rotate(glm::mat4(), rotation.y, glm::vec3(0,1,0)); - rotationMat = rotationMat*glm::rotate(glm::mat4(), rotation.z, glm::vec3(0,0,1)); + glm::mat4 rotationMat = glm::rotate(glm::mat4(), rotation.x * (float)PI/180.0f, glm::vec3(1,0,0)); + rotationMat = rotationMat*glm::rotate(glm::mat4(), rotation.y * (float)PI/180.0f, glm::vec3(0,1,0)); + rotationMat = rotationMat*glm::rotate(glm::mat4(), rotation.z * (float)PI/180.0f, glm::vec3(0,0,1)); glm::mat4 scaleMat = glm::scale(glm::mat4(), scale); return translationMat*rotationMat*scaleMat; } @@ -137,6 +137,16 @@ std::istream& utilityCore::safeGetline(std::istream& is, std::string& t) } } } + +float utilityCore::getCurrentTime() +{ + LARGE_INTEGER litmp; + QueryPerformanceFrequency(&litmp); + double dff =(double)litmp.QuadPart; + QueryPerformanceCounter(&litmp); + double t =(double)litmp.QuadPart; + return t/dff; +} //----------------------------- //-------GLM Printers---------- //----------------------------- diff --git a/src/utilities.h b/src/utilities.h index f51598f..6712394 100644 --- a/src/utilities.h +++ b/src/utilities.h @@ -16,6 +16,7 @@ #include #include #include "cudaMat4.h" +#include #define PI 3.1415926535897932384626422832795028841971 #define TWO_PI 6.2831853071795864769252867665590057683943 @@ -37,6 +38,7 @@ namespace utilityCore { extern void printCudaMat4(cudaMat4 m); extern std::string convertIntToString(int number); extern std::istream& safeGetline(std::istream& is, std::string& t); //Thanks to http://stackoverflow.com/a/6089413 + float getCurrentTime(); //----------------------------- //-------GLM Printers---------- diff --git a/std0.bmp b/std0.bmp new file mode 100644 index 0000000..15c5fe7 Binary files /dev/null and b/std0.bmp differ diff --git a/std1.bmp b/std1.bmp new file mode 100644 index 0000000..84c5037 Binary files /dev/null and b/std1.bmp differ diff --git a/windows/Project3-Pathtracer/Project3-Pathtracer/Project3-Pathtracer.vcxproj b/windows/Project3-Pathtracer/Project3-Pathtracer/Project3-Pathtracer.vcxproj index c45dd79..45d49e7 100644 --- a/windows/Project3-Pathtracer/Project3-Pathtracer/Project3-Pathtracer.vcxproj +++ b/windows/Project3-Pathtracer/Project3-Pathtracer/Project3-Pathtracer.vcxproj @@ -28,7 +28,7 @@ - + @@ -77,6 +77,7 @@ + @@ -86,6 +87,7 @@ + @@ -95,6 +97,6 @@ - + \ No newline at end of file diff --git a/windows/Project3-Pathtracer/Project3-Pathtracer/Project3-Pathtracer.vcxproj.filters b/windows/Project3-Pathtracer/Project3-Pathtracer/Project3-Pathtracer.vcxproj.filters index 584fd19..4b81361 100644 --- a/windows/Project3-Pathtracer/Project3-Pathtracer/Project3-Pathtracer.vcxproj.filters +++ b/windows/Project3-Pathtracer/Project3-Pathtracer/Project3-Pathtracer.vcxproj.filters @@ -48,6 +48,9 @@ Header Files + + Header Files + @@ -71,6 +74,9 @@ Source Files\stb_image + + Source Files + diff --git a/windows/Project3-Pathtracer/Project3-Pathtracer/Thumbs.db b/windows/Project3-Pathtracer/Project3-Pathtracer/Thumbs.db new file mode 100644 index 0000000..b1de845 Binary files /dev/null and b/windows/Project3-Pathtracer/Project3-Pathtracer/Thumbs.db differ