├── .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 |
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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
--------------------------------------------------------------------------------