Colord PhongMaterial::shade(const Raytracer* raytracer, const Rayd& ray, const HitPoint& hitPoint, State& state) const { auto texColor = diffuseTexture() ? diffuseTexture()->evaluate(ray, hitPoint) : Colord::black(); Lambertian ambientBRDF(texColor, ambientCoefficient()); Lambertian diffuseBRDF(texColor, diffuseCoefficient()); Vector3d out = -ray.direction(); auto color = ambientBRDF.reflectance(hitPoint, out) * raytracer->scene()->ambient(); for (const auto& light : raytracer->scene()->lights()) { Vector3d in = light->direction(hitPoint.point()); if (raytracer->scene()->intersects(Rayd(hitPoint.point(), in).epsilonShifted(), state)) { state.shadowHit(this, "PhongMaterial"); } else { state.shadowMiss(this, "PhongMaterial"); double normalDotIn = hitPoint.normal() * in; if (normalDotIn > 0.0) { color += ( diffuseBRDF(hitPoint, out, in) + m_specularBRDF(hitPoint, out, in) ) * light->radiance() * normalDotIn; } } } return color; }
Colord TransparentMaterial::shade(const Raytracer* raytracer, const Rayd& ray, const HitPoint& hitPoint, State& state) const { Vector3d out = -ray.direction(); Vector3d in; Colord reflectedColor = m_reflectiveBRDF.sample(hitPoint, out, in); Rayd reflected(hitPoint.point(), in); if (m_specularBTDF.totalInternalReflection(ray, hitPoint)) { return raytracer->rayColor(reflected.epsilonShifted(), state); } else { auto color = PhongMaterial::shade(raytracer, ray, hitPoint, state); Vector3d trans; Colord transmittedColor = m_specularBTDF.sample(hitPoint, out, trans); Rayd transmitted(hitPoint.point(), trans); state.recordEvent("TransparentMaterial: Tracing reflection"); color += reflectedColor * raytracer->rayColor(reflected.epsilonShifted(), state) * fabs(hitPoint.normal() * in); state.recordEvent("TransparentMaterial: Tracing transmission"); color += transmittedColor * raytracer->rayColor(transmitted.epsilonShifted(), state) * fabs(hitPoint.normal() * trans); return color; } }
const Primitive* ConvexOperation::intersect(const Rayd& ray, HitPointInterval& hitPoints, State& state) const { if (!boundingBoxIntersects(ray)) { state.miss("ConvexOperation, bounding box miss"); return nullptr; } // unlike the intersection methods for box, sphere, etc, convexIntersect() // only returns a single (closest) hitpoint. So we need to shoot a ray in the // opposite direction as well. // calculate hitpoint in ray direction if (convexIntersect(ray, hitPoints)) { // if it's a hit, do it again in the opposite direction HitPointInterval opposite; // If we double the hit distance and take the longest possible length in // the bounding box, we should be beyond the other side of the object Rayd oppositeRay( ray.at(hitPoints.min().distance() * 2.0 + boundingBox().size().length()), -ray.direction() ); // fire! convexIntersect(oppositeRay, opposite); HitPoint oppositePoint = opposite.min(); // now, we have to project that point on the opposite side back to the // original ray to find the real distance value oppositePoint.setDistance(ray.projectedDistance(oppositePoint.point())); // add it to the interval hitPoints.add(oppositePoint); // and merge both points into a single interval. we can do that, since this // is by definition a convex object, so there can be only a single interval hitPoints = hitPoints.merged(); auto hitPoint = hitPoints.minWithPositiveDistance(); if (hitPoint.isUndefined()) { return nullptr; state.miss("ConvexOperation, ray miss"); } else { state.hit("ConvexOperation"); hitPoints.setPrimitive(this); return this; } } state.miss("ConvexOperation, ray miss"); return nullptr; }