## 3.4 Disks

The disk is an interesting quadric since it has a particularly straightforward intersection routine that avoids solving the quadratic equation. In pbrt, a Disk is a circular disk of radius at height along the axis. It is implemented in the files shapes/disk.h and shapes/disk.cpp.

<<Disk Declarations>>=
class Disk : public Shape { public: <<Disk Public Methods>>
Disk(const Transform *ObjectToWorld, const Transform *WorldToObject, bool reverseOrientation, Float height, Float radius, Float innerRadius, Float phiMax) : Shape(ObjectToWorld, WorldToObject, reverseOrientation), height(height), radius(radius), innerRadius(innerRadius), phiMax(Radians(Clamp(phiMax, 0, 360))) { } Bounds3f ObjectBound() const; bool Intersect(const Ray &ray, Float *tHit, SurfaceInteraction *isect, bool testAlphaTexture) const; bool IntersectP(const Ray &ray, bool testAlphaTexture) const; Float Area() const; Interaction Sample(const Point2f &u) const;
private: <<Disk Private Data>>
};

In order to describe partial disks, the user may specify a maximum value beyond which the disk is cut off (Figure 3.8). The disk can also be generalized to an annulus by specifying an inner radius, . In parametric form, it is described by

Figure 3.9 is a rendered image of two disks.

<<Disk Public Methods>>=

<<Disk Private Data>>=

### 3.4.1 Bounding

The bounding method is quite straightforward; it computes a bounding box centered at the height of the disk along , with extent of radius in both the and directions.

<<Disk Method Definitions>>=

### 3.4.2 Intersection Tests

Intersecting a ray with a disk is also easy. The intersection of the ray with the plane that the disk lies in is found and the intersection point is checked to see if it lies inside the disk.

<<Disk Method Definitions>>+=
bool Disk::Intersect(const Ray &r, Float *tHit, SurfaceInteraction *isect, bool testAlphaTexture) const { <<Transform Ray to object space>>
Vector3f oErr, dErr; Ray ray = (*WorldToObject)(r, &oErr, &dErr);
<<Compute plane intersection for disk>>
<<Reject disk intersections for rays parallel to the disk’s plane>>
if (ray.d.z == 0) return false;
Float tShapeHit = (height - ray.o.z) / ray.d.z; if (tShapeHit <= 0 || tShapeHit >= ray.tMax) return false;
<<See if hit point is inside disk radii and >>
Point3f pHit = ray(tShapeHit); Float dist2 = pHit.x * pHit.x + pHit.y * pHit.y; if (dist2 > radius * radius || dist2 < innerRadius * innerRadius) return false; <<Test disk value against >>
Float phi = std::atan2(pHit.y, pHit.x); if (phi < 0) phi += 2 * Pi; if (phi > phiMax) return false;
<<Find parametric representation of disk hit>>
Float u = phi / phiMax; Float rHit = std::sqrt(dist2); Float oneMinusV = ((rHit - innerRadius) / (radius - innerRadius)); Float v = 1 - oneMinusV; Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0); Vector3f dpdv = Vector3f(pHit.x, pHit.y, 0.) * (innerRadius - radius) / rHit; Normal3f dndu(0, 0, 0), dndv(0, 0, 0);
<<Refine disk intersection point>>
pHit.z = height;
<<Compute error bounds for disk intersection>>
Vector3f pError(0, 0, 0);
<<Initialize SurfaceInteraction from parametric information>>
*isect = (*ObjectToWorld)( SurfaceInteraction(pHit, pError, Point2f(u, v), -ray.d, dpdu, dpdv, dndu, dndv, ray.time, this));
*tHit = (Float)tShapeHit;
return true; }

The first step is to compute the parametric value where the ray intersects the plane that the disk lies in. We want to find such that the component of the ray’s position is equal to the height of the disk. Thus,

and

The intersection method computes a value and checks to see if it is inside the legal range of values . If not, the routine can return false.

<<Compute plane intersection for disk>>=
<<Reject disk intersections for rays parallel to the disk’s plane>>
if (ray.d.z == 0) return false;
Float tShapeHit = (height - ray.o.z) / ray.d.z; if (tShapeHit <= 0 || tShapeHit >= ray.tMax) return false;

If the ray is parallel to the disk’s plane (i.e., the component of its direction is zero), no intersection is reported. The case where a ray is both parallel to the disk’s plane and lies within the plane is somewhat ambiguous, but it’s most reasonable to define intersecting a disk edge-on as “no intersection.” This case must be handled explicitly so that NaN floating-point values aren’t generated by the following code.

<<Reject disk intersections for rays parallel to the disk’s plane>>=
if (ray.d.z == 0) return false;

Now the intersection method can compute the point pHit where the ray intersects the plane. Once the plane intersection is known, false is returned if the distance from the hit to the center of the disk is more than Disk::radius or less than Disk::innerRadius. This process can be optimized by actually computing the squared distance to the center, taking advantage of the fact that the and coordinates of the center point are zero, and the coordinate of pHit is equal to height.

<<See if hit point is inside disk radii and >>=
Point3f pHit = ray(tShapeHit); Float dist2 = pHit.x * pHit.x + pHit.y * pHit.y; if (dist2 > radius * radius || dist2 < innerRadius * innerRadius) return false; <<Test disk value against >>
Float phi = std::atan2(pHit.y, pHit.x); if (phi < 0) phi += 2 * Pi; if (phi > phiMax) return false;

If the distance check passes, a final test makes sure that the value of the hit point is between zero and , specified by the caller. Inverting the disk’s parameterization gives the same expression for as the other quadric shapes.

<<Test disk value against >>=
Float phi = std::atan2(pHit.y, pHit.x); if (phi < 0) phi += 2 * Pi; if (phi > phiMax) return false;

If we’ve gotten this far, there is an intersection with the disk. The parameter u is scaled to reflect the partial disk specified by , and v is computed by inverting the parametric equation. The equations for the partial derivatives at the hit point can be derived with a process similar to that used for the previous quadrics. Because the normal of a disk is the same everywhere, the partial derivatives and are both trivially .

<<Find parametric representation of disk hit>>=
Float u = phi / phiMax; Float rHit = std::sqrt(dist2); Float oneMinusV = ((rHit - innerRadius) / (radius - innerRadius)); Float v = 1 - oneMinusV; Vector3f dpdu(-phiMax * pHit.y, phiMax * pHit.x, 0); Vector3f dpdv = Vector3f(pHit.x, pHit.y, 0.) * (innerRadius - radius) / rHit; Normal3f dndu(0, 0, 0), dndv(0, 0, 0);

### 3.4.3 Surface Area

Disks have trivially computed surface area, since they’re just portions of an annulus:

<<Disk Method Definitions>>+=