' '

1. Functions

A Primitive's core functionality consists of computing where a given ray intersects with it. Two functions exist that provide this functionality:

  • find_all_hits

  • find_first_positive_hit

find_all_hits covers all ray-intersection related functionality: it returns a list of all intersections. However, in practice, we’re often only interested in the "first positive hit", that is, the hit with the lowest positive t-value, which corresponds to the hit that’s closest to the eye and in front of it.

first positive hit

find_first_positive_hit is specialized in finding this first positive hit and therefore is a more efficient choice if one is only interested in this hit. For example, in the figure above, find_all_hits returns both \(H_1\) and \(H_2\), whereas find_first_positive_hit would only return \(H_1\).

As another example, consider the following situation:

first positive hit2

Here, find_all_hits will still return both \(H_1\) and \(H_2\), even though they occur behind the ray’s origin. find_first_positive_hit, however, will tell you that no hit has been found, as if it returns a hit, it must take place in front of its origin.

2. Hit

A Hit objects contains all relevant information about a single ray/primitive intersection. We discuss each member variable in turn.

2.1. t

The t-value indicates where on the ray the intersection occurs.

t value

2.2. position

position specifies where the hit took place in 3D Cartesian coordinates. It must be consistent with the t-value: if the hit was made by ray with origin \(O\) and direction \(\vec\Delta\), position must be equal to \(O + \vec\Delta \cdot {\tt t}\).

2.3. local_position

local_position specifies the hit position with respect to the primitive’s own coordinate system.

  • local_position has two components: xyz and uv.

  • local_position.xyz expresses the hit position in a 3D Cartesian coordinate system.

  • local_position.uv expresses the hit position in a 2D Cartesian coordinate system.

  • local_position is used by decorators. A 2D material will look at the uv-component, whereas a 3D material will rely on the xyz-component.

When implementing a shape primitive (e.g. a Sphere, Cylinder, Cone, …) the local_position will coincide with the Hit's position member.

The position and local_position will diverge when the primitive is transformed: transformers only update position, but leave local_position alone. The reason for this is that an object’s color is not dependent on its position in the scene.

2.4. normal

The unit normal vector on the primitive at position.

normal

The orientation of the normal vector is important, as it is used by lighting algorithms to determine whether or not photons reach that point. For example,

lighting

The light ray reaching \(P_1\) is greeted with normal vector \(\vec n_1\) which points towards the light ray’s origin \(L\), whereas at \(P_2\) the light ray encounters \(\vec n_2\), which points away from the \(L\). The lighting algorithm interprets this as \(P_1\) receiving photons, while \(P_2\) does not.

Now consider the following setup:

inside

Both eye and light source are inside the sphere. We expect the inside of the sphere to be illuminated by \(L\), but according to our lighting algorithm, it is not: the normal vectors will point away from \(L\) each time.

One way to solve this problem is to let Sphere be smart about the direction of its normal vectors. While computing hits, the primitive receives a ray whose origin represents the eye of the camera. When determining the normal, it can choose its direction is such a way that it points towards this eye.

Mathematically, this can be done using the dot product: given the ray origin \(E\), the hit position \(P\) and a normal \(\vec n\) at \(P\), it computes \((E - P) \cdot \vec n\). If it is negative, the normal points towards \(E\), which is what we want. If, however, the dot product turns out to be positive, we need to flip the normal around: \(-\vec n\) becomes the new normal.

You can see this algorithm at work in SphereImplementation::compute_normal_at.

2.5. material

material contains the material which the scene object is made of at that point.

2.6. group_id

group_id represents which group the intersected primitive is part of. This is important for edge detection.