├── .gitignore ├── Configuration └── SampleCode.xcconfig ├── Documentation ├── final.png ├── primary_2x.png ├── reflection_2x.png ├── secondary_2x.png ├── shadow_2x.png └── texture_2x.png ├── LICENSE └── LICENSE.txt ├── MPSPathTracingSample.xcodeproj ├── .xcodesamplecode.plist ├── project.pbxproj └── project.xcworkspace │ └── xcshareddata │ └── WorkspaceSettings.xcsettings ├── MPSPathTracingSample ├── Renderer.h ├── Renderer.mm ├── Scene.h ├── Scene.mm ├── ShaderTypes.h ├── Shaders.metal ├── Transforms.h ├── Transforms.mm ├── iOS │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── GameViewController.h │ ├── GameViewController.m │ ├── Info.plist │ └── main.m └── macOS │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ └── Main.storyboard │ ├── GameViewController.h │ ├── GameViewController.m │ ├── Info.plist │ └── main.m ├── README.md ├── crate.jpg ├── mario.obj └── mario.png /.gitignore: -------------------------------------------------------------------------------- 1 | # See LICENSE folder for this sample’s licensing information. 2 | # 3 | # Apple sample code gitignore configuration. 4 | 5 | # Finder 6 | .DS_Store 7 | 8 | # Xcode - User files 9 | xcuserdata/ 10 | 11 | **/*.xcodeproj/project.xcworkspace/* 12 | !**/*.xcodeproj/project.xcworkspace/xcshareddata 13 | 14 | **/*.xcodeproj/project.xcworkspace/xcshareddata/* 15 | !**/*.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings 16 | 17 | **/*.playground/playground.xcworkspace/* 18 | !**/*.playground/playground.xcworkspace/xcshareddata 19 | 20 | **/*.playground/playground.xcworkspace/xcshareddata/* 21 | !**/*.playground/playground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings 22 | -------------------------------------------------------------------------------- /Configuration/SampleCode.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // See LICENSE folder for this sample’s licensing information. 3 | // 4 | // SampleCode.xcconfig 5 | // 6 | 7 | // The `SAMPLE_CODE_DISAMBIGUATOR` configuration is to make it easier to build 8 | // and run a sample code project. Once you set your project's development team, 9 | // you'll have a unique bundle identifier. This is because the bundle identifier 10 | // is derived based on the 'SAMPLE_CODE_DISAMBIGUATOR' value. Do not use this 11 | // approach in your own projects—it's only useful for sample code projects because 12 | // they are frequently downloaded and don't have a development team set. 13 | SAMPLE_CODE_DISAMBIGUATOR=${DEVELOPMENT_TEAM} 14 | -------------------------------------------------------------------------------- /Documentation/final.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetiger/MetalRayTracing/87f6f72334b1ae71be7fcad287ab71640a8f0668/Documentation/final.png -------------------------------------------------------------------------------- /Documentation/primary_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetiger/MetalRayTracing/87f6f72334b1ae71be7fcad287ab71640a8f0668/Documentation/primary_2x.png -------------------------------------------------------------------------------- /Documentation/reflection_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetiger/MetalRayTracing/87f6f72334b1ae71be7fcad287ab71640a8f0668/Documentation/reflection_2x.png -------------------------------------------------------------------------------- /Documentation/secondary_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetiger/MetalRayTracing/87f6f72334b1ae71be7fcad287ab71640a8f0668/Documentation/secondary_2x.png -------------------------------------------------------------------------------- /Documentation/shadow_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetiger/MetalRayTracing/87f6f72334b1ae71be7fcad287ab71640a8f0668/Documentation/shadow_2x.png -------------------------------------------------------------------------------- /Documentation/texture_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetiger/MetalRayTracing/87f6f72334b1ae71be7fcad287ab71640a8f0668/Documentation/texture_2x.png -------------------------------------------------------------------------------- /LICENSE/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright © 2019 Apple Inc. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | 9 | -------------------------------------------------------------------------------- /MPSPathTracingSample.xcodeproj/.xcodesamplecode.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /MPSPathTracingSample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3E9448D620BC6BF0001DD43A /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 3E9448D520BC6BF0001DD43A /* README.md */; }; 11 | 4E289C562439C06E00069BF8 /* mario.obj in Resources */ = {isa = PBXBuildFile; fileRef = 4E289C542439C06E00069BF8 /* mario.obj */; }; 12 | 4E289C572439C06E00069BF8 /* mario.png in Resources */ = {isa = PBXBuildFile; fileRef = 4E289C552439C06E00069BF8 /* mario.png */; }; 13 | 4E289C5B2439C93C00069BF8 /* mario.obj in Resources */ = {isa = PBXBuildFile; fileRef = 4E289C542439C06E00069BF8 /* mario.obj */; }; 14 | 4E289C5C2439C94000069BF8 /* mario.png in Resources */ = {isa = PBXBuildFile; fileRef = 4E289C552439C06E00069BF8 /* mario.png */; }; 15 | 4EC9159024403945004F6B2A /* ModelIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC9158F24403944004F6B2A /* ModelIO.framework */; }; 16 | 4EC915922440394E004F6B2A /* ModelIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC915912440394E004F6B2A /* ModelIO.framework */; }; 17 | 4EE7DAD5261ED90F00BF6A62 /* crate.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4EE7DAD4261ED90F00BF6A62 /* crate.jpg */; }; 18 | 4EE7DAD8261ED97000BF6A62 /* crate.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 4EE7DAD4261ED90F00BF6A62 /* crate.jpg */; }; 19 | 51C2956220A81D5500F951BE /* Scene.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51C2956120A81D5500F951BE /* Scene.mm */; }; 20 | 51C2956320A81D5500F951BE /* Scene.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51C2956120A81D5500F951BE /* Scene.mm */; }; 21 | 51F7000A209BC4040017E288 /* Renderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFE0209BC3520017E288 /* Renderer.mm */; }; 22 | 51F7000B209BC4040017E288 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFE2209BC3530017E288 /* Shaders.metal */; }; 23 | 51F7000C209BC4040017E288 /* Transforms.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFE1209BC3530017E288 /* Transforms.mm */; }; 24 | 51F7000D209BC4050017E288 /* Renderer.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFE0209BC3520017E288 /* Renderer.mm */; }; 25 | 51F7000E209BC4050017E288 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFE2209BC3530017E288 /* Shaders.metal */; }; 26 | 51F7000F209BC4050017E288 /* Transforms.mm in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFE1209BC3530017E288 /* Transforms.mm */; }; 27 | 51F70012209BCA010017E288 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51F7FFF8209BC3C00017E288 /* Main.storyboard */; }; 28 | 51F70013209BCA010017E288 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51F7FFF6209BC3C00017E288 /* LaunchScreen.storyboard */; }; 29 | 51F70014209BCA0B0017E288 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFEE209BC3B60017E288 /* AppDelegate.m */; }; 30 | 51F70015209BCA0B0017E288 /* GameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFF0209BC3B70017E288 /* GameViewController.m */; }; 31 | 51F70016209BCA0B0017E288 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFEC209BC3B60017E288 /* main.m */; }; 32 | 51F70017209BCA110017E288 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFFD209BC3E90017E288 /* AppDelegate.m */; }; 33 | 51F70018209BCA110017E288 /* GameViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 51F7FFFC209BC3E90017E288 /* GameViewController.m */; }; 34 | 51F70019209BCA110017E288 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 51F70001209BC3E90017E288 /* main.m */; }; 35 | 51F7001A209BCA180017E288 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 51F70006209BC3F00017E288 /* Main.storyboard */; }; 36 | 51F7FFBA209BC1C40017E288 /* MetalPerformanceShaders.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51E97FA52017014200D09D13 /* MetalPerformanceShaders.framework */; }; 37 | 51F7FFCD209BC1C60017E288 /* MetalPerformanceShaders.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51E97FA72017014A00D09D13 /* MetalPerformanceShaders.framework */; }; 38 | /* End PBXBuildFile section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | 3E9448D520BC6BF0001DD43A /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 42 | 4E289C542439C06E00069BF8 /* mario.obj */ = {isa = PBXFileReference; lastKnownFileType = text; path = mario.obj; sourceTree = SOURCE_ROOT; }; 43 | 4E289C552439C06E00069BF8 /* mario.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = mario.png; sourceTree = SOURCE_ROOT; }; 44 | 4EC9158F24403944004F6B2A /* ModelIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ModelIO.framework; path = System/Library/Frameworks/ModelIO.framework; sourceTree = SDKROOT; }; 45 | 4EC915912440394E004F6B2A /* ModelIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ModelIO.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS13.2.sdk/System/Library/Frameworks/ModelIO.framework; sourceTree = DEVELOPER_DIR; }; 46 | 4EE7DAD4261ED90F00BF6A62 /* crate.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = crate.jpg; sourceTree = SOURCE_ROOT; }; 47 | 51564714205B0E0B006AF627 /* Metal.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Metal.framework; path = System/Library/Frameworks/Metal.framework; sourceTree = SDKROOT; }; 48 | 51564716205B0E11006AF627 /* MetalPerformanceShaders.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalPerformanceShaders.framework; path = System/Library/Frameworks/MetalPerformanceShaders.framework; sourceTree = SDKROOT; }; 49 | 51C2956020A81D3300F951BE /* Scene.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Scene.h; sourceTree = ""; }; 50 | 51C2956120A81D5500F951BE /* Scene.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Scene.mm; sourceTree = ""; }; 51 | 51E97FA52017014200D09D13 /* MetalPerformanceShaders.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalPerformanceShaders.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS11.3.Internal.sdk/System/Library/Frameworks/MetalPerformanceShaders.framework; sourceTree = DEVELOPER_DIR; }; 52 | 51E97FA72017014A00D09D13 /* MetalPerformanceShaders.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MetalPerformanceShaders.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.Internal.sdk/System/Library/Frameworks/MetalPerformanceShaders.framework; sourceTree = DEVELOPER_DIR; }; 53 | 51F70000209BC3E90017E288 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | 51F70001209BC3E90017E288 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 55 | 51F70007209BC3F00017E288 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 56 | 51F7FFC2209BC1C40017E288 /* MPSPathTracingSample-iOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MPSPathTracingSample-iOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 51F7FFD4209BC1C60017E288 /* MPSPathTracingSample-macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MPSPathTracingSample-macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 58 | 51F7FFDB209BC3520017E288 /* Renderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Renderer.h; sourceTree = ""; }; 59 | 51F7FFDE209BC3520017E288 /* Transforms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Transforms.h; sourceTree = ""; }; 60 | 51F7FFDF209BC3520017E288 /* ShaderTypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShaderTypes.h; sourceTree = ""; }; 61 | 51F7FFE0209BC3520017E288 /* Renderer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Renderer.mm; sourceTree = ""; }; 62 | 51F7FFE1209BC3530017E288 /* Transforms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Transforms.mm; sourceTree = ""; }; 63 | 51F7FFE2209BC3530017E288 /* Shaders.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = Shaders.metal; sourceTree = ""; }; 64 | 51F7FFEC209BC3B60017E288 /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 65 | 51F7FFED209BC3B60017E288 /* GameViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameViewController.h; sourceTree = ""; }; 66 | 51F7FFEE209BC3B60017E288 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 67 | 51F7FFEF209BC3B70017E288 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 68 | 51F7FFF0209BC3B70017E288 /* GameViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GameViewController.m; sourceTree = ""; }; 69 | 51F7FFF1209BC3B70017E288 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 70 | 51F7FFF7209BC3C00017E288 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 71 | 51F7FFF9209BC3C00017E288 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 72 | 51F7FFFC209BC3E90017E288 /* GameViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GameViewController.m; sourceTree = ""; }; 73 | 51F7FFFD209BC3E90017E288 /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 74 | 51F7FFFE209BC3E90017E288 /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 75 | 51F7FFFF209BC3E90017E288 /* GameViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GameViewController.h; sourceTree = ""; }; 76 | 78F3A0E078F4118000000001 /* SampleCode.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = SampleCode.xcconfig; path = Configuration/SampleCode.xcconfig; sourceTree = ""; }; 77 | 7B1474507B1471A000000001 /* LICENSE.txt */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = LICENSE.txt; sourceTree = ""; }; 78 | /* End PBXFileReference section */ 79 | 80 | /* Begin PBXFrameworksBuildPhase section */ 81 | 51F7FFB9209BC1C40017E288 /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | 4EC915922440394E004F6B2A /* ModelIO.framework in Frameworks */, 86 | 51F7FFBA209BC1C40017E288 /* MetalPerformanceShaders.framework in Frameworks */, 87 | ); 88 | runOnlyForDeploymentPostprocessing = 0; 89 | }; 90 | 51F7FFCC209BC1C60017E288 /* Frameworks */ = { 91 | isa = PBXFrameworksBuildPhase; 92 | buildActionMask = 2147483647; 93 | files = ( 94 | 4EC9159024403945004F6B2A /* ModelIO.framework in Frameworks */, 95 | 51F7FFCD209BC1C60017E288 /* MetalPerformanceShaders.framework in Frameworks */, 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | /* End PBXFrameworksBuildPhase section */ 100 | 101 | /* Begin PBXGroup section */ 102 | 51E97F632016FC6600D09D13 = { 103 | isa = PBXGroup; 104 | children = ( 105 | 3E9448D520BC6BF0001DD43A /* README.md */, 106 | 51E97F682016FC6600D09D13 /* MPSPathTracingSample */, 107 | 51E97F732016FC6700D09D13 /* Products */, 108 | 51E97FA42017014100D09D13 /* Frameworks */, 109 | 78F3FEB078F379B000000001 /* Configuration */, 110 | 7B146B407B146E0000000001 /* LICENSE */, 111 | ); 112 | sourceTree = ""; 113 | }; 114 | 51E97F682016FC6600D09D13 /* MPSPathTracingSample */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 4EE7DAD4261ED90F00BF6A62 /* crate.jpg */, 118 | 4E289C542439C06E00069BF8 /* mario.obj */, 119 | 4E289C552439C06E00069BF8 /* mario.png */, 120 | 51F7FFDB209BC3520017E288 /* Renderer.h */, 121 | 51F7FFE0209BC3520017E288 /* Renderer.mm */, 122 | 51C2956020A81D3300F951BE /* Scene.h */, 123 | 51C2956120A81D5500F951BE /* Scene.mm */, 124 | 51F7FFE2209BC3530017E288 /* Shaders.metal */, 125 | 51F7FFDF209BC3520017E288 /* ShaderTypes.h */, 126 | 51F7FFDE209BC3520017E288 /* Transforms.h */, 127 | 51F7FFE1209BC3530017E288 /* Transforms.mm */, 128 | 51F7FFEA209BC36D0017E288 /* iOS */, 129 | 51F7FFEB209BC37E0017E288 /* macOS */, 130 | ); 131 | path = MPSPathTracingSample; 132 | sourceTree = ""; 133 | }; 134 | 51E97F732016FC6700D09D13 /* Products */ = { 135 | isa = PBXGroup; 136 | children = ( 137 | 51F7FFC2209BC1C40017E288 /* MPSPathTracingSample-iOS.app */, 138 | 51F7FFD4209BC1C60017E288 /* MPSPathTracingSample-macOS.app */, 139 | ); 140 | name = Products; 141 | sourceTree = ""; 142 | }; 143 | 51E97FA42017014100D09D13 /* Frameworks */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 4EC9158F24403944004F6B2A /* ModelIO.framework */, 147 | 4EC915912440394E004F6B2A /* ModelIO.framework */, 148 | 51564716205B0E11006AF627 /* MetalPerformanceShaders.framework */, 149 | 51564714205B0E0B006AF627 /* Metal.framework */, 150 | 51E97FA72017014A00D09D13 /* MetalPerformanceShaders.framework */, 151 | 51E97FA52017014200D09D13 /* MetalPerformanceShaders.framework */, 152 | ); 153 | name = Frameworks; 154 | sourceTree = ""; 155 | }; 156 | 51F7FFEA209BC36D0017E288 /* iOS */ = { 157 | isa = PBXGroup; 158 | children = ( 159 | 51F7FFEF209BC3B70017E288 /* AppDelegate.h */, 160 | 51F7FFEE209BC3B60017E288 /* AppDelegate.m */, 161 | 51F7FFED209BC3B60017E288 /* GameViewController.h */, 162 | 51F7FFF0209BC3B70017E288 /* GameViewController.m */, 163 | 51F7FFF8209BC3C00017E288 /* Main.storyboard */, 164 | 51F7FFF6209BC3C00017E288 /* LaunchScreen.storyboard */, 165 | 51F7FFF1209BC3B70017E288 /* Info.plist */, 166 | 51F7FFEC209BC3B60017E288 /* main.m */, 167 | ); 168 | path = iOS; 169 | sourceTree = ""; 170 | }; 171 | 51F7FFEB209BC37E0017E288 /* macOS */ = { 172 | isa = PBXGroup; 173 | children = ( 174 | 51F7FFFE209BC3E90017E288 /* AppDelegate.h */, 175 | 51F7FFFD209BC3E90017E288 /* AppDelegate.m */, 176 | 51F7FFFF209BC3E90017E288 /* GameViewController.h */, 177 | 51F7FFFC209BC3E90017E288 /* GameViewController.m */, 178 | 51F70006209BC3F00017E288 /* Main.storyboard */, 179 | 51F70000209BC3E90017E288 /* Info.plist */, 180 | 51F70001209BC3E90017E288 /* main.m */, 181 | ); 182 | path = macOS; 183 | sourceTree = ""; 184 | }; 185 | 78F3FEB078F379B000000001 /* Configuration */ = { 186 | isa = PBXGroup; 187 | children = ( 188 | 78F3A0E078F4118000000001 /* SampleCode.xcconfig */, 189 | ); 190 | name = Configuration; 191 | sourceTree = ""; 192 | }; 193 | 7B146B407B146E0000000001 /* LICENSE */ = { 194 | isa = PBXGroup; 195 | children = ( 196 | 7B1474507B1471A000000001 /* LICENSE.txt */, 197 | ); 198 | path = LICENSE; 199 | sourceTree = ""; 200 | }; 201 | /* End PBXGroup section */ 202 | 203 | /* Begin PBXNativeTarget section */ 204 | 51F7FFB1209BC1C40017E288 /* MPSPathTracingSample-iOS */ = { 205 | isa = PBXNativeTarget; 206 | buildConfigurationList = 51F7FFBF209BC1C40017E288 /* Build configuration list for PBXNativeTarget "MPSPathTracingSample-iOS" */; 207 | buildPhases = ( 208 | 51F7FFB2209BC1C40017E288 /* Sources */, 209 | 51F7FFB9209BC1C40017E288 /* Frameworks */, 210 | 51F7FFBB209BC1C40017E288 /* Resources */, 211 | ); 212 | buildRules = ( 213 | ); 214 | dependencies = ( 215 | ); 216 | name = "MPSPathTracingSample-iOS"; 217 | productName = "SimpleRenderingSample-iOS"; 218 | productReference = 51F7FFC2209BC1C40017E288 /* MPSPathTracingSample-iOS.app */; 219 | productType = "com.apple.product-type.application"; 220 | }; 221 | 51F7FFC4209BC1C60017E288 /* MPSPathTracingSample-macOS */ = { 222 | isa = PBXNativeTarget; 223 | buildConfigurationList = 51F7FFD1209BC1C60017E288 /* Build configuration list for PBXNativeTarget "MPSPathTracingSample-macOS" */; 224 | buildPhases = ( 225 | 51F7FFC5209BC1C60017E288 /* Sources */, 226 | 51F7FFCC209BC1C60017E288 /* Frameworks */, 227 | 51F7FFCE209BC1C60017E288 /* Resources */, 228 | ); 229 | buildRules = ( 230 | ); 231 | dependencies = ( 232 | ); 233 | name = "MPSPathTracingSample-macOS"; 234 | productName = "SimpleRenderingSample-macOS"; 235 | productReference = 51F7FFD4209BC1C60017E288 /* MPSPathTracingSample-macOS.app */; 236 | productType = "com.apple.product-type.application"; 237 | }; 238 | /* End PBXNativeTarget section */ 239 | 240 | /* Begin PBXProject section */ 241 | 51E97F642016FC6600D09D13 /* Project object */ = { 242 | isa = PBXProject; 243 | attributes = { 244 | LastUpgradeCheck = 1000; 245 | ORGANIZATIONNAME = Apple; 246 | }; 247 | buildConfigurationList = 51E97F672016FC6600D09D13 /* Build configuration list for PBXProject "MPSPathTracingSample" */; 248 | compatibilityVersion = "Xcode 9.3"; 249 | developmentRegion = en; 250 | hasScannedForEncodings = 0; 251 | knownRegions = ( 252 | en, 253 | Base, 254 | ); 255 | mainGroup = 51E97F632016FC6600D09D13; 256 | productRefGroup = 51E97F732016FC6700D09D13 /* Products */; 257 | projectDirPath = ""; 258 | projectRoot = ""; 259 | targets = ( 260 | 51F7FFB1209BC1C40017E288 /* MPSPathTracingSample-iOS */, 261 | 51F7FFC4209BC1C60017E288 /* MPSPathTracingSample-macOS */, 262 | ); 263 | }; 264 | /* End PBXProject section */ 265 | 266 | /* Begin PBXResourcesBuildPhase section */ 267 | 51F7FFBB209BC1C40017E288 /* Resources */ = { 268 | isa = PBXResourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | 4E289C572439C06E00069BF8 /* mario.png in Resources */, 272 | 3E9448D620BC6BF0001DD43A /* README.md in Resources */, 273 | 51F70013209BCA010017E288 /* LaunchScreen.storyboard in Resources */, 274 | 4E289C562439C06E00069BF8 /* mario.obj in Resources */, 275 | 51F70012209BCA010017E288 /* Main.storyboard in Resources */, 276 | 4EE7DAD5261ED90F00BF6A62 /* crate.jpg in Resources */, 277 | ); 278 | runOnlyForDeploymentPostprocessing = 0; 279 | }; 280 | 51F7FFCE209BC1C60017E288 /* Resources */ = { 281 | isa = PBXResourcesBuildPhase; 282 | buildActionMask = 2147483647; 283 | files = ( 284 | 4E289C5C2439C94000069BF8 /* mario.png in Resources */, 285 | 4EE7DAD8261ED97000BF6A62 /* crate.jpg in Resources */, 286 | 4E289C5B2439C93C00069BF8 /* mario.obj in Resources */, 287 | 51F7001A209BCA180017E288 /* Main.storyboard in Resources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | /* End PBXResourcesBuildPhase section */ 292 | 293 | /* Begin PBXSourcesBuildPhase section */ 294 | 51F7FFB2209BC1C40017E288 /* Sources */ = { 295 | isa = PBXSourcesBuildPhase; 296 | buildActionMask = 2147483647; 297 | files = ( 298 | 51F7000B209BC4040017E288 /* Shaders.metal in Sources */, 299 | 51F70015209BCA0B0017E288 /* GameViewController.m in Sources */, 300 | 51F7000C209BC4040017E288 /* Transforms.mm in Sources */, 301 | 51F70014209BCA0B0017E288 /* AppDelegate.m in Sources */, 302 | 51F7000A209BC4040017E288 /* Renderer.mm in Sources */, 303 | 51C2956220A81D5500F951BE /* Scene.mm in Sources */, 304 | 51F70016209BCA0B0017E288 /* main.m in Sources */, 305 | ); 306 | runOnlyForDeploymentPostprocessing = 0; 307 | }; 308 | 51F7FFC5209BC1C60017E288 /* Sources */ = { 309 | isa = PBXSourcesBuildPhase; 310 | buildActionMask = 2147483647; 311 | files = ( 312 | 51F7000E209BC4050017E288 /* Shaders.metal in Sources */, 313 | 51F70018209BCA110017E288 /* GameViewController.m in Sources */, 314 | 51F7000F209BC4050017E288 /* Transforms.mm in Sources */, 315 | 51F70017209BCA110017E288 /* AppDelegate.m in Sources */, 316 | 51F7000D209BC4050017E288 /* Renderer.mm in Sources */, 317 | 51C2956320A81D5500F951BE /* Scene.mm in Sources */, 318 | 51F70019209BCA110017E288 /* main.m in Sources */, 319 | ); 320 | runOnlyForDeploymentPostprocessing = 0; 321 | }; 322 | /* End PBXSourcesBuildPhase section */ 323 | 324 | /* Begin PBXVariantGroup section */ 325 | 51F70006209BC3F00017E288 /* Main.storyboard */ = { 326 | isa = PBXVariantGroup; 327 | children = ( 328 | 51F70007209BC3F00017E288 /* Base */, 329 | ); 330 | name = Main.storyboard; 331 | sourceTree = ""; 332 | }; 333 | 51F7FFF6209BC3C00017E288 /* LaunchScreen.storyboard */ = { 334 | isa = PBXVariantGroup; 335 | children = ( 336 | 51F7FFF7209BC3C00017E288 /* Base */, 337 | ); 338 | name = LaunchScreen.storyboard; 339 | sourceTree = ""; 340 | }; 341 | 51F7FFF8209BC3C00017E288 /* Main.storyboard */ = { 342 | isa = PBXVariantGroup; 343 | children = ( 344 | 51F7FFF9209BC3C00017E288 /* Base */, 345 | ); 346 | name = Main.storyboard; 347 | sourceTree = ""; 348 | }; 349 | /* End PBXVariantGroup section */ 350 | 351 | /* Begin XCBuildConfiguration section */ 352 | 51E97F9C2016FC6700D09D13 /* Debug */ = { 353 | isa = XCBuildConfiguration; 354 | baseConfigurationReference = 78F3A0E078F4118000000001 /* SampleCode.xcconfig */; 355 | buildSettings = { 356 | ALWAYS_SEARCH_USER_PATHS = NO; 357 | CLANG_ANALYZER_NONNULL = YES; 358 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 359 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 360 | CLANG_CXX_LIBRARY = "libc++"; 361 | CLANG_ENABLE_MODULES = YES; 362 | CLANG_ENABLE_OBJC_ARC = YES; 363 | CLANG_ENABLE_OBJC_WEAK = YES; 364 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 365 | CLANG_WARN_BOOL_CONVERSION = YES; 366 | CLANG_WARN_COMMA = YES; 367 | CLANG_WARN_CONSTANT_CONVERSION = YES; 368 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 369 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 370 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 371 | CLANG_WARN_EMPTY_BODY = YES; 372 | CLANG_WARN_ENUM_CONVERSION = YES; 373 | CLANG_WARN_INFINITE_RECURSION = YES; 374 | CLANG_WARN_INT_CONVERSION = YES; 375 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 376 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 377 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 378 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 379 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 380 | CLANG_WARN_STRICT_PROTOTYPES = YES; 381 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 382 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | COPY_PHASE_STRIP = NO; 386 | DEBUG_INFORMATION_FORMAT = dwarf; 387 | ENABLE_STRICT_OBJC_MSGSEND = YES; 388 | ENABLE_TESTABILITY = YES; 389 | GCC_C_LANGUAGE_STANDARD = gnu11; 390 | GCC_DYNAMIC_NO_PIC = NO; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_OPTIMIZATION_LEVEL = 0; 393 | GCC_PREPROCESSOR_DEFINITIONS = ( 394 | "DEBUG=1", 395 | "$(inherited)", 396 | ); 397 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 398 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 399 | GCC_WARN_UNDECLARED_SELECTOR = YES; 400 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 401 | GCC_WARN_UNUSED_FUNCTION = YES; 402 | GCC_WARN_UNUSED_VARIABLE = YES; 403 | MTL_ENABLE_DEBUG_INFO = YES; 404 | ONLY_ACTIVE_ARCH = YES; 405 | }; 406 | name = Debug; 407 | }; 408 | 51E97F9D2016FC6700D09D13 /* Release */ = { 409 | isa = XCBuildConfiguration; 410 | baseConfigurationReference = 78F3A0E078F4118000000001 /* SampleCode.xcconfig */; 411 | buildSettings = { 412 | ALWAYS_SEARCH_USER_PATHS = NO; 413 | CLANG_ANALYZER_NONNULL = YES; 414 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 415 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 416 | CLANG_CXX_LIBRARY = "libc++"; 417 | CLANG_ENABLE_MODULES = YES; 418 | CLANG_ENABLE_OBJC_ARC = YES; 419 | CLANG_ENABLE_OBJC_WEAK = YES; 420 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 421 | CLANG_WARN_BOOL_CONVERSION = YES; 422 | CLANG_WARN_COMMA = YES; 423 | CLANG_WARN_CONSTANT_CONVERSION = YES; 424 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 425 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 426 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 427 | CLANG_WARN_EMPTY_BODY = YES; 428 | CLANG_WARN_ENUM_CONVERSION = YES; 429 | CLANG_WARN_INFINITE_RECURSION = YES; 430 | CLANG_WARN_INT_CONVERSION = YES; 431 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 432 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 433 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 434 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 435 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 436 | CLANG_WARN_STRICT_PROTOTYPES = YES; 437 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 438 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 439 | CLANG_WARN_UNREACHABLE_CODE = YES; 440 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 441 | COPY_PHASE_STRIP = NO; 442 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 443 | ENABLE_NS_ASSERTIONS = NO; 444 | ENABLE_STRICT_OBJC_MSGSEND = YES; 445 | GCC_C_LANGUAGE_STANDARD = gnu11; 446 | GCC_NO_COMMON_BLOCKS = YES; 447 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 448 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 449 | GCC_WARN_UNDECLARED_SELECTOR = YES; 450 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 451 | GCC_WARN_UNUSED_FUNCTION = YES; 452 | GCC_WARN_UNUSED_VARIABLE = YES; 453 | MTL_ENABLE_DEBUG_INFO = NO; 454 | }; 455 | name = Release; 456 | }; 457 | 51F7FFC0209BC1C40017E288 /* Debug */ = { 458 | isa = XCBuildConfiguration; 459 | baseConfigurationReference = 78F3A0E078F4118000000001 /* SampleCode.xcconfig */; 460 | buildSettings = { 461 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 462 | CODE_SIGN_IDENTITY = "iPhone Developer"; 463 | CODE_SIGN_STYLE = Automatic; 464 | DEVELOPMENT_TEAM = ""; 465 | INFOPLIST_FILE = MPSPathTracingSample/iOS/Info.plist; 466 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 467 | LD_RUNPATH_SEARCH_PATHS = ( 468 | "$(inherited)", 469 | "@executable_path/Frameworks", 470 | ); 471 | PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.SimpleRenderingSample${SAMPLE_CODE_DISAMBIGUATOR}"; 472 | PRODUCT_NAME = "$(TARGET_NAME)"; 473 | PROVISIONING_PROFILE_SPECIFIER = ""; 474 | SDKROOT = iphoneos; 475 | TARGETED_DEVICE_FAMILY = "1,2"; 476 | }; 477 | name = Debug; 478 | }; 479 | 51F7FFC1209BC1C40017E288 /* Release */ = { 480 | isa = XCBuildConfiguration; 481 | baseConfigurationReference = 78F3A0E078F4118000000001 /* SampleCode.xcconfig */; 482 | buildSettings = { 483 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 484 | CODE_SIGN_IDENTITY = "iPhone Developer"; 485 | CODE_SIGN_STYLE = Automatic; 486 | DEVELOPMENT_TEAM = ""; 487 | INFOPLIST_FILE = MPSPathTracingSample/iOS/Info.plist; 488 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 489 | LD_RUNPATH_SEARCH_PATHS = ( 490 | "$(inherited)", 491 | "@executable_path/Frameworks", 492 | ); 493 | PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.SimpleRenderingSample${SAMPLE_CODE_DISAMBIGUATOR}"; 494 | PRODUCT_NAME = "$(TARGET_NAME)"; 495 | PROVISIONING_PROFILE_SPECIFIER = ""; 496 | SDKROOT = iphoneos; 497 | TARGETED_DEVICE_FAMILY = "1,2"; 498 | VALIDATE_PRODUCT = YES; 499 | }; 500 | name = Release; 501 | }; 502 | 51F7FFD2209BC1C60017E288 /* Debug */ = { 503 | isa = XCBuildConfiguration; 504 | baseConfigurationReference = 78F3A0E078F4118000000001 /* SampleCode.xcconfig */; 505 | buildSettings = { 506 | CODE_SIGN_IDENTITY = "-"; 507 | CODE_SIGN_STYLE = Automatic; 508 | COMBINE_HIDPI_IMAGES = YES; 509 | DEVELOPMENT_TEAM = 25QRARVF8V; 510 | INFOPLIST_FILE = MPSPathTracingSample/macOS/Info.plist; 511 | LD_RUNPATH_SEARCH_PATHS = ( 512 | "$(inherited)", 513 | "@executable_path/../Frameworks", 514 | ); 515 | MACOSX_DEPLOYMENT_TARGET = 10.14; 516 | PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.SimpleRenderingSample${SAMPLE_CODE_DISAMBIGUATOR}"; 517 | PRODUCT_NAME = "$(TARGET_NAME)"; 518 | PROVISIONING_PROFILE_SPECIFIER = ""; 519 | SDKROOT = macosx; 520 | }; 521 | name = Debug; 522 | }; 523 | 51F7FFD3209BC1C60017E288 /* Release */ = { 524 | isa = XCBuildConfiguration; 525 | baseConfigurationReference = 78F3A0E078F4118000000001 /* SampleCode.xcconfig */; 526 | buildSettings = { 527 | CODE_SIGN_IDENTITY = "-"; 528 | CODE_SIGN_STYLE = Automatic; 529 | COMBINE_HIDPI_IMAGES = YES; 530 | DEVELOPMENT_TEAM = 25QRARVF8V; 531 | INFOPLIST_FILE = MPSPathTracingSample/macOS/Info.plist; 532 | LD_RUNPATH_SEARCH_PATHS = ( 533 | "$(inherited)", 534 | "@executable_path/../Frameworks", 535 | ); 536 | MACOSX_DEPLOYMENT_TARGET = 10.14; 537 | PRODUCT_BUNDLE_IDENTIFIER = "com.example.apple-samplecode.SimpleRenderingSample${SAMPLE_CODE_DISAMBIGUATOR}"; 538 | PRODUCT_NAME = "$(TARGET_NAME)"; 539 | PROVISIONING_PROFILE_SPECIFIER = ""; 540 | SDKROOT = macosx; 541 | }; 542 | name = Release; 543 | }; 544 | /* End XCBuildConfiguration section */ 545 | 546 | /* Begin XCConfigurationList section */ 547 | 51E97F672016FC6600D09D13 /* Build configuration list for PBXProject "MPSPathTracingSample" */ = { 548 | isa = XCConfigurationList; 549 | buildConfigurations = ( 550 | 51E97F9C2016FC6700D09D13 /* Debug */, 551 | 51E97F9D2016FC6700D09D13 /* Release */, 552 | ); 553 | defaultConfigurationIsVisible = 0; 554 | defaultConfigurationName = Release; 555 | }; 556 | 51F7FFBF209BC1C40017E288 /* Build configuration list for PBXNativeTarget "MPSPathTracingSample-iOS" */ = { 557 | isa = XCConfigurationList; 558 | buildConfigurations = ( 559 | 51F7FFC0209BC1C40017E288 /* Debug */, 560 | 51F7FFC1209BC1C40017E288 /* Release */, 561 | ); 562 | defaultConfigurationIsVisible = 0; 563 | defaultConfigurationName = Release; 564 | }; 565 | 51F7FFD1209BC1C60017E288 /* Build configuration list for PBXNativeTarget "MPSPathTracingSample-macOS" */ = { 566 | isa = XCConfigurationList; 567 | buildConfigurations = ( 568 | 51F7FFD2209BC1C60017E288 /* Debug */, 569 | 51F7FFD3209BC1C60017E288 /* Release */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | /* End XCConfigurationList section */ 575 | }; 576 | rootObject = 51E97F642016FC6600D09D13 /* Project object */; 577 | } 578 | -------------------------------------------------------------------------------- /MPSPathTracingSample.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Latest 7 | 8 | 9 | -------------------------------------------------------------------------------- /MPSPathTracingSample/Renderer.h: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Header for platform independent renderer class 6 | */ 7 | 8 | #import 9 | 10 | // Our platform independent renderer class. Implements the MTKViewDelegate protocol which 11 | // allows it to accept per-frame update and drawable resize callbacks. 12 | @interface Renderer : NSObject 13 | 14 | -(nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)view; 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /MPSPathTracingSample/Renderer.mm: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Implementation for platform independent renderer class 6 | */ 7 | 8 | #import 9 | #import 10 | #import 11 | #import 12 | 13 | #import "Renderer.h" 14 | #import "Transforms.h" 15 | #import "ShaderTypes.h" 16 | #import "Scene.h" 17 | 18 | using namespace simd; 19 | 20 | static const NSUInteger maxFramesInFlight = 3; 21 | static const size_t alignedUniformsSize = (sizeof(Uniforms) + 255) & ~255; 22 | 23 | static const size_t rayStride = 48; 24 | static const size_t intersectionStride = sizeof(MPSIntersectionDistancePrimitiveIndexCoordinates); 25 | 26 | @implementation Renderer 27 | { 28 | MTKView *_view; 29 | id _device; 30 | id _queue; 31 | id _library; 32 | 33 | MPSTriangleAccelerationStructure *_accelerationStructure; 34 | MPSRayIntersector *_intersector; 35 | 36 | id _vertexPositionBuffer; 37 | id _vertexNormalBuffer; 38 | id _vertexColorBuffer; 39 | id _vertexTextureCoordsBuffer; 40 | id _textureIndexBuffer; 41 | id _reflectionBuffer; 42 | id _reflectionBlurBuffer; 43 | id _refractionBuffer; 44 | id _refractionIndexBuffer; 45 | 46 | id _rayBuffer; 47 | id _shadowRayBuffer; 48 | id _intersectionBuffer; 49 | id _uniformBuffer; 50 | id _triangleMaskBuffer; 51 | 52 | id _rayPipeline; 53 | id _shadePipeline; 54 | id _shadowPipeline; 55 | id _accumulatePipeline; 56 | id _copyPipeline; 57 | 58 | id _renderTargets[2]; 59 | id _accumulationTargets[2]; 60 | id _randomTexture; 61 | id _colorTexture[2]; 62 | 63 | dispatch_semaphore_t _sem; 64 | CGSize _size; 65 | NSUInteger _uniformBufferOffset; 66 | NSUInteger _uniformBufferIndex; 67 | 68 | unsigned int _frameIndex; 69 | } 70 | 71 | -(nonnull instancetype)initWithMetalKitView:(nonnull MTKView *)view; 72 | { 73 | self = [super init]; 74 | 75 | if (self) 76 | { 77 | // Metal device was created by platform-specific support code. See iOS/GameViewController.m 78 | // and macOS/GameViewController.m 79 | _view = view; 80 | _device = view.device; 81 | 82 | NSLog(@"Metal device: %@", _device.name); 83 | 84 | _sem = dispatch_semaphore_create(maxFramesInFlight); 85 | 86 | [self loadMetal]; 87 | [self createPipelines]; 88 | [self createScene]; 89 | [self createBuffers]; 90 | [self createIntersector]; 91 | } 92 | 93 | return self; 94 | } 95 | 96 | - (void)loadMetal 97 | { 98 | // Configure view 99 | _view.colorPixelFormat = MTLPixelFormatRGBA16Float; 100 | _view.sampleCount = 1; 101 | _view.drawableSize = _view.frame.size; 102 | 103 | // Create Metal shader library and command queue. Commands will be executed by GPU from this command queue. 104 | _library = [_device newDefaultLibrary]; 105 | _queue = [_device newCommandQueue]; 106 | } 107 | 108 | - (void)createPipelines 109 | { 110 | NSError *error = NULL; 111 | 112 | // Create compute pipelines will will execute code on the GPU 113 | MTLComputePipelineDescriptor *computeDescriptor = [[MTLComputePipelineDescriptor alloc] init]; 114 | 115 | // Set to YES to allow compiler to make certain optimizations 116 | computeDescriptor.threadGroupSizeIsMultipleOfThreadExecutionWidth = YES; 117 | 118 | // Generates rays according to view/projection matrices 119 | computeDescriptor.computeFunction = [_library newFunctionWithName:@"rayKernel"]; 120 | 121 | _rayPipeline = [_device newComputePipelineStateWithDescriptor:computeDescriptor 122 | options:0 123 | reflection:nil 124 | error:&error]; 125 | 126 | if (!_rayPipeline) 127 | NSLog(@"Failed to create pipeline state: %@", error); 128 | 129 | // Consumes ray/scene intersection test results to perform shading 130 | computeDescriptor.computeFunction = [_library newFunctionWithName:@"shadeKernel"]; 131 | 132 | _shadePipeline = [_device newComputePipelineStateWithDescriptor:computeDescriptor 133 | options:0 134 | reflection:nil 135 | error:&error]; 136 | 137 | if (!_shadePipeline) 138 | NSLog(@"Failed to create pipeline state: %@", error); 139 | 140 | // Consumes shadow ray intersection tests to update the output image 141 | computeDescriptor.computeFunction = [_library newFunctionWithName:@"shadowKernel"]; 142 | 143 | _shadowPipeline = [_device newComputePipelineStateWithDescriptor:computeDescriptor 144 | options:0 145 | reflection:nil 146 | error:&error]; 147 | 148 | if (!_shadowPipeline) 149 | NSLog(@"Failed to create pipeline state: %@", error); 150 | 151 | // Averages the current frame's output image with all previous frames 152 | computeDescriptor.computeFunction = [_library newFunctionWithName:@"accumulateKernel"]; 153 | 154 | _accumulatePipeline = [_device newComputePipelineStateWithDescriptor:computeDescriptor 155 | options:0 156 | reflection:nil 157 | error:&error]; 158 | 159 | if (!_accumulatePipeline) 160 | NSLog(@"Failed to create pipeline state: %@", error); 161 | 162 | // Copies rendered scene into the MTKView 163 | MTLRenderPipelineDescriptor *renderDescriptor = [[MTLRenderPipelineDescriptor alloc] init]; 164 | renderDescriptor.sampleCount = _view.sampleCount; 165 | renderDescriptor.vertexFunction = [_library newFunctionWithName:@"copyVertex"]; 166 | renderDescriptor.fragmentFunction = [_library newFunctionWithName:@"copyFragment"]; 167 | renderDescriptor.colorAttachments[0].pixelFormat = _view.colorPixelFormat; 168 | 169 | _copyPipeline = [_device newRenderPipelineStateWithDescriptor:renderDescriptor error:&error]; 170 | 171 | if (!_copyPipeline) 172 | NSLog(@"Failed to create pipeline state, error %@", error); 173 | } 174 | 175 | - (void)createScene 176 | { 177 | float4x4 transform = matrix4x4_translation(0.0f, 1.0f, 0.0f) * 178 | matrix4x4_scale(0.5f, 1.98f, 0.5f); 179 | 180 | // Light source 181 | createCube(FACE_MASK_POSITIVE_Y, vector3(1.0f, 1.0f, 1.0f), transform, true, 0, 0.0f, 0.0f, 0.0f, 0.0f, TRIANGLE_MASK_LIGHT); 182 | 183 | transform = matrix4x4_translation(0.0f, 1.0f, 0.0f) * matrix4x4_scale(2.0f, 2.0f, 2.0f); 184 | 185 | // Top, bottom, and back walls 186 | createCube(FACE_MASK_NEGATIVE_Y | FACE_MASK_POSITIVE_Y | FACE_MASK_NEGATIVE_Z, vector3(0.725f, 0.71f, 0.68f), transform, true, false, 0.0f, 0.0f, 0.0f, 0.0f, TRIANGLE_MASK_GEOMETRY); 187 | 188 | // Left wall 189 | createCube(FACE_MASK_NEGATIVE_X, vector3(0.63f, 0.065f, 0.05f), transform, true, 0, 0.0f, 0.0f, 0.0f, 0.0f, TRIANGLE_MASK_GEOMETRY); 190 | 191 | // Right wall 192 | createCube(FACE_MASK_POSITIVE_X, vector3(0.14f, 0.45f, 0.091f), transform, true, 0, 0.0f, 0.0f, 0.0f, 0.0f, TRIANGLE_MASK_GEOMETRY); 193 | 194 | transform = matrix4x4_translation(0.3275f, 0.3f, 0.0f) * 195 | matrix4x4_rotation(-0.3f, vector3(0.0f, 1.0f, 0.0f)) * 196 | matrix4x4_scale(0.6f, 0.6f, 0.6f); 197 | 198 | // Short box 199 | createCube(FACE_MASK_ALL, vector3(0.725f, 0.725f, 0.725f), transform, false, 1, 0.0f, 0.0f, 0.0f, 0.0f, TRIANGLE_MASK_GEOMETRY); 200 | 201 | transform = matrix4x4_translation(-0.335f, 0.6f, -0.29f) * 202 | matrix4x4_rotation(0.3f, vector3(0.0f, 1.0f, 0.0f)) * 203 | matrix4x4_scale(0.6f, 1.2f, 0.6f); 204 | 205 | // Tall box 206 | createCube(FACE_MASK_ALL, vector3(0.725f, 0.71f, 0.68f), transform, false, 0, 0.0f, 0.0f, 0.0f, 0.0f, TRIANGLE_MASK_GEOMETRY); 207 | 208 | transform = matrix4x4_translation(0.3275f, 0.7f, 0.6f) * 209 | matrix4x4_rotation(1.9f, vector3(1.0f, 0.0f, 0.0f)) * 210 | matrix4x4_scale(0.25f, 0.25f, 0.25f); 211 | 212 | createSphere(vector3(1.0f, 0.5f, 1.0f), transform, 0, 0.0f, 0.0f, 1.0f, 1.1f, TRIANGLE_MASK_GEOMETRY); 213 | 214 | transform = matrix4x4_translation(-0.3275f, 0.7f, 0.3f) * 215 | matrix4x4_rotation(1.9f, vector3(1.0f, 0.0f, 0.0f)) * 216 | matrix4x4_scale(0.25f, 0.25f, 0.25f); 217 | 218 | createSphere(vector3(1.0f, 1.0f, 1.0f), transform, 0, 1.0f, 0.0f, 0.0f, 0.0f, TRIANGLE_MASK_GEOMETRY); 219 | 220 | transform = matrix4x4_translation(0.3275f, 0.6f, -0.1) * 221 | matrix4x4_rotation(0.01f, vector3(1.0f, 0.0f, 0.0f)) * 222 | matrix4x4_scale(0.01f, 0.01f, 0.01f); 223 | 224 | NSString* path = [[NSBundle mainBundle] pathForResource:@"mario" ofType:@"obj"]; 225 | NSURL *url = [NSURL fileURLWithPath:path]; 226 | MDLAsset *asset = [[MDLAsset alloc] initWithURL:url]; 227 | MDLMesh *mesh = (MDLMesh*)[asset objectAtIndex:0]; 228 | createMesh(mesh, vector3(1.0f, 1.0f, 1.0f), transform, 2, 0.0f, 0.0f, 0.0f, 0.0f, TRIANGLE_MASK_GEOMETRY); 229 | } 230 | 231 | - (void)createBuffers 232 | { 233 | // Uniform buffer contains a few small values which change from frame to frame. We will have up to 3 234 | // frames in flight at once, so allocate a range of the buffer for each frame. The GPU will read from 235 | // one chunk while the CPU writes to the next chunk. Each chunk must be aligned to 256 bytes on macOS 236 | // and 16 bytes on iOS. 237 | NSUInteger uniformBufferSize = alignedUniformsSize * maxFramesInFlight; 238 | 239 | // Vertex data should be stored in private or managed buffers on discrete GPU systems (AMD, NVIDIA). 240 | // Private buffers are stored entirely in GPU memory and cannot be accessed by the CPU. Managed 241 | // buffers maintain a copy in CPU memory and a copy in GPU memory. 242 | MTLResourceOptions options = 0; 243 | 244 | #if !TARGET_OS_IPHONE 245 | options = MTLResourceStorageModeManaged; 246 | #else 247 | options = MTLResourceStorageModeShared; 248 | #endif 249 | 250 | _uniformBuffer = [_device newBufferWithLength:uniformBufferSize options:options]; 251 | 252 | // Allocate buffers for vertex positions, colors, and normals. Note that each vertex position is a 253 | // float3, which is a 16 byte aligned type. 254 | _vertexPositionBuffer = [_device newBufferWithLength:vertices.size() * sizeof(float3) options:options]; 255 | _vertexColorBuffer = [_device newBufferWithLength:colors.size() * sizeof(float3) options:options]; 256 | _vertexNormalBuffer = [_device newBufferWithLength:normals.size() * sizeof(float3) options:options]; 257 | _vertexTextureCoordsBuffer = [_device newBufferWithLength:textureCoords.size() * sizeof(float2) options:options]; 258 | _textureIndexBuffer = [_device newBufferWithLength:textureIndices.size() * sizeof(uint32_t) options:options]; 259 | _reflectionBuffer = [_device newBufferWithLength:reflections.size() * sizeof(float1) options:options]; 260 | _reflectionBlurBuffer = [_device newBufferWithLength:reflectionBlurs.size() * sizeof(float1) options:options]; 261 | _refractionBuffer = [_device newBufferWithLength:refractions.size() * sizeof(float1) options:options]; 262 | _refractionIndexBuffer = [_device newBufferWithLength:refractionIndices.size() * sizeof(float1) options:options]; 263 | _triangleMaskBuffer = [_device newBufferWithLength:masks.size() * sizeof(uint32_t) options:options]; 264 | 265 | // Copy vertex data into buffers 266 | memcpy(_vertexPositionBuffer.contents, &vertices[0], _vertexPositionBuffer.length); 267 | memcpy(_vertexColorBuffer.contents, &colors[0], _vertexColorBuffer.length); 268 | memcpy(_vertexNormalBuffer.contents, &normals[0], _vertexNormalBuffer.length); 269 | memcpy(_vertexTextureCoordsBuffer.contents, &textureCoords[0], _vertexTextureCoordsBuffer.length); 270 | memcpy(_textureIndexBuffer.contents, &textureIndices[0], _textureIndexBuffer.length); 271 | memcpy(_reflectionBuffer.contents, &reflections[0], _reflectionBuffer.length); 272 | memcpy(_reflectionBlurBuffer.contents, &reflectionBlurs[0], _reflectionBlurBuffer.length); 273 | memcpy(_refractionBuffer.contents, &refractions[0], _refractionBuffer.length); 274 | memcpy(_refractionIndexBuffer.contents, &refractionIndices[0], _refractionIndexBuffer.length); 275 | memcpy(_triangleMaskBuffer.contents, &masks[0], _triangleMaskBuffer.length); 276 | 277 | // When using managed buffers, we need to indicate that we modified the buffer so that the GPU 278 | // copy can be updated 279 | #if !TARGET_OS_IPHONE 280 | [_vertexPositionBuffer didModifyRange:NSMakeRange(0, _vertexPositionBuffer.length)]; 281 | [_vertexColorBuffer didModifyRange:NSMakeRange(0, _vertexColorBuffer.length)]; 282 | [_vertexNormalBuffer didModifyRange:NSMakeRange(0, _vertexNormalBuffer.length)]; 283 | [_vertexTextureCoordsBuffer didModifyRange:NSMakeRange(0, _vertexTextureCoordsBuffer.length)]; 284 | [_textureIndexBuffer didModifyRange:NSMakeRange(0, _textureIndexBuffer.length)]; 285 | [_reflectionBuffer didModifyRange:NSMakeRange(0, _reflectionBuffer.length)]; 286 | [_reflectionBlurBuffer didModifyRange:NSMakeRange(0, _reflectionBlurBuffer.length)]; 287 | [_refractionBuffer didModifyRange:NSMakeRange(0, _refractionBuffer.length)]; 288 | [_refractionIndexBuffer didModifyRange:NSMakeRange(0, _refractionIndexBuffer.length)]; 289 | [_triangleMaskBuffer didModifyRange:NSMakeRange(0, _triangleMaskBuffer.length)]; 290 | #endif 291 | } 292 | 293 | - (void)createIntersector 294 | { 295 | // Create a raytracer for our Metal device 296 | _intersector = [[MPSRayIntersector alloc] initWithDevice:_device]; 297 | 298 | _intersector.rayDataType = MPSRayDataTypeOriginMaskDirectionMaxDistance; 299 | _intersector.rayStride = rayStride; 300 | _intersector.rayMaskOptions = MPSRayMaskOptionPrimitive; 301 | 302 | // Create an acceleration structure from our vertex position data 303 | _accelerationStructure = [[MPSTriangleAccelerationStructure alloc] initWithDevice:_device]; 304 | 305 | _accelerationStructure.vertexBuffer = _vertexPositionBuffer; 306 | _accelerationStructure.maskBuffer = _triangleMaskBuffer; 307 | _accelerationStructure.triangleCount = vertices.size() / 3; 308 | 309 | [_accelerationStructure rebuild]; 310 | } 311 | 312 | - (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size 313 | { 314 | _size = size; 315 | 316 | // Handle window size changes by allocating a buffer large enough to contain one standard ray, 317 | // one shadow ray, and one ray/triangle intersection result per pixel 318 | NSUInteger rayCount = (NSUInteger)_size.width * (NSUInteger)_size.height; 319 | 320 | // We use private buffers here because rays and intersection results will be entirely produced 321 | // and consumed on the GPU 322 | _rayBuffer = [_device newBufferWithLength:rayStride * rayCount options:MTLResourceStorageModePrivate]; 323 | _shadowRayBuffer = [_device newBufferWithLength:rayStride * rayCount options:MTLResourceStorageModePrivate]; 324 | _intersectionBuffer = [_device newBufferWithLength:intersectionStride * rayCount options:MTLResourceStorageModePrivate]; 325 | 326 | // Create a render target which the shading kernel can write to 327 | MTLTextureDescriptor *renderTargetDescriptor = [[MTLTextureDescriptor alloc] init]; 328 | 329 | renderTargetDescriptor.pixelFormat = MTLPixelFormatRGBA32Float; 330 | renderTargetDescriptor.textureType = MTLTextureType2D; 331 | renderTargetDescriptor.width = size.width; 332 | renderTargetDescriptor.height = size.height; 333 | 334 | // Stored in private memory because it will only be read and written from the GPU 335 | renderTargetDescriptor.storageMode = MTLStorageModePrivate; 336 | 337 | // Indicate that we will read and write the texture from the GPU 338 | renderTargetDescriptor.usage = MTLTextureUsageShaderRead | MTLTextureUsageShaderWrite; 339 | 340 | for (NSUInteger i = 0; i < 2; i++) { 341 | _renderTargets[i] = [_device newTextureWithDescriptor:renderTargetDescriptor]; 342 | _accumulationTargets[i] = [_device newTextureWithDescriptor:renderTargetDescriptor]; 343 | } 344 | 345 | renderTargetDescriptor.pixelFormat = MTLPixelFormatR32Uint; 346 | renderTargetDescriptor.usage = MTLTextureUsageShaderRead; 347 | #if !TARGET_OS_IPHONE 348 | renderTargetDescriptor.storageMode = MTLStorageModeManaged; 349 | #else 350 | renderTargetDescriptor.storageMode = MTLStorageModeShared; 351 | #endif 352 | 353 | // Generate a texture containing a random integer value for each pixel. This value 354 | // will be used to decorrelate pixels while drawing pseudorandom numbers from the 355 | // Halton sequence. 356 | _randomTexture = [_device newTextureWithDescriptor:renderTargetDescriptor]; 357 | 358 | uint32_t *randomValues = (uint32_t *)malloc(sizeof(uint32_t) * size.width * size.height); 359 | 360 | for (NSUInteger i = 0; i < size.width * size.height; i++) 361 | randomValues[i] = rand() % (1024 * 1024); 362 | 363 | [_randomTexture replaceRegion:MTLRegionMake2D(0, 0, size.width, size.height) 364 | mipmapLevel:0 365 | withBytes:randomValues 366 | bytesPerRow:sizeof(uint32_t) * size.width]; 367 | 368 | free(randomValues); 369 | 370 | MTKTextureLoader *loader = [[MTKTextureLoader alloc] initWithDevice: _device]; 371 | NSURL *imageURL = [[NSBundle mainBundle] URLForResource:@"crate" withExtension:@"jpg"]; 372 | _colorTexture[0] = [loader newTextureWithContentsOfURL:imageURL options:nil error:nil]; 373 | if(!_colorTexture[0]) 374 | NSLog(@"Failed to create the texture from %@", imageURL.absoluteString); 375 | 376 | loader = [[MTKTextureLoader alloc] initWithDevice: _device]; 377 | imageURL = [[NSBundle mainBundle] URLForResource:@"mario" withExtension:@"png"]; 378 | _colorTexture[1] = [loader newTextureWithContentsOfURL:imageURL options:nil error:nil]; 379 | if(!_colorTexture[1]) 380 | NSLog(@"Failed to create the texture from %@", imageURL.absoluteString); 381 | 382 | 383 | _frameIndex = 0; 384 | } 385 | 386 | - (void)updateUniforms 387 | { 388 | // Update this frame's uniforms 389 | _uniformBufferOffset = alignedUniformsSize * _uniformBufferIndex; 390 | 391 | Uniforms *uniforms = (Uniforms *)((char *)_uniformBuffer.contents + _uniformBufferOffset); 392 | 393 | uniforms->camera.position = vector3(0.0f, 1.0f, 3.38f); 394 | 395 | uniforms->camera.forward = vector3(0.0f, 0.0f, -1.0f); 396 | uniforms->camera.right = vector3(1.0f, 0.0f, 0.0f); 397 | uniforms->camera.up = vector3(0.0f, 1.0f, 0.0f); 398 | 399 | uniforms->light.position = vector3(0.0f, 1.98f, 0.0f); 400 | uniforms->light.forward = vector3(0.0f, -1.0f, 0.0f); 401 | uniforms->light.right = vector3(0.25f, 0.0f, 0.0f); 402 | uniforms->light.up = vector3(0.0f, 0.0f, 0.25f); 403 | uniforms->light.color = vector3(4.0f, 4.0f, 4.0f); 404 | 405 | float fieldOfView = 45.0f * (M_PI / 180.0f); 406 | float aspectRatio = (float)_size.width / (float)_size.height; 407 | float imagePlaneHeight = tanf(fieldOfView / 2.0f); 408 | float imagePlaneWidth = aspectRatio * imagePlaneHeight; 409 | 410 | uniforms->camera.right *= imagePlaneWidth; 411 | uniforms->camera.up *= imagePlaneHeight; 412 | 413 | uniforms->width = (unsigned int)_size.width; 414 | uniforms->height = (unsigned int)_size.height; 415 | 416 | uniforms->frameIndex = _frameIndex++; 417 | 418 | #if !TARGET_OS_IPHONE 419 | [_uniformBuffer didModifyRange:NSMakeRange(_uniformBufferOffset, alignedUniformsSize)]; 420 | #endif 421 | 422 | // Advance to the next slot in the uniform buffer 423 | _uniformBufferIndex = (_uniformBufferIndex + 1) % maxFramesInFlight; 424 | } 425 | 426 | - (void)drawInMTKView:(nonnull MTKView *)view 427 | { 428 | // We are using the uniform buffer to stream uniform data to the GPU, so we need to wait until the oldest 429 | // GPU frame has completed before we can reuse that space in the buffer. 430 | dispatch_semaphore_wait(_sem, DISPATCH_TIME_FOREVER); 431 | 432 | // Create a command buffer which will contain our GPU commands 433 | id commandBuffer = [_queue commandBuffer]; 434 | 435 | // When the frame has finished, signal that we can reuse the uniform buffer space from this frame. 436 | // Note that the contents of completion handlers should be as fast as possible as the GPU driver may 437 | // have other work scheduled on the underlying dispatch queue. 438 | [commandBuffer addCompletedHandler:^(id buffer) { 439 | dispatch_semaphore_signal(self->_sem); 440 | }]; 441 | 442 | [self updateUniforms]; 443 | 444 | NSUInteger width = (NSUInteger)_size.width; 445 | NSUInteger height = (NSUInteger)_size.height; 446 | 447 | // We will launch a rectangular grid of threads on the GPU to generate the rays. Threads are launched in 448 | // groups called "threadgroups". We need to align the number of threads to be a multiple of the threadgroup 449 | // size. We indicated when compiling the pipeline that the threadgroup size would be a multiple of the thread 450 | // execution width (SIMD group size) which is typically 32 or 64 so 8x8 is a safe threadgroup size which 451 | // should be small to be supported on most devices. A more advanced application would choose the threadgroup 452 | // size dynamically. 453 | MTLSize threadsPerThreadgroup = MTLSizeMake(8, 8, 1); 454 | MTLSize threadgroups = MTLSizeMake((width + threadsPerThreadgroup.width - 1) / threadsPerThreadgroup.width, 455 | (height + threadsPerThreadgroup.height - 1) / threadsPerThreadgroup.height, 456 | 1); 457 | 458 | // First, we will generate rays on the GPU. We create a compute command encoder which will be used to add 459 | // commands to the command buffer. 460 | id computeEncoder = [commandBuffer computeCommandEncoder]; 461 | 462 | // Bind buffers needed by the compute pipeline 463 | [computeEncoder setBuffer:_uniformBuffer offset:_uniformBufferOffset atIndex:0]; 464 | [computeEncoder setBuffer:_rayBuffer offset:0 atIndex:1]; 465 | 466 | [computeEncoder setTexture:_randomTexture atIndex:0]; 467 | [computeEncoder setTexture:_colorTexture[0] atIndex:1]; 468 | [computeEncoder setTexture:_colorTexture[1] atIndex:2]; 469 | [computeEncoder setTexture:_renderTargets[0] atIndex:3]; 470 | 471 | // Bind the ray generation compute pipeline 472 | [computeEncoder setComputePipelineState:_rayPipeline]; 473 | 474 | // Launch threads 475 | [computeEncoder dispatchThreadgroups:threadgroups threadsPerThreadgroup:threadsPerThreadgroup]; 476 | 477 | // End the encoder 478 | [computeEncoder endEncoding]; 479 | 480 | // We will iterate over the next few kernels several times to allow light to bounce around the scene 481 | for (int bounce = 0; bounce < 3; bounce++) { 482 | _intersector.intersectionDataType = MPSIntersectionDataTypeDistancePrimitiveIndexCoordinates; 483 | 484 | // We can then pass the rays to the MPSRayIntersector to compute the intersections with our acceleration structure 485 | [_intersector encodeIntersectionToCommandBuffer:commandBuffer // Command buffer to encode into 486 | intersectionType:MPSIntersectionTypeNearest // Intersection test type 487 | rayBuffer:_rayBuffer // Ray buffer 488 | rayBufferOffset:0 // Offset into ray buffer 489 | intersectionBuffer:_intersectionBuffer // Intersection buffer (destination) 490 | intersectionBufferOffset:0 // Offset into intersection buffer 491 | rayCount:width * height // Number of rays 492 | accelerationStructure:_accelerationStructure]; // Acceleration structure 493 | // We launch another pipeline to consume the intersection results and shade the scene 494 | computeEncoder = [commandBuffer computeCommandEncoder]; 495 | 496 | [computeEncoder setBuffer:_uniformBuffer offset:_uniformBufferOffset atIndex:0]; 497 | [computeEncoder setBuffer:_rayBuffer offset:0 atIndex:1]; 498 | [computeEncoder setBuffer:_shadowRayBuffer offset:0 atIndex:2]; 499 | [computeEncoder setBuffer:_intersectionBuffer offset:0 atIndex:3]; 500 | [computeEncoder setBuffer:_vertexColorBuffer offset:0 atIndex:4]; 501 | [computeEncoder setBuffer:_vertexNormalBuffer offset:0 atIndex:5]; 502 | [computeEncoder setBuffer:_vertexTextureCoordsBuffer offset:0 atIndex:6]; 503 | [computeEncoder setBuffer:_textureIndexBuffer offset:0 atIndex:7]; 504 | [computeEncoder setBuffer:_reflectionBuffer offset:0 atIndex:8]; 505 | [computeEncoder setBuffer:_reflectionBlurBuffer offset:0 atIndex:9]; 506 | [computeEncoder setBuffer:_refractionBuffer offset:0 atIndex:10]; 507 | [computeEncoder setBuffer:_refractionIndexBuffer offset:0 atIndex:11]; 508 | [computeEncoder setBuffer:_triangleMaskBuffer offset:0 atIndex:12]; 509 | [computeEncoder setBytes:&bounce length:sizeof(bounce) atIndex:13]; 510 | 511 | [computeEncoder setTexture:_randomTexture atIndex:0]; 512 | [computeEncoder setTexture:_colorTexture[0] atIndex:1]; 513 | [computeEncoder setTexture:_colorTexture[1] atIndex:2]; 514 | [computeEncoder setTexture:_renderTargets[0] atIndex:3]; 515 | 516 | [computeEncoder setComputePipelineState:_shadePipeline]; 517 | 518 | [computeEncoder dispatchThreadgroups:threadgroups threadsPerThreadgroup:threadsPerThreadgroup]; 519 | 520 | [computeEncoder endEncoding]; 521 | 522 | // We intersect rays with the scene, except this time we are intersecting shadow rays. We only need 523 | // to know whether the shadows rays hit anything on the way to the light source, not which triangle 524 | // was intersected. Therefore, we can use the "any" intersection type to end the intersection search 525 | // as soon as any intersection is found. This is typically much faster than finding the nearest 526 | // intersection. We can also use MPSIntersectionDataTypeDistance, because we don't need the triangle 527 | // index and barycentric coordinates. 528 | _intersector.intersectionDataType = MPSIntersectionDataTypeDistance; 529 | 530 | [_intersector encodeIntersectionToCommandBuffer:commandBuffer 531 | intersectionType:MPSIntersectionTypeAny 532 | rayBuffer:_shadowRayBuffer 533 | rayBufferOffset:0 534 | intersectionBuffer:_intersectionBuffer 535 | intersectionBufferOffset:0 536 | rayCount:width * height 537 | accelerationStructure:_accelerationStructure]; 538 | 539 | // Finally, we launch a kernel which writes the color computed by the shading kernel into the 540 | // output image, but only if the corresponding shadow ray does not intersect anything on the way to 541 | // the light. If the shadow ray intersects a triangle before reaching the light source, the original 542 | // intersection point was in shadow. 543 | computeEncoder = [commandBuffer computeCommandEncoder]; 544 | 545 | [computeEncoder setBuffer:_uniformBuffer offset:_uniformBufferOffset atIndex:0]; 546 | [computeEncoder setBuffer:_shadowRayBuffer offset:0 atIndex:1]; 547 | [computeEncoder setBuffer:_intersectionBuffer offset:0 atIndex:2]; 548 | 549 | [computeEncoder setTexture:_renderTargets[0] atIndex:0]; 550 | [computeEncoder setTexture:_renderTargets[1] atIndex:1]; 551 | 552 | [computeEncoder setComputePipelineState:_shadowPipeline]; 553 | 554 | [computeEncoder dispatchThreadgroups:threadgroups threadsPerThreadgroup:threadsPerThreadgroup]; 555 | 556 | [computeEncoder endEncoding]; 557 | 558 | std::swap(_renderTargets[0], _renderTargets[1]); 559 | } 560 | 561 | // The final kernel averages the current frame's image with all previous frames to reduce noise due 562 | // random sampling of the scene. 563 | computeEncoder = [commandBuffer computeCommandEncoder]; 564 | 565 | [computeEncoder setBuffer:_uniformBuffer offset:_uniformBufferOffset atIndex:0]; 566 | 567 | [computeEncoder setTexture:_renderTargets[0] atIndex:0]; 568 | [computeEncoder setTexture:_accumulationTargets[0] atIndex:1]; 569 | [computeEncoder setTexture:_accumulationTargets[1] atIndex:2]; 570 | 571 | [computeEncoder setComputePipelineState:_accumulatePipeline]; 572 | 573 | [computeEncoder dispatchThreadgroups:threadgroups threadsPerThreadgroup:threadsPerThreadgroup]; 574 | 575 | [computeEncoder endEncoding]; 576 | 577 | std::swap(_accumulationTargets[0], _accumulationTargets[1]); 578 | 579 | // Copy the resulting image into our view using the graphics pipeline since we can't write directly to 580 | // it with a compute kernel. We need to delay getting the current render pass descriptor as long as 581 | // possible to avoid stalling until the GPU/compositor release a drawable. The render pass descriptor 582 | // may be nil if the window has moved off screen. 583 | MTLRenderPassDescriptor* renderPassDescriptor = view.currentRenderPassDescriptor; 584 | 585 | if (renderPassDescriptor != nil) { 586 | // Create a render encoder 587 | id renderEncoder = 588 | [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor]; 589 | 590 | [renderEncoder setRenderPipelineState:_copyPipeline]; 591 | 592 | [renderEncoder setFragmentTexture:_accumulationTargets[0] atIndex:0]; 593 | 594 | // Draw a quad which fills the screen 595 | [renderEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:6]; 596 | 597 | [renderEncoder endEncoding]; 598 | 599 | // Present the drawable to the screen 600 | [commandBuffer presentDrawable:view.currentDrawable]; 601 | } 602 | 603 | // Finally, commit the command buffer so that the GPU can start executing 604 | [commandBuffer commit]; 605 | } 606 | 607 | @end 608 | -------------------------------------------------------------------------------- /MPSPathTracingSample/Scene.h: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Header for scene creation functions 6 | */ 7 | 8 | #ifndef Scene_h 9 | #define Scene_h 10 | 11 | #include 12 | #include 13 | #import 14 | 15 | extern std::vector vertices; 16 | extern std::vector normals; 17 | extern std::vector textureCoords; 18 | extern std::vector textureIndices; 19 | extern std::vector reflections; 20 | extern std::vector reflectionBlurs; 21 | extern std::vector refractions; 22 | extern std::vector refractionIndices; 23 | extern std::vector colors; 24 | extern std::vector masks; 25 | 26 | #define FACE_MASK_NONE 0 27 | #define FACE_MASK_NEGATIVE_X (1 << 0) 28 | #define FACE_MASK_POSITIVE_X (1 << 1) 29 | #define FACE_MASK_NEGATIVE_Y (1 << 2) 30 | #define FACE_MASK_POSITIVE_Y (1 << 3) 31 | #define FACE_MASK_NEGATIVE_Z (1 << 4) 32 | #define FACE_MASK_POSITIVE_Z (1 << 5) 33 | #define FACE_MASK_ALL ((1 << 6) - 1) 34 | 35 | void createCube(unsigned int faceMask, 36 | vector_float3 color, 37 | matrix_float4x4 transform, 38 | bool inwardNormals, 39 | unsigned int textureIndex, 40 | float reflection, 41 | float reflectionBlur, 42 | float refraction, 43 | float refractionIndex, 44 | unsigned int triangleMask); 45 | 46 | void createSphere(vector_float3 color, 47 | matrix_float4x4 transform, 48 | unsigned int textureIndex, 49 | float reflection, 50 | float reflectionBlur, 51 | float refraction, 52 | float refractionIndex, 53 | unsigned int triangleMask); 54 | 55 | void createMesh(MDLMesh* mesh, 56 | vector_float3 color, 57 | matrix_float4x4 transform, 58 | unsigned int textureIndex, 59 | float reflection, 60 | float reflectionBlur, 61 | float refraction, 62 | float refractionIndex, 63 | unsigned int triangleMask); 64 | 65 | #endif /* Scene_h */ 66 | -------------------------------------------------------------------------------- /MPSPathTracingSample/Scene.mm: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Implementation for scene creation functions 6 | */ 7 | 8 | 9 | #import "Scene.h" 10 | 11 | using namespace simd; 12 | 13 | std::vector vertices; 14 | std::vector normals; 15 | std::vector textureCoords; 16 | std::vector textureIndices; 17 | std::vector reflections; 18 | std::vector reflectionBlurs; 19 | std::vector refractions; 20 | std::vector refractionIndices; 21 | std::vector colors; 22 | std::vector masks; 23 | 24 | float3 getTriangleNormal(float3 v0, float3 v1, float3 v2) { 25 | float3 e1 = normalize(v1 - v0); 26 | float3 e2 = normalize(v2 - v0); 27 | 28 | return cross(e1, e2); 29 | } 30 | 31 | void createCubeFace(std::vector & vertices, 32 | std::vector & normals, 33 | std::vector & textureCoords, 34 | std::vector & colors, 35 | float3 *cubeVertices, 36 | float3 color, 37 | unsigned int i0, 38 | unsigned int i1, 39 | unsigned int i2, 40 | unsigned int i3, 41 | bool inwardNormals, 42 | unsigned int textureIndex, 43 | float reflection, 44 | float reflectionBlur, 45 | float refraction, 46 | float refractionIndex, 47 | unsigned int triangleMask) 48 | { 49 | float3 v0 = cubeVertices[i0]; 50 | float3 v1 = cubeVertices[i1]; 51 | float3 v2 = cubeVertices[i2]; 52 | float3 v3 = cubeVertices[i3]; 53 | 54 | float3 n0 = getTriangleNormal(v0, v1, v2); 55 | float3 n1 = getTriangleNormal(v0, v2, v3); 56 | 57 | if (inwardNormals) { 58 | n0 = -n0; 59 | n1 = -n1; 60 | } 61 | 62 | vertices.push_back(v0); 63 | vertices.push_back(v1); 64 | vertices.push_back(v2); 65 | vertices.push_back(v0); 66 | vertices.push_back(v2); 67 | vertices.push_back(v3); 68 | 69 | for (int i = 0; i < 6; i++) { 70 | reflections.push_back(reflection); 71 | reflectionBlurs.push_back(reflectionBlur); 72 | refractions.push_back(refraction); 73 | refractionIndices.push_back(refractionIndex); 74 | textureIndices.push_back(textureIndex); 75 | } 76 | 77 | textureCoords.push_back(vector2(0.0f, 0.0f)); 78 | textureCoords.push_back(vector2(0.0f, 1.0f)); 79 | textureCoords.push_back(vector2(1.0f, 1.0f)); 80 | 81 | textureCoords.push_back(vector2(0.0f, 0.0f)); 82 | textureCoords.push_back(vector2(1.0f, 1.0f)); 83 | textureCoords.push_back(vector2(1.0f, 0.0f)); 84 | 85 | for (int i = 0; i < 3; i++) 86 | normals.push_back(n0); 87 | 88 | for (int i = 0; i < 3; i++) 89 | normals.push_back(n1); 90 | 91 | for (int i = 0; i < 6; i++) 92 | colors.push_back(color); 93 | 94 | for (int i = 0; i < 2; i++) 95 | masks.push_back(triangleMask); 96 | } 97 | 98 | void createCube(unsigned int faceMask, 99 | vector_float3 color, 100 | matrix_float4x4 transform, 101 | bool inwardNormals, 102 | unsigned int textureIndex, 103 | float reflection, 104 | float reflectionBlur, 105 | float refraction, 106 | float refractionIndex, 107 | unsigned int triangleMask) 108 | { 109 | float3 cubeVertices[] = { 110 | vector3(-0.5f, -0.5f, -0.5f), 111 | vector3( 0.5f, -0.5f, -0.5f), 112 | vector3(-0.5f, 0.5f, -0.5f), 113 | vector3( 0.5f, 0.5f, -0.5f), 114 | vector3(-0.5f, -0.5f, 0.5f), 115 | vector3( 0.5f, -0.5f, 0.5f), 116 | vector3(-0.5f, 0.5f, 0.5f), 117 | vector3( 0.5f, 0.5f, 0.5f), 118 | }; 119 | 120 | for (int i = 0; i < 8; i++) { 121 | float3 vertex = cubeVertices[i]; 122 | 123 | float4 transformedVertex = vector4(vertex.x, vertex.y, vertex.z, 1.0f); 124 | transformedVertex = transform * transformedVertex; 125 | 126 | cubeVertices[i] = transformedVertex.xyz; 127 | } 128 | 129 | if (faceMask & FACE_MASK_NEGATIVE_X) 130 | createCubeFace(vertices, normals, textureCoords, colors, cubeVertices, color, 0, 4, 6, 2, inwardNormals, textureIndex, reflection, reflectionBlur, refraction, refractionIndex, triangleMask); 131 | 132 | if (faceMask & FACE_MASK_POSITIVE_X) 133 | createCubeFace(vertices, normals, textureCoords, colors, cubeVertices, color, 1, 3, 7, 5, inwardNormals, textureIndex, reflection, reflectionBlur, refraction, refractionIndex, triangleMask); 134 | 135 | if (faceMask & FACE_MASK_NEGATIVE_Y) 136 | createCubeFace(vertices, normals, textureCoords, colors, cubeVertices, color, 0, 1, 5, 4, inwardNormals, textureIndex, reflection, reflectionBlur, refraction, refractionIndex, triangleMask); 137 | 138 | if (faceMask & FACE_MASK_POSITIVE_Y) 139 | createCubeFace(vertices, normals, textureCoords, colors, cubeVertices, color, 2, 6, 7, 3, inwardNormals, textureIndex, reflection, reflectionBlur, refraction, refractionIndex, triangleMask); 140 | 141 | if (faceMask & FACE_MASK_NEGATIVE_Z) 142 | createCubeFace(vertices, normals, textureCoords, colors, cubeVertices, color, 0, 2, 3, 1, inwardNormals, textureIndex, reflection, reflectionBlur, refraction, refractionIndex, triangleMask); 143 | 144 | if (faceMask & FACE_MASK_POSITIVE_Z) 145 | createCubeFace(vertices, normals, textureCoords, colors, cubeVertices, color, 4, 5, 7, 6, inwardNormals, textureIndex, reflection, reflectionBlur, refraction, refractionIndex, triangleMask); 146 | } 147 | 148 | void createTriangle(std::vector & vertices, 149 | std::vector & normals, 150 | std::vector & textureCoords, 151 | std::vector & colors, 152 | float3 *triangleVertices, 153 | float3 *triangleNormal, 154 | float2 *triangletextureCoords, 155 | float3 color, 156 | unsigned int textureIndex, 157 | float reflection, 158 | float reflectionBlur, 159 | float refraction, 160 | float refractionIndex, 161 | unsigned int triangleMask) 162 | { 163 | for (int i = 0; i < 3; i++) { 164 | reflections.push_back(reflection); 165 | reflectionBlurs.push_back(reflectionBlur); 166 | refractions.push_back(refraction); 167 | refractionIndices.push_back(refractionIndex); 168 | textureIndices.push_back(textureIndex); 169 | } 170 | 171 | for (int i = 0; i < 3; i++) { 172 | vertices.push_back(triangleVertices[i]); 173 | normals.push_back(triangleNormal[i]); 174 | textureCoords.push_back(triangletextureCoords[i]); 175 | colors.push_back(color); 176 | } 177 | 178 | masks.push_back(triangleMask); 179 | } 180 | 181 | #define RING_STEPS 30 182 | #define POINT_STEPS 30 183 | 184 | void createSphere(vector_float3 color, 185 | matrix_float4x4 transform, 186 | unsigned int textureIndex, 187 | float reflection, 188 | float reflectionBlur, 189 | float refraction, 190 | float refractionIndex, 191 | unsigned int triangleMask) { 192 | float pi = 22.0f / 7.0f; 193 | float deltaTheta = pi / (RING_STEPS + 2); 194 | float deltaPhi = 2.0f * pi / POINT_STEPS; 195 | float theta = 0.0f; 196 | 197 | for (int ring = 0; ring < RING_STEPS + 2; ring++) { 198 | float phi = 0.0f; 199 | for (int point = 0; point < POINT_STEPS; point++) { 200 | float3 v0 = vector3(sin(theta) * cos(phi), 201 | sin(theta) * sin(phi), 202 | cos(theta)); 203 | 204 | float3 v1 = vector3(sin(theta + deltaTheta) * cos(phi), 205 | sin(theta + deltaTheta) * sin(phi), 206 | cos(theta + deltaTheta)); 207 | 208 | float3 v2 = vector3(sin(theta) * cos(phi + deltaPhi), 209 | sin(theta) * sin(phi + deltaPhi), 210 | cos(theta)); 211 | 212 | float3 v3 = vector3(sin(theta + deltaTheta) * cos(phi + deltaPhi), 213 | sin(theta + deltaTheta) * sin(phi + deltaPhi), 214 | cos(theta + deltaTheta)); 215 | 216 | float3 triangleVertices0[] = { 217 | (transform * vector4(v0, 1.0f)).xyz, 218 | (transform * vector4(v1, 1.0f)).xyz, 219 | (transform * vector4(v2, 1.0f)).xyz }; 220 | 221 | float3 normal0[] = { 222 | (transform * vector4(v0, 0.0f)).xyz, 223 | (transform * vector4(v1, 0.0f)).xyz, 224 | (transform * vector4(v2, 0.0f)).xyz}; 225 | 226 | float2 textureCoords0[] = {0.0f, 1.0f}; 227 | createTriangle(vertices, normals, textureCoords, colors, triangleVertices0, normal0, textureCoords0, color, textureIndex, reflection, reflectionBlur, refraction, refractionIndex, triangleMask); 228 | 229 | float3 triangleVertices1[] = { 230 | (transform * vector4(v3, 1.0f)).xyz, 231 | (transform * vector4(v2, 1.0f)).xyz, 232 | (transform * vector4(v1, 1.0f)).xyz}; 233 | 234 | float3 normal1[] = { 235 | (transform * vector4(v3, 0.0f)).xyz, 236 | (transform * vector4(v2, 0.0f)).xyz, 237 | (transform * vector4(v1, 0.0f)).xyz}; 238 | 239 | float2 textureCoords1[] = {0.0f, 1.0f}; 240 | createTriangle(vertices, normals, textureCoords, colors, triangleVertices1, normal1, textureCoords1, color, textureIndex, reflection, reflectionBlur, refraction, refractionIndex, triangleMask); 241 | phi += deltaPhi; 242 | } 243 | theta += deltaTheta; 244 | } 245 | } 246 | 247 | void createMesh(MDLMesh* mesh, 248 | vector_float3 color, 249 | matrix_float4x4 transform, 250 | unsigned int textureIndex, 251 | float reflection, 252 | float reflectionBlur, 253 | float refraction, 254 | float refractionIndex, 255 | unsigned int triangleMask) { 256 | for (int i = 0; i < (int)mesh.submeshes.count; i++) { 257 | MDLSubmesh *subMesh = mesh.submeshes[i]; 258 | NSLog(@"Submesh Index count: %d", (int)subMesh.indexCount); 259 | 260 | MDLMeshBufferData *meshBufferForIndices = subMesh.indexBuffer; 261 | NSLog(@"Meshbuffer Index count: %d", (int)subMesh.indexCount); 262 | uint32_t *indices = (uint32_t *)meshBufferForIndices.data.bytes; 263 | 264 | NSArray > *arrayOfMeshBuffers = mesh.vertexBuffers; 265 | MDLMeshBufferData *meshBufferForVertice = arrayOfMeshBuffers[0]; 266 | NSLog(@"Vertex Buffer Count: %d", (int)meshBufferForVertice.length / 4); 267 | float *vertexData = (float *)meshBufferForVertice.data.bytes; 268 | 269 | NSLog(@"Vertex Buffer Count: %d", (int)mesh.vertexCount); 270 | for (int i = 0; i < (int)subMesh.indexCount; i+=3) { 271 | float3 v0 = vector3((float)vertexData[indices[i+0]*8 + 0], (float)vertexData[indices[i+0]*8 + 1], (float)vertexData[indices[i+0]*8 + 2]); 272 | float3 v1 = vector3((float)vertexData[indices[i+1]*8 + 0], (float)vertexData[indices[i+1]*8 + 1], (float)vertexData[indices[i+1]*8 + 2]); 273 | float3 v2 = vector3((float)vertexData[indices[i+2]*8 + 0], (float)vertexData[indices[i+2]*8 + 1], (float)vertexData[indices[i+2]*8 + 2]); 274 | float3 triangleVertices[] = { 275 | (transform * vector4(v0, 1.0f)).xyz, 276 | (transform * vector4(v1, 1.0f)).xyz, 277 | (transform * vector4(v2, 1.0f)).xyz }; 278 | 279 | float3 n0 = vector3((float)vertexData[indices[i+0]*8 + 3], (float)vertexData[indices[i+0]*8 + 4], (float)vertexData[indices[i+0]*8 + 5]); 280 | float3 n1 = vector3((float)vertexData[indices[i+1]*8 + 3], (float)vertexData[indices[i+1]*8 + 4], (float)vertexData[indices[i+1]*8 + 5]); 281 | float3 n2 = vector3((float)vertexData[indices[i+2]*8 + 3], (float)vertexData[indices[i+2]*8 + 4], (float)vertexData[indices[i+2]*8 + 5]); 282 | float3 triangleNormals[] = { 283 | (transform * vector4(n0, 0.0f)).xyz, 284 | (transform * vector4(n1, 0.0f)).xyz, 285 | (transform * vector4(n2, 0.0f)).xyz }; 286 | 287 | float2 t0 = vector2((float)vertexData[indices[i+0]*8 + 6], 1.0f - (float)vertexData[indices[i+0]*8 + 7]); 288 | float2 t1 = vector2((float)vertexData[indices[i+1]*8 + 6], 1.0f - (float)vertexData[indices[i+1]*8 + 7]); 289 | float2 t2 = vector2((float)vertexData[indices[i+2]*8 + 6], 1.0f - (float)vertexData[indices[i+2]*8 + 7]); 290 | float2 triangleTextureCoords[] = { t0, t1, t2}; 291 | 292 | createTriangle(vertices, normals, textureCoords, colors, triangleVertices, triangleNormals, triangleTextureCoords, color, textureIndex, reflection, reflectionBlur, refraction, refractionIndex, triangleMask); 293 | } 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /MPSPathTracingSample/ShaderTypes.h: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Header containing types and enum constants shared between Metal shaders and Swift/ObjC source 6 | */ 7 | 8 | #ifndef ShaderTypes_h 9 | #define ShaderTypes_h 10 | 11 | #include 12 | 13 | #define TRIANGLE_MASK_GEOMETRY 1 14 | #define TRIANGLE_MASK_LIGHT 2 15 | 16 | #define RAY_MASK_PRIMARY 3 17 | #define RAY_MASK_SHADOW 1 18 | #define RAY_MASK_SECONDARY 1 19 | 20 | struct Camera { 21 | vector_float3 position; 22 | vector_float3 right; 23 | vector_float3 up; 24 | vector_float3 forward; 25 | }; 26 | 27 | struct AreaLight { 28 | vector_float3 position; 29 | vector_float3 forward; 30 | vector_float3 right; 31 | vector_float3 up; 32 | vector_float3 color; 33 | }; 34 | 35 | struct Uniforms 36 | { 37 | unsigned int width; 38 | unsigned int height; 39 | unsigned int frameIndex; 40 | Camera camera; 41 | AreaLight light; 42 | }; 43 | 44 | #endif /* ShaderTypes_h */ 45 | 46 | -------------------------------------------------------------------------------- /MPSPathTracingSample/Shaders.metal: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Metal shaders used for ray tracing 6 | */ 7 | 8 | #include 9 | #include 10 | 11 | #import "ShaderTypes.h" 12 | 13 | using namespace metal; 14 | 15 | // Represents a three dimensional ray which will be intersected with the scene. The ray type 16 | // is customized using properties of the MPSRayIntersector. 17 | struct Ray { 18 | // Starting point 19 | packed_float3 origin; 20 | 21 | // Mask which will be bitwise AND-ed with per-triangle masks to filter out certain 22 | // intersections. This is used to make the light source visible to the camera but not 23 | // to shadow or secondary rays. 24 | uint mask; 25 | 26 | // Direction the ray is traveling 27 | packed_float3 direction; 28 | 29 | // Maximum intersection distance to accept. This is used to prevent shadow rays from 30 | // overshooting the light source when checking for visibility. 31 | float maxDistance; 32 | 33 | // The accumulated color along the ray's path so far 34 | float3 color; 35 | }; 36 | 37 | // Represents an intersection between a ray and the scene, returned by the MPSRayIntersector. 38 | // The intersection type is customized using properties of the MPSRayIntersector. 39 | struct Intersection { 40 | // The distance from the ray origin to the intersection point. Negative if the ray did not 41 | // intersect the scene. 42 | float distance; 43 | 44 | // The index of the intersected primitive (triangle), if any. Undefined if the ray did not 45 | // intersect the scene. 46 | int primitiveIndex; 47 | 48 | // The barycentric coordinates of the intersection point, if any. Undefined if the ray did 49 | // not intersect the scene. 50 | float2 coordinates; 51 | }; 52 | 53 | constant unsigned int primes[] = { 54 | 2, 3, 5, 7, 55 | 11, 13, 17, 19, 56 | 23, 29, 31, 37, 57 | 41, 43, 47, 53, 58 | }; 59 | 60 | // Returns the i'th element of the Halton sequence using the d'th prime number as a 61 | // base. The Halton sequence is a "low discrepency" sequence: the values appear 62 | // random but are more evenly distributed then a purely random sequence. Each random 63 | // value used to render the image should use a different independent dimension 'd', 64 | // and each sample (frame) should use a different index 'i'. To decorrelate each 65 | // pixel, a random offset can be applied to 'i'. 66 | float halton(unsigned int i, unsigned int d) { 67 | unsigned int b = primes[d]; 68 | 69 | float f = 1.0f; 70 | float invB = 1.0f / b; 71 | 72 | float r = 0; 73 | 74 | while (i > 0) { 75 | f = f * invB; 76 | r = r + f * (i % b); 77 | i = i / b; 78 | } 79 | 80 | return r; 81 | } 82 | 83 | // Generates rays starting from the camera origin and traveling towards the image plane aligned 84 | // with the camera's coordinate system. 85 | kernel void rayKernel(uint2 tid [[thread_position_in_grid]], 86 | // Buffers bound on the CPU. Note that 'constant' should be used for small 87 | // read-only data which will be reused across threads. 'device' should be 88 | // used for writable data or data which will only be used by a single thread. 89 | constant Uniforms & uniforms, 90 | device Ray *rays, 91 | texture2d randomTex, 92 | texture2d colorTex1, 93 | texture2d colorTex2, 94 | texture2d dstTex) 95 | { 96 | // Since we aligned the thread count to the threadgroup size, the thread index may be out of bounds 97 | // of the render target size. 98 | if (tid.x < uniforms.width && tid.y < uniforms.height) { 99 | // Compute linear ray index from 2D position 100 | unsigned int rayIdx = tid.y * uniforms.width + tid.x; 101 | 102 | // Ray we will produce 103 | device Ray & ray = rays[rayIdx]; 104 | 105 | // Pixel coordinates for this thread 106 | float2 pixel = (float2)tid; 107 | 108 | // Apply a random offset to random number index to decorrelate pixels 109 | unsigned int offset = randomTex.read(tid).x; 110 | 111 | // Add a random offset to the pixel coordinates for antialiasing 112 | float2 r = float2(halton(offset + uniforms.frameIndex, 0), 113 | halton(offset + uniforms.frameIndex, 1)); 114 | 115 | pixel += r; 116 | 117 | // Map pixel coordinates to -1..1 118 | float2 uv = (float2)pixel / float2(uniforms.width, uniforms.height); 119 | uv = uv * 2.0f - 1.0f; 120 | 121 | constant Camera & camera = uniforms.camera; 122 | 123 | // Rays start at the camera position 124 | ray.origin = camera.position; 125 | 126 | // Map normalized pixel coordinates into camera's coordinate system 127 | ray.direction = normalize(uv.x * camera.right + 128 | uv.y * camera.up + 129 | camera.forward); 130 | // The camera emits primary rays 131 | ray.mask = RAY_MASK_PRIMARY; 132 | 133 | // Don't limit intersection distance 134 | ray.maxDistance = INFINITY; 135 | 136 | // Start with a fully white color. Each bounce will scale the color as light 137 | // is absorbed into surfaces. 138 | ray.color = float3(1.0f, 1.0f, 1.0f); 139 | 140 | // Clear the destination image to black 141 | dstTex.write(float4(0.0f, 0.0f, 0.0f, 0.0f), tid); 142 | } 143 | } 144 | 145 | // Interpolates vertex attribute of an arbitrary type across the surface of a triangle 146 | // given the barycentric coordinates and triangle index in an intersection struct 147 | template 148 | inline T interpolateVertexAttribute(device T *attributes, Intersection intersection) { 149 | // Barycentric coordinates sum to one 150 | float3 uvw; 151 | uvw.xy = intersection.coordinates; 152 | uvw.z = 1.0f - uvw.x - uvw.y; 153 | 154 | unsigned int triangleIndex = intersection.primitiveIndex; 155 | 156 | // Lookup value for each vertex 157 | T T0 = attributes[triangleIndex * 3 + 0]; 158 | T T1 = attributes[triangleIndex * 3 + 1]; 159 | T T2 = attributes[triangleIndex * 3 + 2]; 160 | 161 | // Compute sum of vertex attributes weighted by barycentric coordinates 162 | return uvw.x * T0 + uvw.y * T1 + uvw.z * T2; 163 | } 164 | 165 | // Uses the inversion method to map two uniformly random numbers to a three dimensional 166 | // unit hemisphere where the probability of a given sample is proportional to the cosine 167 | // of the angle between the sample direction and the "up" direction (0, 1, 0) 168 | inline float3 sampleCosineWeightedHemisphere(float2 u) { 169 | float phi = 2.0f * M_PI_F * u.x; 170 | 171 | float cos_phi; 172 | float sin_phi = sincos(phi, cos_phi); 173 | 174 | float cos_theta = sqrt(u.y); 175 | float sin_theta = sqrt(1.0f - cos_theta * cos_theta); 176 | 177 | return float3(sin_theta * cos_phi, cos_theta, sin_theta * sin_phi); 178 | } 179 | 180 | // Maps two uniformly random numbers to the surface of a two-dimensional area light 181 | // source and returns the direction to this point, the amount of light which travels 182 | // between the intersection point and the sample point on the light source, as well 183 | // as the distance between these two points. 184 | inline void sampleAreaLight(constant AreaLight & light, 185 | float2 u, 186 | float3 position, 187 | thread float3 & lightDirection, 188 | thread float3 & lightColor, 189 | thread float & lightDistance) 190 | { 191 | // Map to -1..1 192 | u = u * 2.0f - 1.0f; 193 | 194 | // Transform into light's coordinate system 195 | float3 samplePosition = light.position + 196 | light.right * u.x + 197 | light.up * u.y; 198 | 199 | // Compute vector from sample point on light source to intersection point 200 | lightDirection = samplePosition - position; 201 | 202 | lightDistance = length(lightDirection); 203 | 204 | float inverseLightDistance = 1.0f / max(lightDistance, 1e-3f); 205 | 206 | // Normalize the light direction 207 | lightDirection *= inverseLightDistance; 208 | 209 | // Start with the light's color 210 | lightColor = light.color; 211 | 212 | // Light falls off with the inverse square of the distance to the intersection point 213 | lightColor *= (inverseLightDistance * inverseLightDistance); 214 | 215 | // Light also falls off with the cosine of angle between the intersection point and 216 | // the light source 217 | lightColor *= saturate(dot(-lightDirection, light.forward)); 218 | } 219 | 220 | // Aligns a direction on the unit hemisphere such that the hemisphere's "up" direction 221 | // (0, 1, 0) maps to the given surface normal direction 222 | inline float3 alignHemisphereWithNormal(float3 sample, float3 normal) { 223 | // Set the "up" vector to the normal 224 | float3 up = normal; 225 | 226 | // Find an arbitrary direction perpendicular to the normal. This will become the 227 | // "right" vector. 228 | float3 right = normalize(cross(normal, float3(0.0072f, 1.0f, 0.0034f))); 229 | 230 | // Find a third vector perpendicular to the previous two. This will be the 231 | // "forward" vector. 232 | float3 forward = cross(right, up); 233 | 234 | // Map the direction on the unit hemisphere to the coordinate system aligned 235 | // with the normal. 236 | return sample.x * right + sample.y * up + sample.z * forward; 237 | } 238 | 239 | // Consumes ray/triangle intersection results to compute the shaded image 240 | kernel void shadeKernel(uint2 tid [[thread_position_in_grid]], 241 | constant Uniforms & uniforms, 242 | device Ray *rays, 243 | device Ray *shadowRays, 244 | device Intersection *intersections, 245 | device float3 *vertexColors, 246 | device float3 *vertexNormals, 247 | device float2 *textureCoords, 248 | device uint *textureIndex, 249 | device float *reflection, 250 | device float *reflectionBlur, 251 | device float *refraction, 252 | device float *refractionIndex, 253 | device uint *triangleMasks, 254 | constant unsigned int & bounce, 255 | texture2d randomTex, 256 | texture2d colorTex1, 257 | texture2d colorTex2, 258 | texture2d dstTex) 259 | { 260 | if (tid.x < uniforms.width && tid.y < uniforms.height) { 261 | unsigned int rayIdx = tid.y * uniforms.width + tid.x; 262 | device Ray & ray = rays[rayIdx]; 263 | device Ray & shadowRay = shadowRays[rayIdx]; 264 | device Intersection & intersection = intersections[rayIdx]; 265 | 266 | float3 color = ray.color; 267 | 268 | // Intersection distance will be negative if ray missed or was disabled in a previous 269 | // iteration. 270 | if (ray.maxDistance >= 0.0f && intersection.distance >= 0.0f) { 271 | uint mask = triangleMasks[intersection.primitiveIndex]; 272 | 273 | // The light source is included in the acceleration structure so we can see it in the 274 | // final image. However, we will compute and sample the lighting directly, so we mask 275 | // the light out for shadow and secondary rays. 276 | if (mask == TRIANGLE_MASK_GEOMETRY) { 277 | // Compute intersection point 278 | float3 intersectionPoint = ray.origin + ray.direction * intersection.distance; 279 | 280 | // Interpolate the vertex normal at the intersection point 281 | float3 surfaceNormal = interpolateVertexAttribute(vertexNormals, intersection); 282 | surfaceNormal = normalize(surfaceNormal); 283 | 284 | unsigned int offset = randomTex.read(tid).x; 285 | 286 | // Look up two random numbers for this thread 287 | float2 r = float2(halton(offset + uniforms.frameIndex, 2 + bounce * 4 + 0), 288 | halton(offset + uniforms.frameIndex, 2 + bounce * 4 + 1)); 289 | 290 | float3 lightDirection; 291 | float3 lightColor; 292 | float lightDistance; 293 | 294 | // Compute the direction to, color, and distance to a random point on the light 295 | // source 296 | sampleAreaLight(uniforms.light, r, intersectionPoint, lightDirection, 297 | lightColor, lightDistance); 298 | 299 | // Scale the light color by the cosine of the angle between the light direction and 300 | // surface normal 301 | lightColor *= saturate(dot(surfaceNormal, lightDirection)); 302 | 303 | uint texIndex = interpolateVertexAttribute(textureIndex, intersection); 304 | if (texIndex > 0) { 305 | float2 texCoord = interpolateVertexAttribute(textureCoords, intersection); 306 | constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); 307 | if(texIndex == 1) 308 | color *= colorTex1.sample(sam, texCoord).xyz; 309 | else if(texIndex == 2) 310 | color *= colorTex2.sample(sam, texCoord).xyz; 311 | } else { 312 | // Interpolate the vertex color at the intersection point 313 | color *= interpolateVertexAttribute(vertexColors, intersection); 314 | } 315 | 316 | // Compute the shadow ray. The shadow ray will check if the sample position on the 317 | // light source is actually visible from the intersection point we are shading. 318 | // If it is, the lighting contribution we just computed will be added to the 319 | // output image. 320 | 321 | // Add a small offset to the intersection point to avoid intersecting the same 322 | // triangle again. 323 | shadowRay.origin = intersectionPoint + surfaceNormal * 1e-3f; 324 | 325 | // Travel towards the light source 326 | shadowRay.direction = lightDirection; 327 | 328 | // Avoid intersecting the light source itself 329 | shadowRay.mask = RAY_MASK_SHADOW; 330 | 331 | // Don't overshoot the light source 332 | shadowRay.maxDistance = lightDistance - 1e-3f; 333 | 334 | // Multiply the color and lighting amount at the intersection point to get the final 335 | // color, and pass it along with the shadow ray so that it can be added to the 336 | // output image if needed. 337 | shadowRay.color = lightColor * color; 338 | 339 | float reflectionValue = interpolateVertexAttribute(reflection, intersection); 340 | float refractionValue = interpolateVertexAttribute(refraction, intersection); 341 | if (reflectionValue > 0.0f) { 342 | float reflectionBlurValue = interpolateVertexAttribute(reflectionBlur, intersection); 343 | 344 | float3 rayDirection = reflect(ray.direction.xyz, surfaceNormal); 345 | rayDirection.x += reflectionBlurValue * halton(offset + uniforms.frameIndex, 2 + bounce * 4 + 0); 346 | rayDirection.y += reflectionBlurValue * halton(offset + uniforms.frameIndex, 2 + bounce * 4 + 1); 347 | rayDirection.z += reflectionBlurValue * halton(offset + uniforms.frameIndex, 2 + bounce * 4 + 2); 348 | 349 | ray.origin = intersectionPoint + rayDirection * 1e-3f; 350 | ray.direction = rayDirection; 351 | ray.color = color * reflectionValue; 352 | ray.mask = RAY_MASK_PRIMARY; 353 | ray.maxDistance = INFINITY; 354 | shadowRay.maxDistance = -1.0f; 355 | } else if (refractionValue > 0.0f) { 356 | float refractionIndexValue = interpolateVertexAttribute(refractionIndex, intersection); 357 | bool inside = false; 358 | if (dot(ray.direction, surfaceNormal) > 0) { 359 | inside = true; 360 | surfaceNormal = -surfaceNormal; 361 | } 362 | float eta = (inside) ? refractionIndexValue : 1.0f / refractionIndexValue; 363 | float3 rayDirection = refract(ray.direction.xyz, surfaceNormal, eta); 364 | 365 | ray.origin = intersectionPoint + rayDirection * 1e-3f; 366 | ray.direction = rayDirection; 367 | ray.color = color * refractionValue; 368 | ray.mask = RAY_MASK_PRIMARY; 369 | ray.maxDistance = INFINITY; 370 | shadowRay.maxDistance = -1.0f; 371 | } else { 372 | // Next we choose a random direction to continue the path of the ray. This will 373 | // cause light to bounce between surfaces. Normally we would apply a fair bit of math 374 | // to compute the fraction of reflected by the current intersection point to the 375 | // previous point from the next point. However, by choosing a random direction with 376 | // probability proportional to the cosine (dot product) of the angle between the 377 | // sample direction and surface normal, the math entirely cancels out except for 378 | // multiplying by the interpolated vertex color. This sampling strategy also reduces 379 | // the amount of noise in the output image. 380 | r = float2(halton(offset + uniforms.frameIndex, 2 + bounce * 4 + 2), 381 | halton(offset + uniforms.frameIndex, 2 + bounce * 4 + 3)); 382 | 383 | float3 sampleDirection = sampleCosineWeightedHemisphere(r); 384 | sampleDirection = alignHemisphereWithNormal(sampleDirection, surfaceNormal); 385 | 386 | ray.origin = intersectionPoint + surfaceNormal * 1e-3f; 387 | ray.direction = sampleDirection; 388 | ray.color = color; 389 | ray.mask = RAY_MASK_SECONDARY; 390 | } 391 | } 392 | else { 393 | // In this case, a ray coming from the camera hit the light source directly, so 394 | // we'll write the light color into the output image. 395 | dstTex.write(float4(uniforms.light.color, 1.0f), tid); 396 | 397 | // Terminate the ray's path 398 | ray.maxDistance = -1.0f; 399 | shadowRay.maxDistance = -1.0f; 400 | } 401 | } 402 | else { 403 | // The ray missed the scene, so terminate the ray's path 404 | ray.maxDistance = -1.0f; 405 | shadowRay.maxDistance = -1.0f; 406 | } 407 | } 408 | } 409 | 410 | // Checks if a shadow ray hit something on the way to the light source. If not, the point the 411 | // shadow ray started from was not in shadow so it's color should be added to the output image. 412 | kernel void shadowKernel(uint2 tid [[thread_position_in_grid]], 413 | constant Uniforms & uniforms, 414 | device Ray *shadowRays, 415 | device float *intersections, 416 | texture2d srcTex, 417 | texture2d dstTex) 418 | { 419 | if (tid.x < uniforms.width && tid.y < uniforms.height) { 420 | unsigned int rayIdx = tid.y * uniforms.width + tid.x; 421 | device Ray & shadowRay = shadowRays[rayIdx]; 422 | 423 | // Use the MPSRayIntersection intersectionDataType property to return the 424 | // intersection distance for this kernel only. You don't need the other fields, so 425 | // you'll save memory bandwidth. 426 | float intersectionDistance = intersections[rayIdx]; 427 | 428 | float3 color = srcTex.read(tid).xyz; 429 | 430 | // If the shadow ray wasn't disabled (max distance >= 0) and it didn't hit anything 431 | // on the way to the light source, add the color passed along with the shadow ray 432 | // to the output image. 433 | if (shadowRay.maxDistance >= 0.0f && intersectionDistance < 0.0f) 434 | color += shadowRay.color; 435 | 436 | // Write result to render target 437 | dstTex.write(float4(color, 1.0f), tid); 438 | } 439 | } 440 | 441 | // Accumulates the current frame's image with a running average of all previous frames to 442 | // reduce noise over time. 443 | kernel void accumulateKernel(uint2 tid [[thread_position_in_grid]], 444 | constant Uniforms & uniforms, 445 | texture2d renderTex, 446 | texture2d prevTex, 447 | texture2d accumTex) 448 | { 449 | if (tid.x < uniforms.width && tid.y < uniforms.height) { 450 | float3 color = renderTex.read(tid).xyz; 451 | 452 | // Compute the average of all frames including the current frame 453 | if (uniforms.frameIndex > 0) { 454 | float3 prevColor = prevTex.read(tid).xyz; 455 | prevColor *= uniforms.frameIndex; 456 | 457 | color += prevColor; 458 | color /= (uniforms.frameIndex + 1); 459 | } 460 | 461 | accumTex.write(float4(color, 1.0f), tid); 462 | } 463 | } 464 | 465 | // Screen filling quad in normalized device coordinates 466 | constant float2 quadVertices[] = { 467 | float2(-1, -1), 468 | float2(-1, 1), 469 | float2( 1, 1), 470 | float2(-1, -1), 471 | float2( 1, 1), 472 | float2( 1, -1) 473 | }; 474 | 475 | struct CopyVertexOut { 476 | float4 position [[position]]; 477 | float2 uv; 478 | }; 479 | 480 | // Simple vertex shader which passes through NDC quad positions 481 | vertex CopyVertexOut copyVertex(unsigned short vid [[vertex_id]]) { 482 | float2 position = quadVertices[vid]; 483 | 484 | CopyVertexOut out; 485 | 486 | out.position = float4(position, 0, 1); 487 | out.uv = position * 0.5f + 0.5f; 488 | 489 | return out; 490 | } 491 | 492 | // Simple fragment shader which copies a texture and applies a simple tonemapping function 493 | fragment float4 copyFragment(CopyVertexOut in [[stage_in]], 494 | texture2d tex) 495 | { 496 | constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); 497 | 498 | float3 color = tex.sample(sam, in.uv).xyz; 499 | 500 | // Apply a very simple tonemapping function to reduce the dynamic range of the 501 | // input image into a range which can be displayed on screen. 502 | color = color / (1.0f + color); 503 | 504 | return float4(color, 1.0f); 505 | } 506 | -------------------------------------------------------------------------------- /MPSPathTracingSample/Transforms.h: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Header for trasnform matrices 6 | */ 7 | 8 | #ifndef Transforms_h 9 | #define Transforms_h 10 | 11 | #import 12 | 13 | matrix_float4x4 matrix4x4_translation(float tx, float ty, float tz); 14 | matrix_float4x4 matrix4x4_rotation(float radians, vector_float3 axis); 15 | matrix_float4x4 matrix4x4_scale(float sx, float sy, float sz); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /MPSPathTracingSample/Transforms.mm: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Implementation for trasnform matrices 6 | */ 7 | 8 | #import "Transforms.h" 9 | 10 | matrix_float4x4 matrix4x4_translation(float tx, float ty, float tz) { 11 | return (matrix_float4x4) {{ 12 | { 1, 0, 0, 0 }, 13 | { 0, 1, 0, 0 }, 14 | { 0, 0, 1, 0 }, 15 | { tx, ty, tz, 1 } 16 | }}; 17 | } 18 | 19 | matrix_float4x4 matrix4x4_rotation(float radians, vector_float3 axis) { 20 | axis = vector_normalize(axis); 21 | float ct = cosf(radians); 22 | float st = sinf(radians); 23 | float ci = 1 - ct; 24 | float x = axis.x, y = axis.y, z = axis.z; 25 | 26 | return (matrix_float4x4) {{ 27 | { ct + x * x * ci, y * x * ci + z * st, z * x * ci - y * st, 0}, 28 | { x * y * ci - z * st, ct + y * y * ci, z * y * ci + x * st, 0}, 29 | { x * z * ci + y * st, y * z * ci - x * st, ct + z * z * ci, 0}, 30 | { 0, 0, 0, 1} 31 | }}; 32 | } 33 | 34 | matrix_float4x4 matrix4x4_scale(float sx, float sy, float sz) { 35 | return (matrix_float4x4) {{ 36 | { sx, 0, 0, 0 }, 37 | { 0, sy, 0, 0 }, 38 | { 0, 0, sz, 0 }, 39 | { 0, 0, 0, 1 } 40 | }}; 41 | } 42 | -------------------------------------------------------------------------------- /MPSPathTracingSample/iOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Header for our iOS Application Delegate 6 | */ 7 | 8 | #import 9 | 10 | @interface AppDelegate : UIResponder 11 | 12 | @property (strong, nonatomic) UIWindow *window; 13 | 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /MPSPathTracingSample/iOS/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Implementation for our iOS Application Delegate 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 18 | // Override point for customization after application launch. 19 | return YES; 20 | } 21 | 22 | 23 | - (void)applicationWillResignActive:(UIApplication *)application { 24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 25 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 26 | } 27 | 28 | 29 | - (void)applicationDidEnterBackground:(UIApplication *)application { 30 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 31 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 32 | } 33 | 34 | 35 | - (void)applicationWillEnterForeground:(UIApplication *)application { 36 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 37 | } 38 | 39 | 40 | - (void)applicationDidBecomeActive:(UIApplication *)application { 41 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 42 | } 43 | 44 | 45 | - (void)applicationWillTerminate:(UIApplication *)application { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | 50 | @end 51 | -------------------------------------------------------------------------------- /MPSPathTracingSample/iOS/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /MPSPathTracingSample/iOS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /MPSPathTracingSample/iOS/GameViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Header for our iOS view controller 6 | */ 7 | 8 | #import 9 | #import 10 | #import 11 | #import "Renderer.h" 12 | 13 | // Our iOS view controller 14 | @interface GameViewController : UIViewController 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /MPSPathTracingSample/iOS/GameViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Implementation for our iOS view controller 6 | */ 7 | 8 | #import "GameViewController.h" 9 | #import "Renderer.h" 10 | 11 | @implementation GameViewController 12 | { 13 | MTKView *_view; 14 | 15 | Renderer *_renderer; 16 | } 17 | 18 | - (void)viewDidLoad 19 | { 20 | [super viewDidLoad]; 21 | 22 | _view = (MTKView *)self.view; 23 | 24 | _view.device = MTLCreateSystemDefaultDevice(); 25 | _view.backgroundColor = UIColor.clearColor; 26 | 27 | if(!_view.device) 28 | { 29 | NSLog(@"Metal is not supported on this device"); 30 | self.view = [[UIView alloc] initWithFrame:self.view.frame]; 31 | return; 32 | } 33 | 34 | _renderer = [[Renderer alloc] initWithMetalKitView:_view]; 35 | 36 | [_renderer mtkView:_view drawableSizeWillChange:_view.bounds.size]; 37 | 38 | _view.delegate = _renderer; 39 | } 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /MPSPathTracingSample/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | arm64 30 | 31 | UIStatusBarHidden 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /MPSPathTracingSample/iOS/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Main Application Entrypoint 6 | */ 7 | 8 | #import 9 | #import "AppDelegate.h" 10 | 11 | int main(int argc, char * argv[]) { 12 | @autoreleasepool { 13 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /MPSPathTracingSample/macOS/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Header for our macOS Application Delegate 6 | */ 7 | 8 | #import 9 | 10 | @interface AppDelegate : NSObject 11 | 12 | 13 | @end 14 | 15 | -------------------------------------------------------------------------------- /MPSPathTracingSample/macOS/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Implementation for our macOS Application Delegate 6 | */ 7 | 8 | #import "AppDelegate.h" 9 | 10 | @interface AppDelegate () 11 | 12 | @end 13 | 14 | @implementation AppDelegate 15 | 16 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 17 | // Insert code here to initialize your application 18 | } 19 | 20 | 21 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 22 | // Insert code here to tear down your application 23 | } 24 | 25 | 26 | - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { 27 | return YES; 28 | } 29 | 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /MPSPathTracingSample/macOS/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | Default 530 | 531 | 532 | 533 | 534 | 535 | 536 | Left to Right 537 | 538 | 539 | 540 | 541 | 542 | 543 | Right to Left 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | Default 555 | 556 | 557 | 558 | 559 | 560 | 561 | Left to Right 562 | 563 | 564 | 565 | 566 | 567 | 568 | Right to Left 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | -------------------------------------------------------------------------------- /MPSPathTracingSample/macOS/GameViewController.h: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Header for our macOS view controller 6 | */ 7 | 8 | #import 9 | #import 10 | #import 11 | #import "Renderer.h" 12 | 13 | // Our macOS view controller. 14 | @interface GameViewController : NSViewController 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /MPSPathTracingSample/macOS/GameViewController.m: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Implementation for our macOS view controller 6 | */ 7 | 8 | #import "GameViewController.h" 9 | #import "Renderer.h" 10 | 11 | @implementation GameViewController 12 | { 13 | MTKView *_view; 14 | 15 | Renderer *_renderer; 16 | } 17 | 18 | - (void)viewDidLoad 19 | { 20 | [super viewDidLoad]; 21 | 22 | _view = (MTKView *)self.view; 23 | 24 | // Set color space of view to SRGB 25 | CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceLinearSRGB); 26 | _view.colorspace = colorSpace; 27 | CGColorSpaceRelease(colorSpace); 28 | 29 | // Lookup high power GPU if this is a discrete GPU system 30 | NSArray> *devices = MTLCopyAllDevices(); 31 | 32 | id device = devices[0]; 33 | 34 | for (id potentialDevice in devices) { 35 | if (!potentialDevice.lowPower) { 36 | device = potentialDevice; 37 | break; 38 | } 39 | } 40 | 41 | _view.device = device; 42 | 43 | if(!_view.device) 44 | { 45 | NSLog(@"Metal is not supported on this device"); 46 | self.view = [[NSView alloc] initWithFrame:self.view.frame]; 47 | return; 48 | } 49 | 50 | _renderer = [[Renderer alloc] initWithMetalKitView:_view]; 51 | 52 | [_renderer mtkView:_view drawableSizeWillChange:_view.bounds.size]; 53 | 54 | _view.delegate = _renderer; 55 | } 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /MPSPathTracingSample/macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | Copyright © 2018 Apple. All rights reserved. 27 | NSMainStoryboardFile 28 | Main 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /MPSPathTracingSample/macOS/main.m: -------------------------------------------------------------------------------- 1 | /* 2 | See LICENSE folder for this sample’s licensing information. 3 | 4 | Abstract: 5 | Main Application Entrypoint 6 | */ 7 | 8 | #import 9 | 10 | int main(int argc, const char * argv[]) { 11 | return NSApplicationMain(argc, argv); 12 | } 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Metal for Accelerating Ray Tracing 2 | 3 | Use the Metal Performance Shaders ray intersector to perform ray-traced rendering. 4 | 5 | ***Note: This repo is based on Apple's [Metal for Accelerating Ray Tracing](https://developer.apple.com/documentation/metalperformanceshaders/metal_for_accelerating_ray_tracing) example available at the Developer portal. The features like reflection, refraction, OBJ Import and texture mapping are added on top of the original codebase.*** 6 | 7 | ## Final Output 8 | ![Ray tracing on Metal devices with OBJ file import with texture mapping and reflection.](Documentation/final.png) 9 | 10 | ## Overview 11 | 12 | This sample walks you through the steps typically followed for ray tracing a scene: 13 | 14 | 1. Casting _primary rays_ from the camera to the scene and computing shading at the nearest intersection point; that is, the point nearest to the camera where a ray hits geometry. 15 | 2. Casting _shadow rays_ from the intersection point to the light source. If a shadow ray doesn't reach the light because of intersecting geometry, the intersection point is in shadow. 16 | 3. Casting _secondary rays_ from the intersection point in random directions to simulate light bouncing. Lighting contributions are added where the secondary rays intersect geometry. 17 | 18 | Ray-tracing apps spend a significant amount of time computing ray-triangle intersections, so the efficiency of the intersection algorithm impacts rendering performance. Metal Performance Shaders solves this intersection problem with a high-performance intersector. 19 | 20 | The Metal Performance Shaders [MPSRayIntersector](https://developer.apple.com/documentation/metalperformanceshaders/mpsrayintersector) class accelerates ray-triangle intersection tests on the GPU. It accepts rays through a Metal buffer and returns either the closest intersection (for primary rays) or any intersection (for shadow rays) along each ray through a Metal buffer. 21 | 22 | Metal Performance Shaders builds a data structure called an _acceleration structure_ that optimizes computing intersections. 23 | 24 | Metal Performance Shaders builds the acceleration structure from vertices that describe the triangles in a scene. To search for intersections, you provide the acceleration structure to an intersector. 25 | 26 | In the real world, photons are emitted from light sources and end in the camera. Simulating real-world conditions is computationally expensive, so in this sample, rays originate from the camera. 27 | 28 | ## Cast Primary Rays 29 | 30 | ![Image showing two boxes rendered using only primary rays. There are no shadows, and geometry not in direct light is black.](Documentation/primary_2x.png) 31 | 32 | Primary rays render an image that’s equivalent to an image rendered by a rasterizer. 33 | 34 | **Create an Intersector** 35 | 36 | Instantiate an intersector using a [MTLDevice](https://developer.apple.com/documentation/metal/mtldevice) object: 37 | 38 | ``` objective-c 39 | _intersector = [[MPSRayIntersector alloc] initWithDevice:_device]; 40 | ``` 41 | 42 | **Create and Build an Acceleration Structure** 43 | 44 | Create a triangle acceleration structure, specifying the device, the vertex buffer, and the number of triangles: 45 | 46 | ``` objective-c 47 | _accelerationStructure = [[MPSTriangleAccelerationStructure alloc] initWithDevice:_device]; 48 | 49 | _accelerationStructure.vertexBuffer = _vertexPositionBuffer; 50 | _accelerationStructure.maskBuffer = _triangleMaskBuffer; 51 | _accelerationStructure.triangleCount = vertices.size() / 3; 52 | 53 | [_accelerationStructure rebuild]; 54 | ``` 55 | 56 | **Generate Primary Rays** 57 | 58 | Each primary ray starts at the camera position and passes through a pixel on the image plane, resulting in one primary ray per pixel. This sample generates rays with a compute kernel that computes the origin and direction of each ray: 59 | 60 | ``` metal 61 | // Rays start at the camera position 62 | ray.origin = camera.position; 63 | 64 | // Map normalized pixel coordinates into camera's coordinate system 65 | ray.direction = normalize(uv.x * camera.right + 66 | uv.y * camera.up + 67 | camera.forward); 68 | ``` 69 | 70 | Assign each ray a fully white color that's scaled as light is absorbed into surfaces: 71 | 72 | ``` metal 73 | ray.color = float3(1.0f, 1.0f, 1.0f); 74 | ``` 75 | 76 | **Intersect Rays with the Scene** 77 | 78 | The intersector’s [encodeIntersection](https://developer.apple.com/documentation/metalperformanceshaders/mpsrayintersector/2998434-encodeintersection) method computes intersections and encodes its results to a Metal command buffer. 79 | 80 | For primary rays, set the intersection type so that the intersector returns the intersections that are closest to the camera ([MPSIntersectionTypeNearest](https://developer.apple.com/documentation/metalperformanceshaders/mpsintersectiontype/mpsintersectiontypenearest?language=objc)). Then pass the ray buffer containing the rays generated in the previous step, an intersection buffer to receive the intersection results, the ray count (in this case, the number of pixels), and the acceleration structure: 81 | 82 | ``` objective-c 83 | [_intersector encodeIntersectionToCommandBuffer:commandBuffer // Command buffer to encode into 84 | intersectionType:MPSIntersectionTypeNearest // Intersection test type 85 | rayBuffer:_rayBuffer // Ray buffer 86 | rayBufferOffset:0 // Offset into ray buffer 87 | intersectionBuffer:_intersectionBuffer // Intersection buffer (destination) 88 | intersectionBufferOffset:0 // Offset into intersection buffer 89 | rayCount:width * height // Number of rays 90 | accelerationStructure:_accelerationStructure]; // Acceleration structure 91 | ``` 92 | 93 | **Use the Intersection Results to Shade the Final Image** 94 | 95 | Another compute kernel applies lighting and textures based on the intersection point and vertex attributes. This shading kernel has one thread per pixel and takes the place of the fragment shader in a rasterization workflow. Unlike with fragment shaders that interpolate vertex attributes, with a shading kernel, you do the interpolation yourself. 96 | 97 | Intersections in the shading kernel are defined by the distance between the intersecting ray's origin and the geometry, the primitive index, and a two-element vector that represents the barycentric coordinates of the intersection on the triangle. 98 | 99 | Use the barycentric coordinates to interpolate vertex attributes at the intersection point on a triangle. The following function returns an interpolated vertex attribute of an arbitrary type across the surface of a triangle: 100 | 101 | ``` metal 102 | template 103 | inline T interpolateVertexAttribute(device T *attributes, Intersection intersection) { 104 | // Barycentric coordinates sum to one 105 | float3 uvw; 106 | uvw.xy = intersection.coordinates; 107 | uvw.z = 1.0f - uvw.x - uvw.y; 108 | 109 | unsigned int triangleIndex = intersection.primitiveIndex; 110 | 111 | // Lookup value for each vertex 112 | T T0 = attributes[triangleIndex * 3 + 0]; 113 | T T1 = attributes[triangleIndex * 3 + 1]; 114 | T T2 = attributes[triangleIndex * 3 + 2]; 115 | 116 | // Compute sum of vertex attributes weighted by barycentric coordinates 117 | return uvw.x * T0 + uvw.y * T1 + uvw.z * T2; 118 | } 119 | ``` 120 | 121 | ## Add Shadows 122 | 123 | ![Image showing two boxes rendered using primary rays and shadow rays. Shadows and geometry not in direct light are black.](Documentation/shadow_2x.png) 124 | 125 | Add shadows by casting a _shadow ray_ from the intersection point to the light source. If the shadow ray doesn’t reach the light source (if some other geometry is blocking the path between the intersection and the light), that intersection point is in shadow, and you shouldn’t add its color to the image. 126 | 127 | Shadow rays differ from primary rays in these ways: 128 | 129 | * They require the maximum intersection distance to avoid overshooting the light source. 130 | * The primitive index and coordinates of the intersection are unimportant. 131 | * They propagate the color from the shading kernel to the final kernel. 132 | 133 | **Configure the Intersector for Shadow Rays** 134 | 135 | You can reuse the intersector and acceleration structure you used for primary rays to compute shadow ray intersections, but you'll need to configure it to use a ray data type that supports shadow rays, based on the differences listed above. You can add your own properties to the ray structure and specify the intersector's ray stride to make the intersector skip over the additional data when reading from the ray buffer. 136 | 137 | Because shadows require neither a triangle index nor coordinates, set the intersector's intersection data type to [MPSIntersectionDataType.distance](https://developer.apple.com/documentation/metalperformanceshaders/mpsintersectiondatatype/distance): 138 | 139 | ``` objective-c 140 | _intersector.intersectionDataType = MPSIntersectionDataTypeDistance; 141 | ``` 142 | 143 | **Compute the Shadow Ray Intersections** 144 | 145 | Unlike with primary ray intersections, where you need to know the nearest surface to the camera that intersects the ray, it doesn't matter which surface intersects a shadow ray. If triangles exist between the primary intersection and the light source, the primary intersection is shadowed. Therefore, when computing shadow ray intersections, set the intersector type to [MPSIntersectionTypeAny](https://developer.apple.com/documentation/metalperformanceshaders/mpsintersectiontype/mpsintersectiontypeany?language=objc): 146 | 147 | ``` objective-c 148 | [_intersector encodeIntersectionToCommandBuffer:commandBuffer 149 | intersectionType:MPSIntersectionTypeAny 150 | rayBuffer:_shadowRayBuffer 151 | rayBufferOffset:0 152 | intersectionBuffer:_intersectionBuffer 153 | intersectionBufferOffset:0 154 | rayCount:width * height 155 | accelerationStructure:_accelerationStructure]; 156 | ``` 157 | 158 | **Add the Shadows** 159 | 160 | The shadow kernel, like the shading kernel, has one thread per pixel. If the shadow ray’s intersection distance is negative, it means the intersection point wasn't in shadow (it reached the light source). The kernel adds the color propagated from the shading kernel to the output: 161 | 162 | ``` metal 163 | kernel void shadowKernel(uint2 tid [[thread_position_in_grid]], 164 | constant Uniforms & uniforms, 165 | device Ray *shadowRays, 166 | device float *intersections, 167 | texture2d srcTex, 168 | texture2d dstTex) 169 | { 170 | if (tid.x < uniforms.width && tid.y < uniforms.height) { 171 | unsigned int rayIdx = tid.y * uniforms.width + tid.x; 172 | device Ray & shadowRay = shadowRays[rayIdx]; 173 | 174 | // Use the MPSRayIntersection intersectionDataType property to return the 175 | // intersection distance for this kernel only. You don't need the other fields, so 176 | // you'll save memory bandwidth. 177 | float intersectionDistance = intersections[rayIdx]; 178 | 179 | float3 color = srcTex.read(tid).xyz; 180 | 181 | // If the shadow ray wasn't disabled (max distance >= 0) and it didn't hit anything 182 | // on the way to the light source, add the color passed along with the shadow ray 183 | // to the output image. 184 | if (shadowRay.maxDistance >= 0.0f && intersectionDistance < 0.0f) 185 | color += shadowRay.color; 186 | 187 | // Write result to render target 188 | dstTex.write(float4(color, 1.0f), tid); 189 | } 190 | } 191 | ``` 192 | 193 | ## Simulate Light Bouncing with Secondary Rays 194 | 195 | ![Image showing two boxes rendered using primary rays and shadow rays. Shadows are soft, and geometry not in direct light is illuminated by the secondary rays.](Documentation/secondary_2x.png) 196 | 197 | Secondary rays simulate light bouncing around the scene, adding diffuse reflected light to areas in shadow. This effect is difficult to simulate with a rasterizer, but simple with a ray tracer. You can implement secondary rays by looping over the kernels, applying a random direction to rays with each iteration. 198 | 199 | This sample chooses a random direction for each secondary ray with a probability proportional to the cosine (dot product) of the angle between the sample direction and surface normal. This sampling strategy reduces the amount of noise in the output image. 200 | 201 | The `sampleCosineWeightedHemisphere` function uses the inversion method to map two uniformly random numbers to a three-dimensional unit hemisphere. The probability of a given sample is proportional to the cosine of the angle between the sample direction and the "up" direction `(0, 1, 0)`. 202 | 203 | ``` metal 204 | inline float3 sampleCosineWeightedHemisphere(float2 u) { 205 | float phi = 2.0f * M_PI_F * u.x; 206 | 207 | float cos_phi; 208 | float sin_phi = sincos(phi, cos_phi); 209 | 210 | float cos_theta = sqrt(u.y); 211 | float sin_theta = sqrt(1.0f - cos_theta * cos_theta); 212 | 213 | return float3(sin_theta * cos_phi, cos_theta, sin_theta * sin_phi); 214 | } 215 | ``` 216 | 217 | ## Texture Mapping 218 | 219 | ![Image shows PNG texture mapped on to the cube](Documentation/texture_2x.png) 220 | 221 | The small cube is now coloured using a texture map. Texture coordinates, and the related attributes are passed to the kernel for loading the surface colour from the given png texture image. 222 | 223 | The boolean value hasTexture represents whether the triangle has a texture mapped or not. And the Texture coordinates are used to get the colour. 224 | 225 | ``` 226 | uint hasTex = interpolateVertexAttribute(hasTexture, intersection); 227 | if (hasTex == 1) { 228 | float2 texCoord = interpolateVertexAttribute(textureCoords, intersection); 229 | constexpr sampler sam(min_filter::nearest, mag_filter::nearest, mip_filter::none); 230 | color *= colorTex.sample(sam, texCoord).xyz; 231 | } else { 232 | // Interpolate the vertex color at the intersection point 233 | color *= interpolateVertexAttribute(vertexColors, intersection); 234 | } 235 | ``` 236 | 237 | ## Reflection for metalic surface 238 | 239 | ![Image shows fully reflective spheres](Documentation/reflection_2x.png) 240 | 241 | If the surface has reflection value, the ray is reflected on the surface by calculating the normal using reflect function. 242 | 243 | ``` 244 | float reflectionIndex = interpolateVertexAttribute(reflection, intersection); 245 | if (reflectionIndex > 0.0f) { 246 | float3 reflectDirection = reflect(ray.direction.xyz, surfaceNormal); 247 | 248 | ray.origin = intersectionPoint + reflectDirection * 1e-3f; 249 | ray.direction = reflectDirection; 250 | ray.color = color; 251 | ray.mask = RAY_MASK_SECONDARY; 252 | } else { 253 | ``` 254 | 255 | ## Refraction for transparent surface 256 | 257 | ![Image shows fully reflective spheres](Documentation/final.png) 258 | 259 | If the surface has transparency value and refraction index, the ray is refract through the surface by calculating the normal using refract function. 260 | 261 | ``` 262 | float refractionIndexValue = interpolateVertexAttribute(refractionIndex, intersection); 263 | bool inside = false; 264 | if (dot(ray.direction, surfaceNormal) > 0) { 265 | inside = true; 266 | surfaceNormal = -surfaceNormal; 267 | } 268 | float eta = (inside) ? refractionIndexValue : 1.0f / refractionIndexValue; 269 | float3 rayDirection = refract(ray.direction.xyz, surfaceNormal, eta); 270 | 271 | ray.origin = intersectionPoint + rayDirection * 1e-3f; 272 | ray.direction = rayDirection; 273 | ray.color = color * refractionValue; 274 | ray.mask = RAY_MASK_PRIMARY; 275 | ray.maxDistance = INFINITY; 276 | shadowRay.maxDistance = -1.0f; 277 | 278 | ``` 279 | 280 | 281 | 282 | -------------------------------------------------------------------------------- /crate.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetiger/MetalRayTracing/87f6f72334b1ae71be7fcad287ab71640a8f0668/crate.jpg -------------------------------------------------------------------------------- /mario.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codetiger/MetalRayTracing/87f6f72334b1ae71be7fcad287ab71640a8f0668/mario.png --------------------------------------------------------------------------------