swift arkit源码_如何在Swift和ARKit中可视化随机分布算法
swift arkit源码
by Dan Wyszynski
通过Dan Wyszynski
如何在Swift和ARKit中可视化随机分布算法 (How to visualize random distribution algorithms in Swift and ARKit)
森林里的一棵树 (A tree in the forest)
I was recently working on a prototype where I needed to place a large amount of objects in 3D space. This was an AR project, and the thought was that these objects would be placed around you in a random fashion, scattering around you as they dropped in from the sky.
我最近正在开发一个原型,需要在3D空间中放置大量对象。 这是一个AR项目,并且认为这些对象将以随机的方式放置在您周围,并在它们从天上掉落时散落在您周围。
This brings up a few problems. First, we don’t know the current surroundings of the user, so we have to limit the radius of the dropped items to something that we can configure based on location or other factors.
这带来了一些问题。 首先,我们不知道用户的当前周围环境,因此我们必须将放置物品的半径限制为我们可以根据位置或其他因素配置的物品。
Next, we know that built-in random generators are not very random unless specific precautions are taken.
接下来,我们知道内置的随机生成器不是非常随机的,除非采取特定的预防措施。
Lastly, generating points with random generators, no matter how random, results in clustering, where many of the generated points can fall in close areas to one another, and leave you with spots that are bald of any points. And nobody wants bald spots: trust me, I know.
最后,无论随机性如何,使用随机生成器生成点都会导致聚类,其中许多生成的点可能会落在彼此靠近的区域中,并留下任何点都秃顶的斑点。 没有人想要秃头:相信我,我知道。
There are a number of ways to accomplish random equal distribution. Like any developer worth his salt, I found a couple which have simple implementations that work well for our purpose. Let’s examine each one, and implement them in Swift using ARKit and SceneKit.
有多种方法可以实现随机均等分布。 像任何值得他投入工作的开发人员一样,我发现一对夫妇的简单实现对我们的目标很有效。 让我们检查每个组件,并使用ARKit和SceneKit在Swift中实现它们。
掷骰子! (Roll them dice!)
Before we get to the “good” algorithms, we should look to see what we get by using just random numbers to place our points. In doing this, we’ll get our app put together and use it to test out the other implementations in the same form.
在使用“好的”算法之前,我们应该看一下通过仅使用随机数放置点来获得的结果。 为此,我们将把我们的应用程序放在一起,并使用它以相同的形式测试其他实现。
Let’s create our app and get some of the basic stuff in place. Open up Xcode and create a new project using the Augmented Reality App template. Build and run the app to make sure everything is working and you see the default ship appear in front of your phone when running the app.
让我们创建我们的应用程序,并准备一些基本的东西。 打开Xcode并使用Augmented Reality App模板创建一个新项目。 生成并运行该应用程序,以确保一切正常,并且在运行该应用程序时,您会在手机前看到默认的发货。
Next, we’re going set up our project like we did for the first tutorial in my ARKit series. Follow the steps in that tutorial, with the one difference being the name of the scene file. Instead of naming it HoverScene, we’ll name it MainScene instead. Add the render delegate as described in the tutorial, and follow the Extra Credits section where the tap gesture recognizer is created.
接下来,我们将像在ARKit系列中的第一个教程中那样设置项目。 按照该教程中的步骤进行操作,唯一的区别是场景文件的名称。 与其命名为HoverScene , 不如将其命名为MainScene 。 按照教程中的说明添加渲染委托,然后按照“创建点数”部分中的创建点击手势识别器的步骤进行操作。
At this point the project is almost ready, but we don’t have (nor need) the addSphere method that is referenced in that tutorial. Instead, we will begin creating our algorithm generators.
至此,项目几乎已经准备就绪,但是我们(也不需要)该教程中引用的addSphere方法。 相反,我们将开始创建算法生成器。
Create a file called PointGenerator.swift. In here we’ll put several iterations of our algorithms. Let’s begin with the random number point generator. We’ll create a protocol that our algorithms will adhere to, making it easy to switch between algorithms in our source later on.
创建一个名为PointGenerator.swift的文件。 在这里,我们将对算法进行几次迭代。 让我们从随机数点生成器开始。 我们将创建一个算法将遵循的协议,以便日后轻松在源代码中的算法之间进行切换。
Our protocol is simple. Given a number of points to generate and a width and length to limit the points to, give back an array of points:
我们的协议很简单。 给定要生成的点数以及将点限制为的宽度和长度,请返回一个点数组:
Our RandomPointGenerator will adhere to this class and output our first set of results:
我们的RandomPointGenerator将遵循此类并输出我们的第一组结果:
The code here is simple. We iterate and create points that lie within the width and length limits, placing points on either side of the midpoint of those limits. We add the created points to an array then simply return the points.
这里的代码很简单。 我们迭代并创建位于宽度和长度限制内的点,将点放置在这些限制的中点的任一侧。 我们将创建的点添加到数组中,然后简单地返回这些点。
Create a class called Visualizer derived from SCNNode. This class will serve as the container that holds the objects that we’ll place in the world to visualize the point set. For the moment, we’re going to create small spheres at each point generated by our algorithms.
创建一个从SCNNode派生的名为Visualizer的类。 此类将用作容纳将放置在世界上以可视化点集的对象的容器。 目前,我们将在算法生成的每个点上创建一个小球体。
This is what our class should look like:
这是我们的类的样子:
Alright, now we’re ready to create our points. Let’s go back to our MainScene class and add a method called createPointField, which takes in a SCNVector3 position:
好了,现在我们可以创建点了。 让我们回到MainScene类,添加一个名为createPointField的方法,该方法将处于SCNVector3位置:
We’re going to call this from our ViewController when we tap on the screen. Let’s go to our didTapScreen method and make the part where we previously created a sphere (in that first tutorial) look like the following:
当我们点击屏幕时,我们将从ViewController调用它。 让我们转到didTapScreen方法,使之前创建球体的部分(在第一个教程中)如下所示:
Build and run, and we now have our first algorithm implemented.
构建并运行,现在我们实现了第一个算法。
Notice how the spheres are clustered in certain spots. This is exactly what we want to avoid.
请注意,球体是如何聚集在某些位置的。 这正是我们要避免的。
I won’t get into detailed descriptions of each algorithm, but I’ll provide links to the sites that I found informative and gave me the inspiration to implement in Swift and AR.
我不会详细介绍每种算法,但会提供指向我认为有帮助的站点的链接,并为我提供了在Swift和AR中实现的灵感。
泊松抽样和米切尔的最佳人选 (Poisson Sampling and Mitchell’s best-candidate)
What we need as an alternative to the random number generator way is an algorithm which returns a set of points that are close to each other, but no closer than some specified minimum distance. That’s where Poisson-disc sampling comes into the picture. There are several ways of implementing the Poisson-disc algorithm. The one we’re going to be implementing in our code is called Mitchell’s best-candidate algorithm. It’s easy to implement and runs fast.
作为随机数生成器方法的替代方案,我们需要一种算法,该算法返回一组彼此靠近但不比指定的最小距离更近的点。 这就是Poisson光盘采样的用武之地。 有几种实现Poisson-disc算法的方法。 我们将在代码中实现的一种称为Mitchell的最佳候选算法。 它易于实施且运行速度很快。
The idea behind the algorithm is to place down points, and as you place them, check whether they meet the requirement of being at least the minimum distance away from the points already placed. To do this, you sample the point as you place it, by looking at the distance that nearby points have. If there are no points within the minimum requirement, place the new point, otherwise, try to find another location. You can read more about the algorithm here.
该算法的思想是放置下降点,并在放置它们时检查它们是否满足与已放置的点至少相距最小距离的要求。 为此,您可以在放置点时通过查看附近点的距离来对其进行采样。 如果在最低要求范围内没有点,则放置新点,否则,尝试寻找其他位置。 您可以在此处阅读有关该算法的更多信息。
To implement, we’re going to create another implementation of our PointGenerator protocol:
为了实现,我们将创建PointGenerator协议的另一个实现:
Let’s head back to our MainScene class, comment out the random generator lines, and add these new lines in:
让我们回到MainScene类,注释掉随机生成器行,并将这些新行添加到:
Run the app again, and let’s look at our results.
再次运行该应用程序,让我们看看结果。
Much better! There are no big clusters or barren areas. This is now something we can use to place items in the world. Other uses for this algorithm include things like dynamically generating textures at runtime or noise generators.
好多了! 没有大的集群或荒芜的地区。 现在,我们可以使用它在世界上放置物品。 该算法的其他用途包括在运行时动态生成纹理或生成噪声。
We have what we need with this implementation, and we’re going to make use of it later. But what if we wanted to have something a bit different, where we need uniform distribution within a circular boundary? This is where the Sunflower Seed algorithm comes in.
我们已经有了实现所需的东西,稍后我们将使用它。 但是,如果我们想要一些不同的东西,我们需要在圆形边界内均匀分布怎么办? 这就是向日葵种子算法的用武之地。
额外信用 (Extra Credit)
葵花籽算法 (Sunflower Seed Algorithm)
Throughout history, we have found mathematical patterns in nature. One of the interesting features in many of these patterns that are mimicked in nature is the existence of the Fibonacci sequence in plants. These features manifest themselves in spiral patterns in leaves, seeds and petal arrangements. The study of these patterns is called Phyllotaxis. The following algorithm implements the mathematical model of one of these spirals. You can find more info here and here.
纵观历史,我们发现了自然界中的数学模式。 在自然界中被模仿的许多模式中,有趣的特征之一是植物中斐波那契序列的存在。 这些特征以叶片,种子和花瓣排列的螺旋形形式表现出来。 对这些模式的研究称为音律 。 以下算法实现了这些螺旋之一的数学模型。 您可以在此处和此处找到更多信息。
Let’s go back to our PointGenerator file and create our new implementation:
让我们回到PointGenerator文件并创建新的实现:
You’ll notice here that we are ignoring the width and height parameters passed in. This is because instead of constraining the points to a region, we will be evenly distributing the points in a spiral fashion until we run out of points.
您会在此处注意到,我们忽略了传入的width和height参数。这是因为,我们将以螺旋方式均匀分布点,直到用尽所有点,而不是将点约束到某个区域。
Changing the alpha in the parameter passed in to the sunflower method controls the granularity of the points at the edge of the boundary. That is, we can make the boundary smoother or rougher by controlling the point distribution. The above code uses an alpha of 2, which is on the high side, and results in a more even boundary.
更改传入sunflower方法的参数中的alpha ,可以控制边界边缘点的粒度。 也就是说,我们可以通过控制点分布来使边界更平滑或更粗糙。 上面的代码使用2的alpha值,该值偏高,并且边界更均匀。
Let’s go to our MainScene again, and comment out the previous algorithm. Let’s add in a call to get our Sunflower pattern generating points:
让我们再次进入MainScene ,并注释掉先前的算法。 让我们添加一个调用来获取我们的Sunflower模式生成点:
Let’s run our app again and see what we get.
让我们再次运行我们的应用程序,看看会得到什么。
As you can see, we have a pattern that mimics the way sunflowers hold their seeds. There is also an interesting variation we can apply to the algorithm, as detailed in one of the comments in this Stack Overflow question.
如您所见,我们有一个模仿向日葵固定种子的方式的图案。 我们还可以对算法应用一个有趣的变化,如该Stack Overflow 问题中的注释之一所详述。
By changing the theta to a bearing, the commenter turned the algorithm into a geodesic formation.
通过将theta更改为方位角,评论者将算法转换为测地线形式。
Change the theta line in our code to the following:
将我们代码中的theta行更改为以下内容:
Let’s run our algorithm again and see what it looks like.
让我们再次运行算法,看看它是什么样子。
Cool! Now we have the pattern as spirals.
凉! 现在我们有了螺旋式的模式。
Speaking of spirals, let’s check out one last algorithm.
说到螺旋,让我们看看最后一种算法。
沃格尔螺旋 (The Vogel Spiral)
Here we have another closely related algorithm which also uses a Fibonacci sequence and the Golden Angle. You can read more about the Vogel Spiral here and here.
在这里,我们还有另一个密切相关的算法,该算法也使用斐波那契数列和黄金角 。 您可以在此处和此处阅读有关Vogel螺旋的更多信息。
Let’s implement it, and then we’ll tweak it to see how it influences the results.
让我们实现它,然后我们对其进行调整,以了解它如何影响结果。
In our PointGenerator class, add our implementation of this algorithm.
在我们的PointGenerator类中,添加我们对该算法的实现。
This algorithm, like the Sunflower Seed algorithm, also ignores the width and height parameters.
与“向日葵种子”算法一样,该算法也忽略了width和height参数。
Let’s replace the previous algorithm with our new calls.
让我们用新的调用替换以前的算法。
Let’s give that a run and see what we get.
让我们来看看我们得到了什么。
Nice! Now, let’s try different variations of this algorithm. By changing the formula, we can get different spiral formations. Change the it declaration to the following line:
真好! 现在,让我们尝试该算法的不同变体。 通过更改公式,我们可以获得不同的螺旋形式。 将it声明更改为以下行:
Run that and check out what we get.
运行并检查我们得到了什么。
That spiral now looks like a flipped version of our Sunflower spiral with the updated theta. Interesting!
现在,该螺旋看起来像是向日葵螺旋的翻转版本,带有更新的theta 。 有趣!
Let’s try it with the following formula:
让我们尝试使用以下公式:
Similar to the first algorithm, but the spirals are separated. Very cool stuff!
与第一种算法相似,但螺旋线是分开的。 很酷的东西!
We’ve now run through 4 different algorithms, including the not-very-good strictly random number placement. Each one of these has a place in our toolbox, though, and they can be used to fulfill a variety of needs.
现在,我们已经经历了4种不同的算法,包括不太好使用的严格随机数放置。 不过,每个工具在我们的工具箱中都有一个位置,可用于满足各种需求。
额外信用,第二部分 (Extra Credit, Part Two)
It wouldn’t be an article of mine if we didn’t do something cool with what we just learned, now would it?
如果我们不对刚刚学到的东西做一些很棒的事情,那将不是我的文章,现在呢?
Download some tree models from a 3D model store like Sketchfab or Turbosquid. Convert them to Collada (DAE) format as needed and add them to your project. You may need to resize them to be the proper scale when putting them in your scene, but you’ll know when you go to use them. Be sure to use a low poly model since we are talking about instantiating dozens and even hundreds of object instances.
从3D模型商店(如Sketchfab或Turbosquid)下载一些树模型。 根据需要将它们转换为Collada(DAE)格式,并将它们添加到您的项目中。 将它们放置在场景中时,可能需要将它们调整为适当的比例,但是您会知道何时使用它们。 确保使用低多边形模型,因为我们正在谈论实例化数十个甚至数百个对象实例。
Let’s make a Tree class that derives from SceneObject (we created this class back in our previous tutorial). We’ll make it load a random tree from the ones we have added into our app. We make use of the random convenience function we also added in a previous tutorial.
让我们创建一个从SceneObject派生的Tree类(我们在上一教程中创建了该类)。 我们将从加载到应用程序中的树中加载随机树。 我们利用在上一教程中也添加的随机便利函数。
Here’s what my Tree class looks like:
这是我的Tree类的样子:
Let’s use the Mitchell algorithm and bring down the number of models (points) we want to generate to 60. Depending on the amount of polygons in your models, this might be too large. I started with a different set of models and it took a while to place 20 models. Start low and work your way up. For the models I used I could go higher, but 60 was dense enough.
让我们使用Mitchell算法,将要生成的模型(点)数量减少到60个。根据模型中多边形的数量,这可能太大。 我从一组不同的模型开始,花了一段时间才放置了20个模型。 从低处开始,然后逐步上升。 对于我使用的模型,我可以更高,但是60足够密集。
In our Visualizer code, let’s change our Tree creation to animate the scaling up a bit.
在我们的Visualizer代码中,让我们更改Tree创建以动画化缩放比例。
In my case, with the models I used, I had to scale them down a bit to get them to a reasonable size which is where that 0.45 comes from. I also lowered the position a bit so that they lay on the ground plane. You can adjust these numbers to whatever fits your situation.
就我而言,对于我使用的模型,我不得不将它们缩小一点,以使其达到合理的大小,这就是0.45的来源。 我还降低了位置,使它们躺在地平面上。 您可以根据自己的情况调整这些数字。
Build and run, and now we have a happy little forest created with almost no effort.
构建并运行,现在我们几乎没有任何努力就创建了一个快乐的小森林。
Hope you enjoyed this little experiment. Feel free to show off your work in the comment section!
希望您喜欢这个小实验。 随时在评论部分中展示您的作品!
Daniel Wyszynski is a developer who’s worked on more platforms and languages than he cares to admit to. He believes in building products that create memorable user experiences. To Dan, the users are first, the platforms are second. Follow Dan on Medium or Twitter to hear more from him. Also check out the s23NYC: Engineering blog, where a lot of great content by Nike’s Digital Innovation team gets posted.
Daniel Wyszynski是一名开发人员,他使用的平台和语言超出了他的意愿。 他坚信能够创造令人难忘的用户体验的产品。 对于Dan来说,用户是第一,平台是第二。 在Medium或Twitter上关注Dan,以了解他的更多信息。 另外,请访问s23NYC:工程博客,其中发布了耐克数字创新团队的很多精彩内容。
Author’s Note: Views are my own and do not necessarily represent those of my employer
作者注:观点是我的观点,不一定代表雇主的观点
swift arkit源码
更多推荐
所有评论(0)