计算机图形学代写 Computer Graphics – Homework Assignment 3 – Raytracing

Computer Graphics – Homework Assignment 3 – Raytracing

Goals:

  • Understand how to calculate virtual illumination for a 3D scene.

  • Gain experience deriving and implementing mathematical expressions forgeometric calculations.

  • Become more familiar with a 3D math library (identical to one availablewhen programming for a GPU).

  • Become more comfortable with C++.

Getting Started & Handing In:

  • This project builds off of your raycasting homework. There is no newcode to download. You will augment your existing codebase to generateraytraced output for the .json scenes.

  • You will be fleshing out Scene::rayColor() to implement a Phong lightingmodel with shadows, reflections, and (optionally) refractions. You willalso revisit Shape::rayIntersect() to fill out Intersection.position and.normal if you did not already do so (and, for bonus, Intersection.texCoord).

  • Add your code to scene.cpp and shape.cpp.

  • Build and run and test that it is working correctly.

  • Create 600 pixel images for each of the following .json scenes. Copy the.json and your .png files into a new test/ subdirectory.

    • spheres_cylinder.json

    • cone_cube.json

    • stress_test.json

    • camera_test1.json

    • camera_test2.json

    • orthographic_test1.json

    • orthographic_test2.json

    • A .json scene that you make yourself (can be the one you made for yourraycaster with updated materials or an entirely new one).

    • (optional) refraction.json

    • (optional) refraction_inside.json

  • When done, zip your entire raycasting directory, including the test/subdirectory containing the scenes and your program’s output on themwith the long_edge_pixels command line parameter set to 600, and aNotes.txt file. Name the zip file hw03_lastname_firstname.zip.Upload your solution to Blackboard before the deadline. YourNotes.txt should describe any known issues or extra features. YourNotes.txt should also note the names of people in the class whodeserve a star for helping you (not by giving your their code!).

  • The framework and glm vector math library still provide all the supportcode that you need.

  • **THIS IS AN INDIVIDUAL, NOT A GROUP ASSIGNMENT. That means all codewritten for this assignment should be original! Although you arepermitted to consult with each other while working on this assignment,code that is substantially the same will be considered cheating.** In yourNotes.txt, please note who deserves a star (who helped you with theassignment).

Overview:

In this assignment, you will be implementing a ray tracer which supportsPhong lighting, shadows, reflections, and optionally refractions. Youwill be able to create stunning artwork likethis:

The assignment is broken down into parts: (1) computing accurate normalsfor ray/shape intersections, (2) computing direct illumination (a localPhong lighting model with ambient, diffuse, and specular lighting), (3)computing global illumination effects (shadows, reflections, and,optionally, refractions).

Rubric

  • (25 points) The Shape subclasses’ rayIntersect() methods should fillout the Intersection.position and .normal fields. Both of these shouldbe in world-space. The position in world-space is easy to compute, sinceit is the world-space ray’s position + t * direction. The world-spacenormal can be computed as the inverse transpose of the shape’stransformation matrix * the object-space normal. The object-spacenormal can be computed as the gradient (vector of partial derivativeswith respect to x,y,z) of the implicit function for the part ofthe shape intersected. Note: By convention, normals should pointaway from the shape (towards its exterior, not its interior). Forexample, the normal on the bottom face of the cylinder (the z=0 plane)should be <0,0,-1>, not <0,0,1>. Because an implicit functionF(x,y,z) defines the shape as anywhere F(x,y,z) = 0,both F and –F define the same shape. Their gradients point in theopposite direction. The implicit functions given below—they are thesame as in Homework 2 – Raycasting—are defined properly so that theirgradients points towards the exterior.

    • (5 points) Sphere (centered at the origin with radius 1):

      • F(x,y,z) = x² + y² + z² – 1

    • (5 points) Plane (the xy plane, also known as thez = 0 plane)

      • F(x,y,z) = z

    • (5 points) Cylinder (bottom at the origin, top at (0,0,1), radius 1)with a top and bottom cap (circles with radius 1 at z=0 and z=1).You handle this as a collection of three shapes with conditions:

      • if 0 < z < 1: F(x,y,z) = x² + y² – 1

      • if x² + y² < 1: F(x,y,z) = -z

      • if x² + y² < 1: F(x,y,z) = z-1

      $$\begin{align} F_\text{body}(x,y,z) &= x^2 + y^2 – 1 & \text{if}&\quad 0<z<1 \\ F_\text{top}(x,y,z) &= z – 1 & \text{if}&\quad x^2+y^2 < 1 \\ F_\text{bottom}(x,y,z) &= -z & \text{if}&\quad x^2+y^2 < 1 \end{align}$$

    • (5 points) Cone (bottom at the origin, top at (0,0,1), radius 1 atthe bottom, radius 0 at the top, with a bottom cap).You handle this as a collection of two shapes with conditions:

      • if 0 < z ≤ 1: F(x,y,z) = x² + y² – (1 – z)²

      • if x² + y² < 1: F(x,y,z) = -z

      $$\begin{align} F_\text{body}(x,y,z) &= x^2 + y^2 – (1-z)^2 & \text{if}&\quad 0<z \leq 1 \\ F_\text{bottom}(x,y,z) &= -z & \text{if}&\quad x^2+y^2 < 1 \end{align}$$

    • (5 points) Cube (centered at the origin, with vertices( ±1, ±1, ±1)$). Think of it as six planes:

      • if -1 ≤ y,z ≤ 1: F(x,y,z) = x-1

      • if -1 ≤ y,z ≤ 1: F(x,y,z) = -(x+1)

      • if -1 ≤ x,z ≤ 1: F(x,y,z) = y-1

      • if -1 ≤ x,z ≤ 1: F(x,y,z) = -(y+1)

      • if -1 ≤ x,y ≤ 1: F(x,y,z) = z-1

      • if -1 ≤ x,y ≤ 1: F(x,y,z) = -(z+1)

      $$\begin{align} F_\text{right}(x,y,z) &= x-1 & \text{if}&\quad -1 \leq y,z \leq 1 \\ F_\text{left}(x,y,z) &= -(x+1) & \text{if}&\quad -1 \leq y,z \leq 1 \\ F_\text{top}(x,y,z) &= y-1 & \text{if}&\quad -1 \leq x,z \leq 1 \\ F_\text{bottom}(x,y,z) &= -(y+1) & \text{if}&\quad -1 \leq x,z \leq 1 \\ F_\text{front}(x,y,z) &= z-1 & \text{if}&\quad -1 \leq x,y \leq 1 \\ F_\text{back}(x,y,z) &= -(z+1) & \text{if}&\quad -1 \leq x,y \leq 1 \end{align}$$

    • (bonus 5 points) Mesh (arbitrary triangle meshes)

      • The normal should be perpendicular to whichever triangle is intersected.By convention, the vertices of a triangle are given in counter-clockwiseorder when viewed from the exterior of the triangle along the normal.The normal can be obtained via the cross product.

  • (75 points) Illumination. This code goes into Scene::rayColor() andreplaces the stub that you wrote that simply returnsIntersection.diffuse_color. The expression we will be using for thecolor at a point on a surface is:

    *K<sub>R</sub> * I<sub>R</sub> + K<sub>T</sub> * I<sub>T</sub> + sum<sub>L</sub> ( K<sub>A</sub> * I<sub>AL</sub> + [ K<sub>D</sub> * I<sub>L</sub> * ( N · L ) + K<sub>S</sub> * I<sub>L</sub> * ( V · R )<sup>n</sup> ] * S<sub>L</sub> )*

    In this expression, K<sub>A</sub>, K<sub>D</sub>, K<sub>S</sub>,n, K<sub>R</sub>, and K<sub>T</sub>refer to Material.color_ambient, .color_diffuse, .color_specular,.shininess, .color_reflect, and .color_refract. The summation is overall the Light structures in the Scene; I<sub>AL</sub> and I<sub>L</sub> refer toLight.color_ambient and .color. You can multiply colors stored asvec3‘s, and it will do the right thing (multiply each channel orwavelength of light). The other terms will be defined below.

    • For direct illumination, you will implement a local Phong lighting modelwith ambient, diffuse, and specular terms.

      • (10 points) Ambient lighting: *K<sub>A</sub> * I<sub>AL</sub>*

      • (15 point) Diffuse lighting: *K<sub>D</sub> * I<sub>L</sub> * ( N · L )*.N is the (normalized) surface normal vector and L is the (normalized) vectorfrom the surface position to the light’s position. Note that if this dotproduct is negative, then the light is behind the surface and you shouldnot add diffuse or specular lighting.

      • (15 points) Specular lighting: K<sub>S</sub> * I<sub>L</sub> * ( V · R )<sup>n</sup>.V is the (normalized) vector from the surface position to the “eye”position. (Because you are writing a recursive ray tracer, the “eye” inthis formula should be the position of the ray parameter passed to rayColor().)R is the (normalized) direction from the surface position to the lightposition, reflected across the surface normal. The formula forreflecting a vector across another vector is given below underImplementation Details. Note that if the dot product is negative, thenthe light is reflected away from the viewer and you should not add anyspecular lighting. (You can use max( 0, V · R ) insteadof an if statement if you desire.) Also note that if the dot productused for diffuse lighting is zero (the light is behind the surface),then you also should not add specular lighting.

    • For indirect or global illumination, you will add shadows, reflections,and (optionally) refractions.

      • (15 points) Shadows. Is there an object between the surface positionand the light? If so, set S<sub>L</sub> to 0. Otherwise,set S<sub>L</sub> to 1. You can check this easilyby calling closestIntersection() with a ray fromthe surface position in the direction from the surface position towardsthe light position. Check if there is an intersection closer than thelight itself. Note that if the ray originates from the surface positionexactly, your closestIntersection() method may find that the closestintersection is still the surface position (t=0). To avoid this problem,offset the ray’s position by epsilon (a tiny number) along the ray’s direction.

      • (20 points) Reflections. If Material.reflective is true, thenreflect the vector from the “eye” to the surface position across thesurface normal and call rayColor() recursively to find the color oflight I<sub>R</sub> seen by that ray. (Because you are writinga recursive ray tracer, the “eye” in this formula should be the positionof the ray parameter to rayColor().) The formula to reflect one vectoracross another is below under Implementation Details. Just like withshadow rays, you will want to offset the reflected ray’s position along itsdirection to avoid self-intersection. To keep light from reflectingforever, only do this if max_recursion > 0 and pass max_recursion-1to rayColor().

      • (bonus 15 points) Refraction. If Material.refractive is true, thenthe object is translucent. Refract the vector from the eye to thesurface position into the surface according to the index of refractionand call rayColor() recursively to find the color of light I<sub>T</sub> seenby that ray. (The formula for the refracted direction can be found inthe book.) To keep light from bouncing forever, only do this ifmax_recursion > 0 and pass max_recursion-1 to rayColor().To get this right, you will have to adjust your code elsewhere. You willintersect with the inside of shapes. To detect this, check if the normalfaces towards or away from the ray. You will want to use a normal thatfaces the ray direction when calculating lighting effects other thanrefraction. The ratio of the index of refraction of air to the Materialis inverted when leaving versus entering a surface. Translucent objectsshould cast less of a shadow. If the shadow ray hits a refractiveobject, use the object’s refractive color instead of a 0 or 1 forS<sub>L</sub>.

  • (bonus 20 points) Texture mapping. To do this you do two things. InShape::rayIntersect(), fill out Intersection.texCoord with explicit 2Dcoordinates (numbers between 0 and 1) that parameterize the shape. InScene::rayColor(), if material.use_diffuse_texture is true, get thetexture from Scene::textures[material.diffuse_texture], useIntersection.texCoord to look up the color at that texture coordinate inthe image, and then multiply this color withmaterial.diffuse_color to use as the diffuse color K<sub>D</sub>.If there is interest in this, I can provide additional guidance.You will need to create a scene that uses textures (setsmaterial.use_diffuse_texture to true and material.diffuse_texture to e.g. bricks, and adds a textures property to the JSON scene,e.g. textures = { 'bricks': 'path/to/bricks.png' }).

The Code

  • The code for the raytracer is the same as the code for the raycaster.All of your changes will be to Shape::rayIntersect() (to fill outIntersection.position and .normal) and to Scene::rayColor().

  • The one exception is Scene::render(), where you should initially callrayColor() with a max_recursion of whatever your code can handle. Avalue of 3 is sufficient. Also note that the resulting rayColor() may bevery bright, with a value outside [0,1]. To clamp it to the validrange, you can use the function glm::clamp( color, 0, 1 ).

  • To iterate over the lights in Scene::rayColor(), you can use:

    for( const Light& l : lights ) { ... }

Implementation Details

To reflect a (not necessarily normalized) vector v across anormalized vector n, the formula for the reflected vector r isr = -v + 2(v·n)n.

glm and C/C++ standard library functions you need for this assignment

glm. Please consult the Raycasting handout for the basics ofglm. The code particular to raytracing will make use of transpose(m)and perhaps clamp(v, minVal, maxVal) and reflect(-v,n). Note that glm’sreflect() is different than the formula and diagram above. glm’sreflect() takes v pointed towards the surface, not away from it(that is, negated).

std::max(a,b). This is part of C++’s <algorithm>. Note thatstd::max() requires both parameters to have the exact same type. If not,you will get a very long compiler error since they are generic functionswritten using C++ templates. You can also use glm::max(a,b).