SceneKit Cocoa 添加贴花

SceneKit Cocoa Adding decals

是否有足够的方法向 SCNNode 添加贴花(纹理)? 现在我所能做的就是用 SCNPlane 几何图形创建新的 SCNNode 为其漫反射内容分配纹理并将其添加到场景中...... 这种方法效率很低。

我想添加一个不会成为场景一部分的贴花 - 以提高游戏的性能。 因为我想在地板节点上有比较多的贴花。

也许有点类似于粒子内部的工作原理? 或者其他一些快速图像绘制方法。

我已经尝试过@rickster 的建议 1

Use SKEffectNode as a buffer in the SpriteKit scene — throw splatters into an effect node until it gets to a certain number of children, then turn on its shouldRasterize option so that it doesn't re-render its children anymore (and start using a new effect node for more splatters).

在同一个 SceneKit showreel 项目中 - 仅此一项就已经给出了很好的结果,FPS 在继续播放场景后 5 分钟内没有下降,这很好。

期待实施建议 #2 - 我相信它会减少 2D 场景渲染时间(现在相当长)。

在此屏幕截图中有 5k 个贴花

我建议使用 SpriteKit 场景作为您的 "splattered-on" 几何体的 material。 Apple 有 sample code 说明了很多你在这里想要的东西:

  1. 制作一个 SpriteKit 场景,它是 SceneKit 几何体的 material。
  2. 当 3D 世界中的某些东西需要飞溅时,使用命中测试在 material 中查找纹理坐标并将其转换为 SpriteKit 场景坐标。
  3. 在 SpriteKit 场景中的该位置放置一个精灵以创建贴花。

从你的 other question 来看,你似乎已经开始朝着这个方向前进了......如果你能详细说明问题,也许你会得到更好的帮助?


注意:该示例代码在 iOS 9 / macOS 10.11 及更高版本上存在错误,会干扰飞溅效果。您可以通过在 AAPLGameViewController.m:

中找到这一行来解决它
    ball.physicsBody.collisionBitMask = ~(0x4);

并在其后添加这一行:

    ball.physicsBody.contactTestBitMask = ~(0x4);

您可能会在该演示中注意到,一段时间后,您的帧速率开始下降 — SpriteKit 场景中精灵数量的不断增加使得 2D 渲染占用了越来越多的渲染循环。

(也许这是提示您 other question 的问题?)

尽管无法 "screenshot" 纹理,但有一些方法可以针对这种情况进行优化。调查的一些想法:

  1. 在 SpriteKit 场景中使用 SKEffectNode 作为缓冲区——将 splatters 扔到效果节点中,直到它达到一定数量的 children,然后打开它的 shouldRasterize 选项,这样它就不再 re-render 它的 children (并开始使用新的效果节点来获得更多的飞溅效果)。

  2. 很多 2D 时间都浪费在绘制不可见的 SK 精灵上,因为它们被其他精灵掩埋了。 (或者大部分被掩埋,只有几个像素可见。)因为你的,嗯,"paint" 都是相同的颜色,你可以使用一些简单的方法将你的 SpriteKit 场景分块成区域并决定什么时候一个区域已完全覆盖 — 到那时,您可以从该区域移除所有精灵,用一个 "total coverage" 精灵替换它们,并停止在该区域添加新精灵。 quadtree 可能是在这里使用的好工具。

  3. 如果您想通过着色器修改器而不是 SpriteKit-based material 进行自定义绘图,那么 SceneKit hit-test 到纹理坐标映射技巧同样有效,您可能会在那里找到更有效的 long-term 覆盖率跟踪方法(可能会借助 #3 中的一些技巧)。

  4. 如果您的飞溅不是经常发生,您可以在 CPU 上渲染它们,然后在 material 上设置一个新图像。

  5. 一个想法的远景,不知道它是否可行或性能良好:使用 Core Image as an accumulation buffer 绘制飞溅,并将其渲染成相同的 MTLTexture material 的内容设置为。 (使用 SceneKit 的 willRenderScene 回调每帧渲染一次。)