将图像转换为SceneKit节点

我有一个位图图像: 我的位图图像

(但是,这应该与任何任意图像工作)

我的场景套件节点

我想把我的形象,并使其成为一个3D SCNNode。 这个代码我已经完成了很多。 这需要图像中的每个像素,并创build一个具有SCNBox几何的SCNNode。

static inline SCNNode* NodeFromSprite(const UIImage* image) { SCNNode *node = [SCNNode node]; CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage)); const UInt8* data = CFDataGetBytePtr(pixelData); for (int x = 0; x < image.size.width; x++) { for (int y = 0; y < image.size.height; y++) { int pixelInfo = ((image.size.width * y) + x) * 4; UInt8 alpha = data[pixelInfo + 3]; if (alpha > 3) { UInt8 red = data[pixelInfo]; UInt8 green = data[pixelInfo + 1]; UInt8 blue = data[pixelInfo + 2]; UIColor *color = [UIColor colorWithRed:red/255.0f green:green/255.0f blue:blue/255.0f alpha:alpha/255.0f]; SCNNode *pixel = [SCNNode node]; pixel.geometry = [SCNBox boxWithWidth:1.001 height:1.001 length:1.001 chamferRadius:0]; pixel.geometry.firstMaterial.diffuse.contents = color; pixel.position = SCNVector3Make(x - image.size.width / 2.0, y - image.size.height / 2.0, 0); [node addChildNode:pixel]; } } } CFRelease(pixelData); node = [node flattenedClone]; //The image is upside down and I have no idea why. node.rotation = SCNVector4Make(1, 0, 0, M_PI); return node; } 

但问题是,我正在做的事情占用了太多的内存! 我的记忆统计

我试图find一种方法来做到这一点与更less的内存。

所有代码和资源可以在https://github.com/KonradWright/KNodeFromSpritefind

现在您将每个像素绘制为某种颜色的SCNBox,这意味着:

  • 每箱一个GL抽奖
  • 在相邻的盒子之间画出不必要的两个不可见的面孔
  • 当可以绘制一个1x1xN的盒子时,连续绘制N个相同的1x1x1盒子

看起来像常见的Minecraft般的优化问题:

  1. 处理你的图像是三维数组(其中深度是想要的图像挤出深度),每个元素代表某个颜色的立方体素。
  2. 使用贪心网格algorithm ( 演示 )和自定义SCNGeometry为SceneKit节点创build网格。

用于跳过相邻立方体面的网格划分algorithm的伪代码(简单,但效率低于贪婪网格划分):

 #define SIZE_X = 16; // image width #define SIZE_Y = 16; // image height // pixel data, 0 = transparent pixel int data[SIZE_X][SIZE_Y]; // check if there is non-transparent neighbour at x, y BOOL has_neighbour(x, y) { if (x < 0 || x >= SIZE_X || y < 0 || y >= SIZE_Y || data[x][y] == 0) return NO; // out of dimensions or transparent else return YES; } void add_face(x, y orientation, color) { // add face at (x, y) with specified color and orientation = TOP, BOTTOM, LEFT, RIGHT, FRONT, BACK // can be (easier and slower) implemented with SCNPlane's: https://developer.apple.com/library/mac/documentation/SceneKit/Reference/SCNPlane_Class/index.html#//apple_ref/doc/uid/TP40012010-CLSCHSCNPlane-SW8 // or (harder and faster) using Custom Geometry: https://github.com/d-ronnqvist/blogpost-codesample-CustomGeometry/blob/master/CustomGeometry/CustomGeometryView.m#L84 } for (x = 0; x < SIZE_X; x++) { for (y = 0; y < SIZE_Y; y++) { int color = data[x][y]; // skip current pixel is transparent if (color == 0) continue; // check neighbour at top if (! has_neighbour(x, y + 1)) add_face(x,y, TOP, ); // check neighbour at bottom if (! has_neighbour(x, y - 1)) add_face(x,y, BOTTOM); // check neighbour at bottom if (! has_neighbour(x - 1, y)) add_face(x,y, LEFT); // check neighbour at bottom if (! has_neighbour(x, y - 1)) add_face(x,y, RIGHT); // since array is 2D, front and back faces is always visible for non-transparent pixels add_face(x,y, FRONT); add_face(x,y, BACK); } } 

很多取决于input图像。 如果它不大并且没有多种颜色,那么我将使用SCNNode为可SCNNode添加SCNPlane ,然后SCNPlane flattenedClone()结果。

类似于Ef Dot提出的方法:

  1. 为了尽可能减less抽奖的次数,你想保持尽可能小的材料数量。 在这里,您将需要一种颜色的SCNMaterial
  2. 要使绘图调用的次数尽可能小,请确保没有两个几何元素( SCNGeometryElement )使用相同的材​​质。 换句话说,每个材质(颜色)使用一个几何元素。

所以你将不得不构build一个具有N几何元素和N材质的SCNGeometry ,其中N是图像中不同颜色的数量。

  1. 对于图像中的每种颜色,从该颜色的所有像素构build一个多边形(或一组不相交的多边形)
  2. 对每个多边形(或多边形组)进行三angular剖分,并用该三angular剖分build立一个几何元素。
  3. 从几何元素构build几何。

如果您对自己进行多边形三angular测量感觉不舒服,可以使用SCNShape

  1. 对于每个多边形(或一组多边形)创build一个单一的UIBezierPath并与此build立一个SCNShape
  2. 将所有形状的几何源合并到一个源中,然后重新使用几何元素来创build自定义的SCNGeometry

请注意,如果使用SCNShape的集合来构build几何体,则某些顶点将被复制。 只需很less的努力,您可以确保最终数据源中没有两个顶点具有相同的位置。 相应地更新几何元素中的索引。

我还可以引导你到Nick Lockwood这个优秀的GitHub回购:

https://github.com/nicklockwood/FPSControls

它会告诉你如何生成网格作为飞机(而不是立方体),这是一个快速的方法来实现你所需要的简单场景使用“邻居”检查。

如果你需要大型复杂的场景,那么我build议你使用贪心网格algorithm来寻找Ef Dot提出的解决scheme。