-
Discover ray tracing with Metal
Achieve photorealistic 3D scenes in your apps and games through ray tracing — a core part of the Metal graphics framework and Shading Language. Discover the fundamentals of the Metal ray tracing API and Shading Language extensions for ray tracing, find out how to use them in your graphics apps and games, and learn how to take control of your kernels and combine them into a single compute kernel for optimal performance.
Recursos
- Accelerating ray tracing using Metal
- Modern rendering with Metal
- Accelerating ray tracing and motion blur using Metal
- Debugging the shaders within a draw command or compute dispatch
- Metal Feature Set Tables
- Metal Performance Shaders
- Metal
- Metal Shading Language Specification
Vídeos relacionados
WWDC23
WWDC21
-
Buscar neste vídeo...
-
-
2:42 - Ray tracing with Metal
[[kernel]] void rtKernel(primitive_acceleration_structure accelerationStructure [[buffer(0)]], /* ... */) { // Generate ray ray r = generateCameraRay(tid); // Create an intersector intersector<triangle_data> intersector; // Intersect with scene intersection_result<triangle_data> intersection; intersection = intersector.intersect(r, accelerationStructure); // shading... } -
4:48 - Create an acceleration structure descriptor
let accelerationStructureDescriptor = MTLPrimitiveAccelerationStructureDescriptor() // Create geometry descriptor(s) let geometryDescriptor = MTLAccelerationStructureTriangleGeometryDescriptor() geometryDescriptor.vertexBuffer = vertexBuffer geometryDescriptor.triangleCount = triangleCount accelerationStructureDescriptor.geometryDescriptors = [ geometryDescriptor ] -
5:46 - Allocate acceleration storage
// Query for acceleration structure sizes let sizes = device.accelerationStructureSizes(descriptor: accelerationStructureDescriptor) // Allocate acceleration structure let accelerationStructure = device.makeAccelerationStructure(size: sizes.accelerationStructureSize)! // Allocate scratch buffer let scratchBuffer = device.makeBuffer(length: sizes.buildScratchBufferSize, options: .storageModePrivate)! -
6:24 - Build acceleration structure
// Create command buffer/encoder let commandBuffer = commandQueue.makeCommandBuffer()! let commandEncoder = commandBuffer.makeAccelerationStructureCommandEncoder()! // Encode acceleration structure build commandEncoder.build(accelerationStructure: accelerationStructure, descriptor: accelerationStructureDescriptor, scratchBuffer: scratchBuffer, scratchBufferOffset: 0) // Commit command buffer commandEncoder.endEncoding() commandBuffer.commit() -
7:15 - Pass acceleration structure to ray intersector
[[kernel]] void rtKernel(primitive_acceleration_structure accelerationStructure [[buffer(0)]], /* ... */) { // generate ray, create intersector... intersection = intersector.intersect(r, accelerationStructure); // shading... } -
7:25 - Bind acceleration structure with compute command encoder
computeEncoder.setAccelerationStructure(accelerationStructure, bufferIndex: 0) -
12:16 - Triangle intersection functions
[[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; } -
14:38 - Creating a bounding box acceleration structure
// Create a primitive acceleration structure descriptor let accelerationStructureDescriptor = MTLPrimitiveAccelerationStructureDescriptor() // Create one or more bounding box geometry descriptors: let geometryDescriptor = MTLAccelerationStructureBoundingBoxGeometryDescriptor() geometryDescriptor.boundingBoxBuffer = boundingBoxBuffer geometryDescriptor.boundingBoxCount = boundingBoxCount accelerationStructureDescriptor.geometryDescriptors = [ geometryDescriptor ] -
15:29 - Bounding Box Result
struct BoundingBoxResult { bool accept [[accept_intersection]]; float distance [[distance]]; }; -
15:38 - Bounding box intersection functions
[[intersection(bounding_box)]] BoundingBoxResult sphereIntersectionFunction(float3 origin [[origin]], float3 direction [[direction]], float minDistance [[min_distance]], float maxDistance [[max_distance]], uint primitiveIndex [[primitive_id]], device Sphere *spheres [[buffer(0)]]) { float distance; if (!intersectRaySphere(origin, direction, spheres[primitiveIndex], &distance)) return { false, 0.0f }; if (distance < minDistance || distance > maxDistance) return { false, 0.0f }; return { true, distance }; } -
16:20 - Ray payload
[[intersection(bounding_box)]] BoundingBoxResult sphereIntersectionFunction(/* ... */, ray_data float3 & normal [[payload]]) { // ... if (distance < minDistance || distance > maxDistance) return { false, 0.0f }; float3 intersectionPoint = origin + direction * distance; normal = normalize(intersectionPoint - spheres[primitiveIndex].origin); return { true, distance }; } -
16:48 - Ray payload 2
[[kernel]] void rtKernel(/* ... */) { // generate ray, create intersector... float3 normal; intersection = intersector.intersect(r, accelerationStructure, functionTable, normal); // shading... } -
17:18 - Linking intersection functions
// Load functions from Metal library let sphereIntersectionFunction = library.makeFunction(name: “sphereIntersectionFunction”)! // other functions... // Attach functions to ray tracing compute pipeline descriptor let linkedFunctions = MTLLinkedFunctions() linkedFunctions.functions = [ sphereIntersectionFunction, alphaTestFunction, ... ] computePipelineDescriptor.linkedFunctions = linkedFunctions // Compile and link ray tracing compute pipeline let computePipeline = try device.makeComputePipeline(descriptor: computePipelineDescriptor, options: [], reflection: nil) -
18:17 - Intersection function table offsets
class MTLAccelerationStructureGeometryDescriptor : NSObject { var intersectionFunctionTableOffset: Int // ... } struct MTLAccelerationStructureInstanceDescriptor { var intersectionFunctionTableOffset: UInt32 // ... }; -
18:35 - Creating an intersection function table
// Allocate intersection function table let descriptor = MTLIntersectionFunctionTableDescriptor() descriptor.functionCount = intersectionFunctions.count let functionTable = computePipeline.makeIntersectionFunctionTable(descriptor: descriptor) for i in 0 ..< intersectionFunctions.count { // Get a handle to the linked intersection function in the pipeline state let functionHandle = computePipeline.functionHandle(function: intersectionFunctions[i]) // Insert the function handle into the table functionTable.setFunction(functionHandle, index: i) } // Bind intersection function resources functionTable.setBuffer(sphereBuffer, offset: 0, index: 0) -
19:26 - Pass intersection function table to ray intersector
[[kernel]] void rtKernel(primitive_acceleration_structure accelerationStructure [[buffer(0)]], intersection_function_table<triangle_data> functionTable [[buffer(1)]], /* ... */) { // generate ray, create intersector... intersection = intersector.intersect(r, accelerationStructure, functionTable); // shading... } -
19:33 - Bind intersection function table with compute command encoder
encoder.setIntersectionFunctionTable(functionTable, bufferIndex: 1)
-