ARKit + SceneKit几何图形教程(第2部分)

如果您还没有,我强烈建议您阅读《 ARKit + SceneKit几何图形教程》(第1部分),其中有一些核心图形概念,您需要对其进行理解才能继续。

到目前为止,我已经向您展示了如何使用带有顶点,法线贴图和纹理贴图的原始类型重新创建SCNPlane和SCNBox的示例。 我接下来要讨论的是如何通过缩放,旋转或平移场景节点无法实现的方式使这些几何体生动起来。

这是本教程随附的GitHub存储库:

maxxfrazer / SCNCustomGeometries-Part2

伴随我的中篇文章的第2部分。 通过创建以下内容,为maxxfrazer / SCNCustomGeometries-Part2开发做出贡献:

github.com


我们要做的第一件事是获取多维数据集的单个顶点,并制作一个动画,使其看起来像是从多维数据集的中心拉开,然后随着其移回原位而产生摆动效果。 下面的GIF演示了动画:

单击此处在GitHub上查看此项目

这里的基本思想是,给定点[x,y,z]处的顶点,我想将其拉至[1.5x, 1.5y, 1.5y] ,然后将其释放回[x,y,z] 。 此处是随机选择顶点的,但是您可以添加hitTest来通过点击选择点。

当做这样的事情时,我发现使用图形计算器很有用,Desmos确实很棒,我已经使用了很多年了。

我建立了一个新类BoxStretcher ,它是SCNNode子类。 我还在SCNGeometry添加了另一个类函数,以返回具有8个顶点的标准多维数据集的顶点和索引。 我添加到BoxStretch的另一个有用函数是updateGeometry() ,它在给定更新后的顶点和索引的情况下创建SCNGeometry,并在顶点更新时重新分配节点的几何形状。

我发现为几何更新设置动画的最佳方法是使用SCNAction.customAction(duration:action :)。 无论您在代码块中放置什么,都称为每一帧,这是大多数动画所需要的。

这是animateCorner()的内容,它将在每次调用时更新顶点刷新几何:

上面看起来有点复杂,所以让我们分解一下。 elapsedTime将是一个数字[0–3],因此在开始的0.5秒内,我们将通过if而不是else。 使用Desmos,我们可以看到它的图形如下所示(将时间交换为x):

因此,当elapsedTime达到0.5时,顶点应为[x,y,z] *(1 + 0.5),这将是我们想要的最远距离。

现在,当我们在0.5之后引入该部分时,如果将其分解,则为正弦波乘以指数衰减。 没有衰减,它将永远继续摆动,但是有了衰减,我们得到了顶点的良好运动,速度变慢并回到零。 当我们有零时,我们有:[x,y,z] *(1 + 0.0); 回到我们的起点。

这是指向我的Desmos图的链接,沿y轴有一个点,该点应遵循与顶点完全相同的模式。

拉伸释放

拉伸释放

拉伸释放www.desmos.com

对不起,如果我在那里失去你。 关键是我们希望该点沿[x,y,z]方向移动,但是自从我们开始以来基于时间多少? 我们需要一个函数,该函数需要花费时间并输出我们应该沿着曲线移动多少。 这与用于缓动功能以及具有许多其他应用程序的原理相同。

这种方法效果很好,在另一个程序中将动画烘焙到模型之间的主要区别在于,每个帧的值都作为文件的一部分保存在其中,因此设备无需通过计算I即可计算出最终位置给了包括罪恶和指数。 烘焙动画的好处是它可以更快地计算这些值,但要权衡的是它要使用更多的存储空间,因此在加载对象时会占用内存。 您可以通过在首次加载模型时计算多个tNow值来找到中间立场。 这是经典的时空折衷方案,下面是如何创建查找表的示例:

然后将调用以下animateCornerPrecalc()代替animateCorner()

上述所有功能都包含在存储库中。

在本节中,让我们再次看一个平面而不是一个立方体。

一种使飞机动起来的用例是挥舞旗帜。 因此,在进行编码之前,我想使用Desmos为标记获得漂亮的曲线。

我有两个要求:将其固定在原点的x轴上,但是当该点远离标志的边缘时,它们可以与x轴更多地分开。 我希望像以前一样在这里形成正弦曲线,并随着时间的流逝线性增加波的大小。

这本质上是飞机俯视图 ,我将无法使用Desmos演示动画如何轻松地在第三轴上发生,但最后我将提供示例。 单击此处以链接到第二个Desmos图。

好的,因此将上面的公式放入我们的xCode项目中:

我在SCNGeometry中添加了另一个静态函数,以获取平面的顶点,纹理映射(作为几何源)和索引,该函数是PlaneParts(size: CGSize, xyCount: CGSize) ,它接受两个CGSize参数,第一个表示整体宽度和高度,第二个表示水平和垂直顶点的数量。 如果省略后者,则默认为最小2×2顶点。 我们拥有的顶点越多,标志看起来就越平滑,以每帧更多的计算和场景中更多的顶点为代价。 如果从另一个文件中导入几何图形,则无法执行此操作,这使您可以在创建模型时通过快速计算来知道模型将变小还是变小的情况下,调整顶点数。

我们正在制作的下一个类是FlagNode。

上面的函数与上一个示例中的animateCorner放在同一位置。 在这里, waveScale值等于上面我的紫色线Desmos GIF中的l(x)newZ的倍数的newZ组成了绿色曲线,结果遵循了两者的乘积; 红色曲线。 旁注:我们可能for x in 1..<xCount ,因为第一个始终为零,但我将其保留为在任何情况下都相同。

确定,因此可以创建此标志,这里是2 x 4顶点:

   newNode = FlagNode( 
frameSize:CGSize(宽度:0.75,高度:0.375),
xyCount:CGSize(width: 4 ,height:2),
扩散:UIImage(名称:“ union_jack”)

我们需要更多的三角形! 我们可以轻松地将其更新为40×2顶点。 从上到下更新顶点几乎没有意义,因为它们仍将落在一条直线上,因此不会对标志的外观产生任何影响,只是无缘无故地向场景添加更多计算。

这个看起来好多了,但是直到最后,关闭时我们仍然可以看到各个多边形,让我们再次设置它。

   newNode = FlagNode( 
frameSize:CGSize(宽度:0.75,高度:0.375),
xyCount:CGSize(width: 100 ,height:2),
扩散:UIImage(名称:“ union_jack”)

太好了,我们再也看不到该标志上的各个多边形,所以对我来说这是一个不错的结果! 顺便说一句,此GIF上的帧率不是很好,请克隆存储库以60fps的速度查看

最后,我将更新上面显示的动画,以取决于y轴和wave的x。

此处的主要区别在于,在第二个for循环中计算了总距离(曼哈顿距离),而沿X轴的距离与以前相同。 左边缘仍将锁定在适当的位置,但是朝着右边缘将有另一个波在Y轴上发生干扰。

为此,与上面的第一个标志GIF不同,我们增加了平面y轴上的顶点数量,以使其具有真正的流畅外观。

从这里,您可以直接在xCode中创建整个动画世界,而无需先学习如何使用Blender,3ds Max等3D图形程序。您还可以通过创建类似于本教程中创建的类的类来针对场景优化对象。 。

这是我无意中做出的最后一个标志,并将代码留在GitHub上供您使用,但是我基本上使正弦曲线的波长太短了!

在以后的教程中,我想谈一谈着色器,正如Alberto Taiuti指出的那样,它将是这些动画在诸如挥舞旗帜的对象上的首选替代方法。 着色器可以做很多事情,下面的代码向您展示了一种仅使用SceneKit中使用String的几何着色器就可以通过挥舞的动画制作标志的一种方法:

但是,您需要看到它需要另一种C风格语言的知识,因此值得在其全文中对其进行全面的解释和理解。 (快来了!)


谢谢阅读! 我希望您从本教程中学到了一些东西。 我希望很快能发表更多这些帖子。

如果不清楚,或在Twitter或LinkedIn上找到我,或者对此帖子有任何建议或对未来帖子的想法, 还可以在GitHub上查看我正在从事的其他公共项目。