Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SceneKit Cocoa Adding decals

Is there a sufficient way to add a decals (texture) to a SCNNode ? For now all I can do is to Create new SCNNode with SCNPlane geometry assign a texture to it's diffuse contents and add it to the scene... This approach is highly inefficient.

I want to add a decals that won't be a part of a scene - to increase performance of the game. because I want to have relatively large amount of decals on the floor node.

Maybe somewhat similar to how particles internaly works? Or some other fast image drawing approach.

I have already tried @rickster's Suggestion number 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).

In the very same SceneKit showreel project - this alone already gave a good results, FPS are not dropping for like 5 minutes of continues playing the scene which is good.

Looking forward to implement the Suggestion #2 - I believe it will reduce the 2D scene rendering time (which is quite a big right now).

In this screen shot there are 5k decals

5k

like image 844
ColdSteel Avatar asked Oct 30 '22 16:10

ColdSteel


1 Answers

I'd recommend using a SpriteKit scene as the material for your "splattered-on" geometry. Apple has sample code that illustrates a lot of what you're going for here:

  1. Make a SpriteKit scene that's the material for the SceneKit geometry.
  2. When something in the 3D world needs to make a splatter, use hit testing to look up texture coordinates in the material and convert those to SpriteKit scene coordinates.
  3. Place a sprite in the SpriteKit scene at that location to create a decal.

Judging by your other question you seem to have already started heading in this direction... perhaps you'll get better help if you spell out more of the problem?


Note: that sample code has a bug on iOS 9 / macOS 10.11 and later, which interferes with the splatter effect. You can work around it by finding this line in AAPLGameViewController.m:

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

and adding this line after it:

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

You'll probably notice in that demo that after awhile, your frame rate starts to drop off — the ever growing number of sprites in the SpriteKit scene make 2D rendering eat up more and more of the render loop.

enter image description here (Perhaps this is the issue that prompted your other question?)

There are some ways to optimize for this scenario, though, even without the ability to "screenshot" a texture. Some thoughts to investigate:

  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).

  2. A lot of that 2D time is wasted painting SK sprites that aren't visible because they're buried under other sprites. (Or that are mostly buried, and have only a few pixels visible.) Since your, um, "paint" is all the same color you can use some simple methods to chunk up your SpriteKit scene into areas and decide when an area has been completely covered — at that point, you can remove all the sprites from that area, replace them with one "total coverage" sprite, and stop adding new sprites in that area. A quadtree might be a good tool to use here.

  3. The SceneKit hit-test to texture coordinate mapping trick works just as well if you want to do custom drawing through shader modifiers instead of with a SpriteKit-based material, and you might be able to find a more efficient way of long-term coverage tracking there (possibly aided by some of the tricks in #3).

  4. If your splatters don't occur too often, you could render them on the CPU and just set a new image on the material.

  5. A long shot of an idea, no idea if it'll work or be performant: use Core Image as an accumulation buffer to paint splatters, and render it into the same MTLTexture that the material's content is set to. (Use SceneKit's willRenderScene callback to render once per frame.)

like image 89
rickster Avatar answered Nov 18 '22 09:11

rickster