-
Enhance your app with Metal ray tracing
Achieve photorealistic 3D scenes in your apps and games through ray tracing, a core part of the Metal graphics framework and Shading Language. We'll explore the latest improvements in implementing ray tracing and take you through upgrades to the production rendering process. Discover Metal APIs to help you create more detailed scenes, integrate natively-supported content with motion, and more.
Ressources
- Rendering reflections in real time using ray tracing
- Applying realistic material and lighting effects to entities
- Accelerating ray tracing using Metal
- Managing groups of resources with argument buffers
- Metal
- Metal Shading Language Specification
Vidéos connexes
Tech Talks
WWDC23
WWDC22
WWDC21
-
Rechercher dans cette vidéo…
-
-
4:48 - Specify intersection functions on render pipeline state
// Create and attach MTLLinkedFunctions object NSArray <id <MTLFunction>> *functions = @[ sphere, cone, torus ]; MTLLinkedFunctions *linkedFunctions = [MTLLinkedFunctions linkedFunctions]; linkedFunctions.functions = functions; pipelineDescriptor.fragmentLinkedFunctions = linkedFunctions; // Create pipeline id<MTLRenderPipelineState> rayPipeline; rayPipeline = [device newRenderPipelineStateWithDescriptor:pipelineDescriptor error:&error]; -
5:02 - Create intersection function table
// Fill out intersection function table descriptor MTLIntersectionFunctionTableDescriptor *tableDescriptor = [MTLIntersectionFunctionTableDescriptor intersectionFunctionTableDescriptor]; tableDescriptor.functionCount = functions.count; // Create intersection function table id<MTLIntersectionFunctionTable> table; table = [rayPipeline newIntersectionFunctionTableWithDescriptor:tableDescriptor stage:MTLRenderStageFragment]; -
5:14 - Populate intersection function table
id<MTLFunctionHandle> handle; for (NSUInteger i = 0 ; i < functions.count ; i++) { // Get a handle to the linked intersection function in the pipeline state handle = [rayPipeline functionHandleWithFunction:functions[i] stage:MTLRenderStageFragment]; // Insert the function handle into the table [table setFunction:handle atIndex:i]; } -
5:48 - Bind resources
[renderEncoder setFragmentAccelerationStructure:accelerationStructure atBufferIndex:0]; [renderEncoder setFragmentIntersectionFunctionTable:table atBufferIndex:1]; -
5:57 - Intersect from fragment shader
[[fragment]] float4 rayFragmentShader(vertex_output vo [[stage_in]], primitive_acceleration_structure accelerationStructure, intersection_function_table<triangle_data> functionTable, /* ... */) { // generate ray, create intersector... intersection = intersector.intersect(ray, accelerationStructure, functionTable); // shading... } -
9:32 - Triangle intersection function
[[intersection(triangle, triangle_data)]] bool alphaTestIntersectionFunction(uint primitiveIndex [[primitive_id]], uint geometryIndex [[geometry_id]], float2 barycentricCoords [[barycentric_coord]], device Material *materials [[buffer(0)]]) { texture2d<float> alphaTexture = materials[geometryIndex].alphaTexture; float2 UV = interpolateUVs(materials[geometryIndex].UVs, primitiveIndex, barycentricCoords); float alpha = alphaTexture.sample(sampler, UV).x; return alpha > 0.5f; } -
10:36 - Custom intersection with intersection query
intersection_query<instancing, triangle_data> iq(ray, as, params); // Step 1: start traversing acceleration structure while (iq.next()) { // Step 2: candidate was found. Check type and run custom intersection. switch (iq.get_candidate_intersection_type()) { case intersection_type::triangle: { bool alphaTestResult = alphaTest(iq.get_candidate_geometry_id(), iq.get_candidate_primitive_id(), iq.get_candidate_triangle_barycentric_coord()); // Step 3: commit candidate or ignore if (alphaTestResult) iq.commit_triangle_intersection() } } } -
10:39 - Custom intersection with intersection query 2
switch (iq.get_committed_intersection_type()) { // Miss case case intersection_type::none: { missShading(); break; } // Triangle intersection was committed. Query some info and do shading. case intersection_type::triangle: { shadeHitTriangle(iq.get_committed_instance_id(), iq.get_committed_distance(), iq.get_committed_triangle_barycentric_coord()); break; } } -
15:30 - Specifying user instance IDs
// New instance descriptor type typedef struct { uint32_t userID; // Members from MTLAccelerationStructureInstanceDescriptor... } MTLAccelerationStructureUserIDInstanceDescriptor; // Specify instance descriptor type through acceleration structure descriptor accelDesc.instanceDescriptorType = MTLAccelerationStructureInstanceDescriptorTypeUserID; -
15:47 - Retrieving user instance IDs 1
// Available in intersection functions [[intersection(bounding_box, instancing)]] IntersectionResult sphereInstanceIntersectionFunction(unsigned int userID[[user_instance_id]], /** other args **/) { // ... } -
15:58 - Retrieving user instance IDs 2
// Available from intersection result intersection_result<instancing> intersection = instanceIntersector.intersect(/* args */); if (intersection.type != intersection_type::none) instanceIndex = intersection.user_instance_id; // Available from intersection query intersection_query<instancing> iq(/* args */); iq.next() if (iq.get_committed_intersection_type() != intersection_type::none) instanceIndex = iq.get_committed_user_instance_id(); -
16:36 - Instance transforms
// Available in intersection functions [[intersection(bounding_box, instancing, world_space_data)]] IntersectionResult intersectionFunction(float4x3 objToWorld [[object_to_world_transform]], float4x3 worldToObj [[world_to_object_transform]], /** other args **/) { // ... } -
16:51 - Instance transforms 2
// Available from intersection result intersection_result<instancing, world_space_data> result = intersector.intersect(/* args */); if (result.type != intersection_type::none) { output.myObjectToWorldTransform = result.object_to_world_transform; output.myWorldToObjectTransform = result.world_to_object_transform; } -
17:03 - Instance transforms 3
// Available from intersection query intersection_query<instancing> iq(/* args */); iq.next() if(iq.get_committed_intersection_type() != intersection_type::none){ output.myObjectToWorldTransform = iq.get_committed_object_to_world_transform(); output.myWorldToObjectTransform = iq.get_committed_world_to_object_transform(); } -
19:17 - Extended limits
// Specify through acceleration structure descriptor accelDesc.usage = MTLAccelerationStructureUsageExtendedLimits; // Specify intersector tag intersector<extended_limits> extendedIntersector; -
22:30 - Sampling time
// Randomly sample time float time = random(exposure_start, exposure_end); result = intersector.intersect(ray, acceleration_structure, time); -
25:54 - Motion instance descriptor
descriptor = [MTLInstanceAccelerationStructureDescriptor new]; descriptor.instanceDescriptorType = MTLAccelerationStructureInstanceDescriptorTypeMotion; // Buffer containing motion instance descriptors descriptor.instanceDescriptorBuffer = instanceBuffer; descriptor.instanceCount = instanceCount; // Buffer containing MTLPackedFloat4x3 transformation matrices descriptor.motionTransformBuffer = transformsBuffer; descriptor.motionTransformCount = transformCount; descriptor.instancedAccelerationStructures = primitiveAccelerationStructures; -
26:33 - Instance motion
// Specify intersector tag kernel void raytracingKernel(acceleration_structure<instancing, instance_motion> as, /* other args */) { intersector<instancing, instance_motion> intersector; // ... } -
27:24 - Primitive motion 1
// Collect keyframe vertex buffers NSMutableArray<MTLMotionKeyframeData*> *vertexBuffers = [NSMutableArray new]; for (NSUInteger i = 0 ; i < keyframeBuffers.count ; i++) { MTLMotionKeyframeData *keyframeData = [MTLMotionKeyframeData data]; keyframeData.buffer = keyframeBuffers[i]; [vertexBuffers addObject:keyframeData]; } -
27:39 - Primitive motion 2
// Create motion geometry descriptor MTLAccelerationStructureMotionTriangleGeometryDescriptor *geometryDescriptor = [MTLAccelerationStructureMotionTriangleGeometryDescriptor descriptor]; geometryDescriptor.vertexBuffers = vertexBuffers; geometryDescriptor.triangleCount = triangleCount; -
27:57 - Primitive motion 3
// Create acceleration structure descriptor MTLPrimitiveAccelerationStructureDescriptor *primitiveDescriptor = [MTLPrimitiveAccelerationStructureDescriptor descriptor]; primitiveDescriptor.geometryDescriptors = @[ geometryDescriptor ]; primitiveDescriptor.motionKeyframeCount = keyframeCount; -
28:10 - Primitive motion 4
// Specify intersector tag kernel void raytracingKernel(acceleration_structure<primitive_motion> as, /* other args */) { intersector<primitive_motion> intersector; // ... }
-