-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathcreating-3d-models-out-of-anything-with-realitykit-in-swift.html
More file actions
337 lines (290 loc) · 17 KB
/
creating-3d-models-out-of-anything-with-realitykit-in-swift.html
File metadata and controls
337 lines (290 loc) · 17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://use.fontawesome.com/afd448ce82.js"></script>
<!-- Meta Tag -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<!-- SEO -->
<meta name="author" content="Bruno Rocha">
<meta name="keywords" content="Software, Engineering, Blog, Posts, iOS, Xcode, Swift, Articles, Tutorials, OBJ-C, Objective-C, Apple">
<meta name="description" content="RealityKit Object Capture is a feature introduced in Xcode 13 that allows you to create 3D objects from photographs using a process called photogrammetry.">
<meta name="title" content="Creating 3D Models from Photographs with RealityKit in Swift">
<meta name="url" content="https://swiftrocks.com/creating-3d-models-out-of-anything-with-realitykit-in-swift">
<meta name="image" content="https://swiftrocks.com/images/thumbs/thumb_dark.jpg">
<meta name="copyright" content="Bruno Rocha">
<meta name="robots" content="index,follow">
<meta property="og:title" content="Creating 3D Models from Photographs with RealityKit in Swift"/>
<meta property="og:image" content="https://swiftrocks.com/images/thumbs/thumb_dark.jpg"/>
<meta property="og:description" content="RealityKit Object Capture is a feature introduced in Xcode 13 that allows you to create 3D objects from photographs using a process called photogrammetry."/>
<meta property="og:type" content="website"/>
<meta property="og:url" content="https://swiftrocks.com/creating-3d-models-out-of-anything-with-realitykit-in-swift"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://swiftrocks.com/images/thumbs/thumb_dark.jpg"/>
<meta name="twitter:image:alt" content="Page Thumbnail"/>
<meta name="twitter:title" content="Creating 3D Models from Photographs with RealityKit in Swift"/>
<meta name="twitter:description" content="RealityKit Object Capture is a feature introduced in Xcode 13 that allows you to create 3D objects from photographs using a process called photogrammetry."/>
<meta name="twitter:site" content="@rockbruno_"/>
<meta name="fediverse:creator" content="@rockbruno@hachyderm.io">
<!-- Favicon -->
<!-- 16x16 -->
<link rel="shortcut icon" href="images/favicon/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="images/favicon/favicon_180.png" sizes="180x180">
<link rel="icon" href="images/favicon/favicon_32.png" sizes="32x32" type="image/png">
<link rel="icon" href="images/favicon/favicon_48.png" sizes="48x48" type="image/png">
<link rel="icon" href="images/favicon/favicon_96.png" sizes="96x96" type="image/png">
<link rel="icon" href="images/favicon/favicon_144.png" sizes="144x144" type="image/png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,200..900;1,200..900&display=swap" rel="stylesheet">
<link rel="canonical" href="https://swiftrocks.com/creating-3d-models-out-of-anything-with-realitykit-in-swift"/>
<!-- Bootstrap CSS Plugins -->
<link rel="stylesheet" type="text/css" href="css/bootstrap.css">
<!-- Prism CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/prism5.css">
<!-- Main CSS Stylesheet -->
<link rel="stylesheet" type="text/css" href="css/style49.css">
<link rel="stylesheet" type="text/css" href="css/sponsor5.css">
<!-- HTML5 shiv and Respond.js support IE8 or Older for HTML5 elements and media queries -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
<![endif]-->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"mainEntityOfPage": {
"@type": "WebPage",
"@id": "https://swiftrocks.com/creating-3d-models-out-of-anything-with-realitykit-in-swift"
},
"image": [
"https://swiftrocks.com/images/thumbs/thumb_dark.jpg"
],
"datePublished": "2021-06-22T14:00:00+02:00",
"dateModified": "2021-06-22T14:00:00+02:00",
"author": {
"@type": "Person",
"name": "Bruno Rocha"
},
"publisher": {
"@type": "Organization",
"name": "SwiftRocks",
"logo": {
"@type": "ImageObject",
"url": "https://swiftrocks.com/images/thumbs/thumb_dark.jpg"
}
},
"headline": "Creating 3D Models from Photographs with RealityKit in Swift",
"abstract": "RealityKit Object Capture is a feature introduced in Xcode 13 that allows you to create 3D objects from photographs using a process called photogrammetry."
}
</script>
</head>
<body>
<div id="main">
<!-- Blog Header -->
<!-- Blog Post (Right Sidebar) Start -->
<div class="container">
<div class="col-xs-12">
<div class="page-body">
<div class="row">
<div><a class="logo-link" href="https://swiftrocks.com">
<img id="logo" class="logo" alt="SwiftRocks" src="images/bg/logo2dark.png">
</a>
<div class="menu-large">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-large">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
<div class="menu-small">
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-1">
<div class="menu-item">
<a href="blog">blog</a>
</div>
<div class="menu-item">
<a href="about">about</a>
</div>
<div class="menu-item">
<a href="talks">talks</a>
</div>
<div class="menu-item">
<a href="projects">projects</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
<div class="menu-arrow-right"></div>
<div class="menu-header menu-header-small-2">
<div class="menu-item">
<a href="software-engineering-book-recommendations">book recs</a>
</div>
<div class="menu-item">
<a href="games">game recs</a>
</div>
<div class="menu-arrow-right-2"></div>
</div>
</div>
</div>
<div class="content-page" id="WRITEIT_DYNAMIC_CONTENT">
<!--WRITEIT_POST_NAME=Creating 3D Models from Photographs with RealityKit in Swift-->
<!--WRITEIT_POST_HTML_NAME=creating-3d-models-out-of-anything-with-realitykit-in-swift-->
<!--Add here the additional properties that you want each page to possess.-->
<!--These properties can be used to change content in the template page or in the page itself as shown here.-->
<!--Properties must start with 'WRITEIT_POST'.-->
<!--Writeit provides and injects WRITEIT_POST_NAME and WRITEIT_POST_HTML_NAME by default.-->
<!--WRITEIT_POST_SHORT_DESCRIPTION=RealityKit Object Capture is a feature introduced in Xcode 13 that allows you to create 3D objects from photographs using a process called photogrammetry.-->
<!--DateFormat example: 2020-04-12T14:00:00+02:00-->
<!--WRITEIT_POST_SITEMAP_DATE_LAST_MOD=2021-06-22T14:00:00+02:00-->
<!--WRITEIT_POST_SITEMAP_DATE=2021-06-22T14:00:00+02:00-->
<title>Creating 3D Models from Photographs with RealityKit in Swift</title>
<div class="blog-post">
<div class="post-title-index">
<h1>Creating 3D Models from Photographs with RealityKit in Swift</h1>
</div>
<div class="post-info">
<div class="post-info-text">
Published on 22 Jun 2021
</div>
</div>
<p><i>RealityKit Object Capture</i> is a feature introduced in Xcode 13 that allows you to create 3D objects from photographs using a process called photogrammetry. Although intended for retailers to enhance their online shopping experience by creating models out of things like furniture and using them in augmented reality experiences, RealityKit is incredibly easy to use for anything you may want, like 3D printing random things from your house.</p>
<div class="post-image">
<img style="height: 300px;" src="https://i.imgur.com/sp4LypF.gif" alt="Mug">
</div>
<p>To use Object Capture, you must:</p>
<ul>
<li>Be running macOS 12 (this feature is not available in iOS!)</li>
<li>Provide photographs of the object you want to capture</li>
</ul>
<div class="sponsor-article-ad-auto hidden"></div>
<p>Capturing photographs of the object is not hard, although you may have to fine-tune your environment to get better results. The idea is to place your object in a well-lit place and continuously take pictures as you round the object. You can also take pictures of the bottom of the object if you'd like that to be modeled as well, by taking pictures of the object flipped upside-down in the same environment.</p>
<p>Your pictures don't need to have any particular ordering or naming for Object Capture to work and there's no minimum number of pictures you need to take, although you might better results the more you do. I've found that ~30ish pictures already give a nice result. Quite easy!</p>
<div class="post-image">
<img style="height: 300px;" src="https://i.imgur.com/9ZiPV1Y.gif" alt="Pictures example">
</div>
<h2>Creating a Photogrammetry CLI App</h2>
<p>To use Object Capture, start by creating a new Command Line App in Xcode and add new a class named <code>Session</code>:</p>
<pre><code>import Foundation
import RealityKit
import Combine
final class Session {
let inputFolder = URL(fileURLWithPath: "/Users/myUser/myPictures", isDirectory: true)
let outputFile = URL(fileURLWithPath: "/Users/myUser/result.usdz")
var subscriber: AnyCancellable?
func run() throws {
}
}</code></pre>
<p>Object Capture works by creating a <code>PhotogrammetrySession</code> object, configuring it, passing the folder that contains our pictures and waiting for the result. The result comes asynchronously through Combine, so make sure to create a <code>subscriber</code> object like in the snippet.</p>
<p>In the <code>run()</code> method, create a <code>PhotogrammetrySession</code> with the default configuration:</p>
<pre><code>let configuration = PhotogrammetrySession.Configuration()
let session = try PhotogrammetrySession(
input: inputFolder,
configuration: configuration
)</code></pre>
<p>It's possible to fine-tune the final model, which we'll see further on. For now, let's use the default settings.</p>
<p>With a session, we should now create a request to fabricate a model from our photographs:</p>
<pre><code>let request = PhotogrammetrySession.Request.modelFile(url: outputFile)</code></pre>
<p>To wrap it up, we should observe the progress of the request and wait for it to end. As mentioned before, this process is done asynchronously with Combine, so we should attach a subscriber to the session's <code>output</code> property.</p>
<p>Additionally, since this is a CLI app, we need to make sure the app stays alive while the model is being created. For simplicity, I've decided to attach a semaphore to the operation:</p>
<pre><code>let semaphore = DispatchSemaphore(value: 0)
subscriber = session.output.sink(receiveCompletion: { completion in
print(completion)
exit(0)
}, receiveValue: { output in
switch output {
case .processingComplete:
print("Processing is complete.")
semaphore.signal()
case .requestComplete(let request, let result):
print("Request complete.")
print(request)
print(result)
semaphore.signal()
case .requestProgress(let request, let fractionComplete):
print("Request in progress: \(fractionComplete)")
default:
print(output)
}
})
try session.process(requests: [request])
semaphore.wait()</code></pre>
<p>As you can see, many aspects of the process can be observed. In this case, I'm only interested in the actual progress.</p>
<p>Before running the app, edit your <code>main.swift</code> file to call our <code>run()</code> method:</p>
<pre><code>try! Session().run()</code></pre>
<p>The process takes a while to start and may take several minutes to finish, so don't worry if you get a bunch of prints but not progress at first. Wait a bit and it will start!</p>
<div class="post-image">
<img style="height: 300px;" src="https://i.imgur.com/QvvOiYE.gif" alt="Pictures result">
</div>
<h2>Configuring PhotogrammetrySessions</h2>
<p>To fine-tune your results, there are two aspects of Object Capture that can be configured. The first one is the detail of the output, which you can control to determine the number of polygons in the final model:</p>
<pre><code>let request = PhotogrammetrySession.Request.modelFile(
url: outputFile,
detail: .preview
)</code></pre>
<p>Details range from the lower quality <code>preview</code> to the high-end <code>full</code>. Try playing with lower-quality settings before trying to generate a higher-quality one.</p>
<p>The second aspect that can be configured in the input itself:</p>
<pre><code>var configuration = PhotogrammetrySession.Configuration()
configuration.sampleOverlap = .low
configuration.sampleOrdering = .unordered
configuration.featureSensitivity = .high</code></pre>
<div class="sponsor-article-ad-auto hidden"></div>
<p>The configuration struct allows you to provide more information about your photographs, which may result in a better model. <code>sampleOverlap</code> allows you to describe how much overlap there is between each photograph, <code>sampleOrdering</code> allows you to indicate whether or not your photographs are ordered (which will speed the process), and <code>featureSensitivity</code> indicates how hard RealityKit should try to search for features in your object, which should be used in cases where the object doesn't have a lot of discernible structures, edges or textures.</p>
</div></div>
<div class="blog-post footer-main">
<div class="footer-logos">
<a href="https://swiftrocks.com/rss.xml"><i class="fa fa-rss"></i></a>
<a href="https://hachyderm.io/@rockbruno"><svg class="svg-logo-link" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--!Font Awesome Free 6.7.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5 0 0 0-63.7 28.5-63.7 125.7 0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5 0 0 1 -.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6v-114.2c0-49.7-64-51.6-64 6.9v62.5h-46.3V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"/></svg></a>
<a href="https://twitter.com/rockbruno_"><i class="fa fa-twitter"></i></a>
<a href="https://github.com/rockbruno"><i class="fa fa-github"></i></a>
</div>
<div class="footer-text">
© 2025 Bruno Rocha
</div>
<div class="footer-text">
<p><a href="https://swiftrocks.com">Home</a> / <a href="blog">See all posts</a></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Blog Post (Right Sidebar) End -->
</div>
</div>
</div>
<!-- All Javascript Plugins -->
<script type="text/javascript" src="js/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
<script type="text/javascript" src="js/prism5.js"></script>
<!-- Main Javascript File -->
<script type="text/javascript" src="js/scripts30.js"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-H8KZTWSQ1R"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-H8KZTWSQ1R');
</script>
</body>
</html>