Challenge: Bindless ray tracing
June 9, 2022
Mirror, mirror on the ... other mirror. In this challenge, we invite you to explore bindless rendering in Metal 3 and reflect rays on mirrored surfaces.
Thanks to the bindless enhancements in Metal 3, the HybridRendering sample app looks better than ever. It makes all scene resources available to its shaders using Argument Buffers, then uses Metal ray tracing to produce reflections on metallic surfaces — like the ones below.
But as beautifully as the app has drawn this scene, there’s still a limitation: It’s unable to show reflections within reflections, like the mirrored floor reflecting the mirrored sphere.
In fairness: It’s hard to show mirrors reflecting mirrors! Light infinitely bounces between the two surfaces, creating a situation that can’t be solved computationally. Ray tracing apps work around this issue by adding a limited number of light (or ray) “bounces” in the scene to provide more realism.
In this challenge, we invite you to extend that ray tracing code and increase your image’s realism by adding one (or more) extra ray bounces.
Begin the challenge
Before entering this hall of mirrors, we recommend first watching "Go bindless with Metal 3." After you watch, download the "Rendering reflections in real time using ray tracing" sample code — we'll be using it for this challenge.
Go bindless with Metal 3
Watch nowRendering reflections in real time using ray tracing
The app has a dedicated compute pass that calculates reflections from a thin G-Buffer containing positions and normals for each pixel in the image.
The ray tracing shader reads this data and uses it with the camera’s view direction to calculate the direction of the reflected rays. It then uses Metal to trace these rays, find intersections, and shade reflections.
raytracing::ray r;
r.origin = positions.read(tid).xyz;
r.direction = normalize(directions.read(tid).xyz);
r.min_distance = 0.1;
r.max_distance = FLT_MAX;
raytracing::intersector<raytracing::instancing, raytracing::triangle_data> inter;
inter.assume_geometry_type( raytracing::geometry_type::triangle );
auto intersection = inter.intersect( r, accelerationStructure, 0xFF );
if ( intersection.type == raytracing::intersection_type::triangle )
{
// Calculate direct reflections
}
This produces the following image:
But there’s a problem! The fire trucks are missing from the sphere’s reflection on the floor. We challenge you to reveal the missing trucks by modifying the ray tracing shader, rtReflection
, to add an additional ray trace step.
To complete this challenge, you’ll:
- Use the reflected normal and intersection position to calculate the next bounce of rays.
- Extract the material shading logic into a helper function that allows you to shade reflections within the reflections.
- Combine all reflected colors and write them into the
outImage
.
When you’re done, use the screenshot tool, GPU Debugger, or QuickTime to capture your solution and show us your work by posting it on Twitter with the hashtag #WWDC22Challenges. And if you’d like to discuss bindless ray tracing and other Graphics & Games topics, join the team at events throughout the remainder of the week at WWDC22.