Name: Ivan Kabadzhov & Dion Dermaku
Please refer to the file explanations.txt to review precisely the changes made to the code, as we changed some secondary files in order to satisfy proper execution.
Until now we have only hardcoded our scene descriptions in main.cpp. This is of course not practical. In the new framework, a method CScene::ParseOBJ()
is added to the class CScene
, in order to load a scene-description from an obj-file. To make the method work proceed as follows:
- Fork the current repository
- Modify the README.md file in your fork and put your name (or names if you work in a group) above.
- Have a look at the file cow.obj. Study how triangles are stored in the obj-format. The v ’s indicate a single 3d-vertex position, and the f ’s (faces) are indecies to 3 vertex numbers a triangle consits of (please note that the face indecies are starting with 1 and not 0).
- Implement the missing parts of the ParseOBJ-method. Test your implementation with cow.obj. If your obj-importer works as expected you should see an image of a cow like this:
Hint: The obj file-format can be dumped out from various 3d-modelers. Nevertheless, the output might differ from modeler to modeler and there are also other tokens like vn for vertex normals or vt for texture coordinates. Check obj file format for a full description.
So far, your own ray tracer implementation has used no acceleration structure for reducing the number of ray / primitive intersections. This was simple to implement and worked relatively good. Unfortunately, this, of course, is not practical for larger scenes as you have noticed in the last exercise with the cow. As such, you need a data structure to speed up the process of finding the first hit of a ray with the primitives. In recent years the kd-tree proved to be a useful acceleration data structure for minimizing ray-intersection tests. To implement your kd-tree proceed as follows:
- A new class
CBoundingBox
is now in the framwork which contains twoVec3f
’s for themin, max
- fields of the bounding box. - Furthermore the class has a method
void CBoundingBox::extend(Vec3f a)
. Implement the following functionality: If a is not inside a bounding box b,b.bxtend(a)
should extend the bounding box until it also includes a. Tip: Initialize your box with an ’empty box’ (min = +infinity, max = −infinity). - The method
virtual CBoundingBox CPrim::calcBounds() = 0
has to be implemented in every class derived fromCPrim
. - Most acceleration structures require to clip the ray with the bounding box of the scene, as the origin might otherwise be outside the scene bounds. For clipping a ray with the bounding box of a scene, you can best use the slabs algorithm and implement it in
void CBoundingBox::clip(const Ray& ray, float& t0, float& t1)
. - You will need a method to decide whether a primitive is contained in a given voxel or not. Therefore, a method
bool CPrim::inVoxel(const CBoundingBox& box)
is added to the classCPrim
. Implement the method. For simplicity, just check the primitives bounding box for overlap with the box. Therefore, implement the methodbool CBoundingBox::overlaps(const CBoundingBox& box)
in yourCBoundingBox
class. Keep in mind that floating-point numbers have limited accuracy! - Implement the method
CBoundingBox CScene::CalcBounds()
, which should calculate the bounding box of the scene. - Implement the method
std::shared_ptr<CBSPNode> CBSPTree::BuildTree(const CBoundingBox& box, const std::vector<std::shared_ptr<CPrim>>& vpPrims, int depth)
of the classCBSPTree
. As soon as you have reached a maximum depth (e.g. 20), or you have less then a minimum number of primitives (e.g. 3 or 4), stop subdividing and generate a voxel. Otherweise, split your voxel in the middle (in the maximum dimension), sort your current voxels primitives into two vector left and right, and recursively call BuildTree with the respective voxels and vector for left and right. Start subdivision with a list of all primitives, the total scene bounds, and an initial recursion depth of 0.
Note: BSP-tree is a special case of the KD-tree (for the 3-dimensional case, e.g K=3). A very good implementation of the KD-tree may be found in the DGM-library repository: KDTree.h KDTree.cpp KDNode.h KDNode.cpp. - For traversal, use a simple, recursive algorithm, see Ray Tracing with the BSP tree, by Kelvin Sung and Peter Shirley, in Graphics Gems III or read the chapter 7.2 in the thesis of Dr. Ingo Wald.
Instead of optimizing too much, rather concentrate on a stable, bug-free implementation.