' '

Difficulty

2

Reading material

design/primitives/ray-intersections

Important

Making this extension only makes sense if you also make the Edge Renderer (which in itself leads to the Masking Renderer).

1. Goal

The purpose of the Grouper primitive can better understood in the context of Masking Renderer. Here’s a video:

As you can see, there are black contours to the image. In this case, we can simply add a contour around every primitive.

However, sometimes a single "entity" is made out of multiple primitives, such as a mesh. If we were to draw a contour around every primitive, every triangle in the mesh would have one.

bunny edges

Instead, we want to see the bunny as a whole and have only one contour:

bunny edge

2. Grouper Primitive

In order to tell the ray tracer which primitives form a whole, we need to put them in the same group. For example, to produce the image of the first bunny (with contours around every triangle), each triangle resided in its own separate group, whereas the second bunny had all triangles placed in the same group.

For this extension, it is not important how to draw the contour. We are only interested in defining groups.

Go find the code of Hit. You’ll find there’s a field group_id which is initialized to MISSING_ID by default. It is this field that the Grouper primitive will need to set.

2.1. Skeleton

Create files primitives/group-primitive.cpp and primitives/group-primitive.h. The factory function is

Primitive raytracer::primitives::group(unsigned id, Primitive child);
  • As with other primitives, you need a private GrouperImplementation, subclass of PrimitiveImplementation.

  • Define fields and a constructor.

  • Add (as of yet) empty find_all_hits and bounding_box methods.

2.2. bounding_box

Let’s start with the easiest part: bounding_box. Its implementation should be obvious.

Implement GrouperImplementation::bounding_box.

2.3. find_all_hits

find_all_hits should ask the child primitive for all hits and overwrite all group_id fields with the Grouper's id.

Implement GrouperImplementation::find_all_hits.

3. Evaluation

There are two ways to implement find_all_hits:

  • It could indiscriminately overwrite all group_ids.

  • It could only overwrite them if they’re set to MISSING_ID.

What difference does this cause? Can you relate this to CSS?

Write tests that check the following:

  • Look for a hit-sphere intersection without Grouper. Check that group_id equals MISSING_ID.

  • Place a single sphere inside a group with id 1. Check that group_id is set to 1.

  • Place two spheres inside a group with id 1. Hit one sphere, check that the group_id is set to 1. Hit the other sphere, check that the group_id is also set to 1.

  • Place two spheres inside two separate groups. Hit one sphere, check that the group_id is set correctly. Hit the other sphere, check that the group_id is again set correctly.