Deadline: 21.10.2021
Please put your name here:
Name: .......
- Fork the current repository
- Study the new framework-code of
- IShader.h, ShaderFlat.h, ShaderEyeLight.h, ShaderPhong.h
- ILight.h, LightOmni.h
- Scene.h and main.cpp
- A pointer
std::shared_ptr<const IPrim> hit
is now contained in yourRay
structure. After a ray has been successfully intersected with a primitive, store the primitive’s address inhit
(if the hit ditance is smaller thanray.t
). - In the class
CScene
you find a methodvoid add(const ptr_camera_t pCamera)
. Change your code accordingly using the appropriate vector defined in the class. See also methodCScene::getActiveCamera()
to understand how the member-variablem_activeCamera
should be also initialized. - In the class
CScene
you find a methodvoid add(const ptr_prim_t pPrim)
. Change your code accordingly using the appropriate vector defined in the class. - Rather then intersecting each primitive in the main function we will now use the
bool intersect(Ray& ray) const
method of theScene
class. After modification the method should iterate over all primitives, intersect them and return true or false depending on if we had a valid hit with the scene data or not. - The loop of main.cpp calls the
CScene::RayTrace(Ray& ray)
method. This method should callbool intersect(Ray& ray)
and depending on a hit or not return a white or black color.
If you did everything correct the rendered image should look like:
A surface-shader is a small program that is assigned to a primitive and is responsible for computing the color of each ray hitting this primitive. For example, a flat shader might just return a constant color for a primitive, whereas another shader might compute more complex effects such as lighting, shadows, or texturing.
In this exercise, you will add some missing parts of the given basic shader framework to your ray tracer and implement two simple shaders:
- Implement a simple flat shader. Proceed as follows:
- The shader class has a pure virtual function
Vec3f IShader::shade(const Ray& ray)
, which has to be implemented in all derived shaders. - Implement the
CShaderFlat::shade(const Ray& ray)
method. The method should just return the color passed in the constructor ofCShaderFlat
. - Each primitive has a pointer
ptr_shader_t m_pShader
, which you can obtain viaIPrim::getShader()
function in the new framework and a corresponding modified Constructor definition. Adjust the Constructor code appropriate. For example, our red sphere could be initialized usingCSphere s1(std::make_shared<CShaderFlat>(RGB(1, 0, 0)), Vec3f(-2.0f, 1.7f, 0), 2);
. As we will see later some shaders need access to the scene data (e.g for light or shadow calculations), this is why these shaders gets a reference to the scene objects (e.g.CShaderPhong
). - Finally, if, for instance, the primitive intersected by a ray has been stored in
std::shared_ptr<const IPrim> hit
(you can useray.hit = shared_from_this();
in intersection algorithms), the appropriate color can then be computed by callinghit->getShader()->shade(ray)
; Change your code inCScene::RayTrace(Ray& ray)
such that not black or white is returned but the color from the primitive with the closest hit or the background color if a ray does not hit a primitive.
- The shader class has a pure virtual function
- Implement the
CShaderEyelight::shade(const Ray& ray)
method in the eye light shader, which uses the angle between the incoming ray and the surface normal at the hit point to achieve a better impression of the actual primitive’s shape. The resulting color should be calculated according to: result = |cos(theta)|·color where theta is the angle between the primitive surface normal and the ray direction. As the shader now needs to know some information about the primitive (i.e. the surface normal), some modifications are necessary:- Implement the
Vec3f CPrim::getNormal(const Ray& ray)
method in all classes derived fromIPrim
.getNormal(const Ray& ray)
should return the normalized normal of the primitive. The ray parameter passed togetNormal(const Ray& ray)
should be a ray that has been successfully intersected before, so you can assume that the intersection stored in this ray corresponds to the actual primitive. For example, ray.org + ray.t * ray.dir should be a point on the primitive. - Implement the shading function
CShaderEyelight::shade(const Ray& ray)
usingray.hit->getNormal(ray)
to retrieve the surface normal of the primitive. With the surface normal the above given formula can be applied. If the test scene specified in main.cpp is rendered with these two shaders it should look like:
- Implement the
In the last exercise we implemented two simple surface shaders, which do not take light sources into account. A more advanced surface shading concept, the phong shading model, utilizes light sources to increase the rendering realism and give objects a plastic like appearance. Before we can implement the CShaderPhong::shade(const Ray& ray)
method in ShaderPhong.h we have to implement a simple light source.
-
Implement a point light. Proceed as follows:
- Study the base interface class
ILight
. Each light source which we will derive from it has to implement anILight::illuminate(Ray& ray)
method. - Implement the
CScene::add(const ptr_light_t pLight)
method. - Implement the
CLightOmni::illuminate(Ray& ray)
method. The method should calculate the light intensity, as described in the lecture, which hits the surface point from the light source as well as the direction vector from the surface point to the light source. The direction vector will be later used for shadow computations.
- Study the base interface class
-
Implement the phong illumination model
- The value Lr returned by
Vec3f CShaderPhong::Shade(const Ray& ray) const
should be calculated according to:
Lr = kacaLa + kdcd Σl=0n-1 Ll(Il·N)+ kscs Σl=0n-1 Ll(Il·R)ke
ca: Ambient color
cd: Diffuse color
cs: Specular color (Use cs = (1, 1, 1) for white highlights)ka: Ambient coefficient
kd: Diffuse coefficient
ks: Specular coefficient
ke: Exponent (shine parameter)La: Ambient radiance
Ll: Radiance arriving from light source lIl: Direction to light source l
N: Shading normal
R: Reflected incident ray direction (must point away from the surface)n: Number of lights sources
- The value Lr returned by
- Sometimes an incident ray may hit the backside of a surface (i.e. the shading normal points to the other side.) Then, just turn the shading normal around to face forward.
- Only consider light sources that illuminate the primitive from its front-side (i.e. Il·N > 0).
To add more realism to the phong model we want now to incorporate shadows into it. Proceed as follows:
- Implement the method
CScene::occluded(Ray& ray)
in theCScene
class, which should check if something blocks the light. - Modify
CShaderPhong::shade(const Ray& ray)
to check for occlusion. If everything is implemented correct your images should look like this:
Now we will implement a perfect mirror material with the CShaderMirror
shader. Perfect mirror has no own color and reflects all the rays, thus in constructor it takes only a reference to the scene. Please implement the CShaderMirror::shade(const Ray& ray)
method. To do so you need to calculate two verctors:
- N: Shading normal
- R: Reflected incident ray direction (must point away from the surface)
and use scene object to trace recursively the reflected ray with CScene::rayTrace(Ray& ray)
method.
Once you implement everything correct the rendered image should look like:
Please submit the assignment by making a pull request. Important : Please make sure that
- No extra files are submitted (except those, which were mentioned in the assignment)
- The changes were made only in those files where you were asked to write your code
- The Continiouse Integration system (appVeyor) can build the submitted code
- The rendered images are also submitted in the folder "renders"