2015-03-03 68 views
2

我有一个位图图像: my bit-map image将图像转换到SceneKit节点

(然而,这应与任意图像工作)

my scene kit node

我希望把我的形象,使其成为3D SCNNode。这段代码我已经完成了很多。这需要图像中的每个像素,并使用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; 
} 

但问题是,我正在做的事情占用了很多内存! my memory statistics

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

所有代码和资源,可以发现: https://github.com/KonradWright/KNodeFromSprite

回答

4

现在你绘制每个像素为某种颜色的SCNBox,这意味着:不必要的每盒

  • 一个GL平局
  • 绘图相邻盒子之间的两个看不见的面孔
  • 当一盒1x1xN可以绘制时,连续绘制N个相同的1x1x1盒子

好像普通的Minecraft状优化问题:

  1. 对待你的图像是3维数组(其中深度是想要的图像挤出深度),表示特定颜色的立方体体素的每个元素。
  2. 使用greedy meshing algorithmdemo)和custom SCNGeometry为SceneKit节点创建网格。

伪代码用于啮合算法跳过adjancent立方体的面(simplier,但比贪婪啮合不太有效):

#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); 

    } 
} 

很多依赖于输入图像。如果它不大并且没有多种颜色,我会用SCNNodeSCNPlane的可见面,然后flattenedClone()的结果。

+0

在Objective-C风格的伪代码中,如何将两个并排立方体网格化? 我被给了一个javascript的例子,但我不知道如何在obj-c中做到这一点。 – 2015-03-03 16:00:23

+0

@KonradWright用伪代码更新答案。 – 2015-03-05 09:44:55

+0

谢谢Ef Dot :) – 2015-03-05 10:03:32

3

一个类似于Ef中提出点的一种方法:尽可能你想保留的材料的数量尽可能小

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

所以,你必须建立一个SCNGeometryN几何元素和N材料,其中N是不同颜色的图像数量。

  1. 对于你图像的每种颜色建立从该颜色
  2. 三角测量每个多边形(或组多边形)的所有像素的多边形(或不相交的多边形组),并建立与该三角测量几何元素。
  3. 从几何元素构建几何。

如果您觉得自己对三角形进行三角测量不太舒服,您可以利用SCNShape

  1. 对于(多边形的或组)的每个多边形创建单个UIBezierPath和构建与一个SCNShape
  2. 合并的形状的所有几何体来源的单一来源,并重新使用几何元素创建一个自定义SCNGeometry

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

+2

在这种情况下,您可以轻松地为所有自定义几何网格物体创建一个几何元素(从而将绘制调用减少为一个) - 只需为SCNGeometry填充几何源'SCNGeometrySourceSemanticColor'。当然,这种方式不能使用SCNMaterial。 – 2015-03-05 09:54:21

1

我还可以引导您到这个优秀的GitHub库由尼克·洛克伍德:

https://github.com/nicklockwood/FPSControls

它会告诉你如何生成网格为平面(而不是立方体),这是一种快速的方法来实现使用“邻近”检查来简单场景需要什么。

如果您需要大型复杂场景,那么我建议您使用Ef Dot使用贪婪网格算法提出的解决方案。