组合两个或多个 SCNGeometries
Combining two or more SCNGeometries
我制作了这个方法来抓取 UIImage 并将其转换为 3D 模型。然而,这意味着我添加了很多节点。我在想也许可以通过将所有几何图形添加到单个节点中来优化它。有办法实现吗?
这是代码
static inline SCNNode* S2NNode(const UIImage* sprite) {
SCNNode *spriteNode = [SCNNode node];
CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(sprite.CGImage));
const UInt8* data = CFDataGetBytePtr(pixelData);
for (int x = 0; x < sprite.size.width; x++)
{
for (int y = 0; y < sprite.size.height; y++)
{
int pixelInfo = ((sprite.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 - sprite.size.width / 2.0,
y - sprite.size.height / 2.0,
0);
[spriteNode addChildNode:pixel];
}
}
}
CFRelease(pixelData);
//The image is upside down and I have no idea why.
spriteNode.rotation = SCNVector4Make(1, 0, 0, M_PI);
return spriteNode;
}
这个答案解决了最初提出的问题——如何合并几何图形。但是,合并几何体似乎并不能解决您的实际问题——如何获得具有一定厚度和良好性能的像素化 "sprite"。请参阅 。
SCNNode
方法 flattenedClone
会将一个节点的子节点的所有几何图形组合成一个具有单个几何图形的节点。这将减少场景处理开销并在单个 OpenGL 绘制调用中渲染...但它也会使立方体不能独立移动并且都共享相同的 material,因此它们不会有不同的颜色。此外,根据您希望立方体的排列方式,您可能会将比需要更多的顶点数据推送到 GPU,这仍然会损害您的性能。
根据您想要的效果类型,可能有更好的解决方案。
发布一个单独的答案,因为您的评论表明您实际寻找的内容并不完全是您要问的内容 — 我将留下另一个答案,因为它回答了最初提出的问题,即使它不能解决您的问题。
所以,听起来您真正需要的不是立方体的集合,每个立方体都可以独立移动并且每个都有自己的颜色,而是将 2D 形状挤压成 3D — 具体而言,由非-图像的透明像素:
在那种情况下,制作一堆立方体确实会损害您的性能 - 以及您的渲染结果。查看此详细信息(来自使用大量立方体方法的版本):
那些小白点来自两个立方体相交处的舍入误差。这里的问题——无论你是否使用 flattenedClone
——是被推送到 GPU 的几何数据描述了一大堆该死的多边形,这些多边形要么是不可见的(因为它们位于两个立方体之间),要么是无关紧要的(因为两个相邻立方体的共享面也可以是一个多边形)。这是我对多立方体方法测试的性能指标:
那是 408 次绘制,4.9K 个多边形,14.7K 个顶点。在 iPad Air 上旋转该模型每秒可获得 14 帧 — 糟糕。
您可以使用 SCNShape
class 从贝塞尔路径制作挤压形状。诀窍是根据非透明像素的形状制作最简单的路径。 (你想要的是构成形状轮廓的路径,而不是每个像素的一堆方形路径的并集——否则你又回到了立方体问题。)
这是计算几何学的一个重要部分,但是 there are solutions out there. And there are external tools that can do the tracing for you offline (most of them designed for making sprite-based physics bodies for SpriteKit back before that became a SpriteKit feature in iOS 8 / OS X 10.10): here's one。
一旦你有了路径,你就可以从中制作一个 SCNShape
,将你的图像纹理映射到形状的正面(如果你愿意,也可以在背面),然后使用侧面的纯色:
UIImage *image = [UIImage imageNamed:@"sprite"];
UIBezierPath *path = PathFromImage(image); // Make or load your path here
SCNNode *node = [SCNNode nodeWithGeometry:[SCNShape shapeWithPath:path extrusionDepth:1]];
SCNMaterial *face = [SCNMaterial material];
face.diffuse.contents = image;
face.diffuse.magnificationFilter = SCNFilterModeNone;
SCNMaterial *side = [SCNMaterial material];
side.diffuse.contents = [UIColor blackColor];
side.specular.contents = [UIColor whiteColor];
node.geometry.materials = @[ face, side, side ];
这里是使用 SCNShape
的仪表:
我们将数字减少了两个数量级,相应地帧率也好得多 — 当模型旋转时,我无法将其降至 60 fps 以下。
我制作了这个方法来抓取 UIImage 并将其转换为 3D 模型。然而,这意味着我添加了很多节点。我在想也许可以通过将所有几何图形添加到单个节点中来优化它。有办法实现吗?
这是代码
static inline SCNNode* S2NNode(const UIImage* sprite) {
SCNNode *spriteNode = [SCNNode node];
CFDataRef pixelData = CGDataProviderCopyData(CGImageGetDataProvider(sprite.CGImage));
const UInt8* data = CFDataGetBytePtr(pixelData);
for (int x = 0; x < sprite.size.width; x++)
{
for (int y = 0; y < sprite.size.height; y++)
{
int pixelInfo = ((sprite.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 - sprite.size.width / 2.0,
y - sprite.size.height / 2.0,
0);
[spriteNode addChildNode:pixel];
}
}
}
CFRelease(pixelData);
//The image is upside down and I have no idea why.
spriteNode.rotation = SCNVector4Make(1, 0, 0, M_PI);
return spriteNode;
}
这个答案解决了最初提出的问题——如何合并几何图形。但是,合并几何体似乎并不能解决您的实际问题——如何获得具有一定厚度和良好性能的像素化 "sprite"。请参阅
SCNNode
方法 flattenedClone
会将一个节点的子节点的所有几何图形组合成一个具有单个几何图形的节点。这将减少场景处理开销并在单个 OpenGL 绘制调用中渲染...但它也会使立方体不能独立移动并且都共享相同的 material,因此它们不会有不同的颜色。此外,根据您希望立方体的排列方式,您可能会将比需要更多的顶点数据推送到 GPU,这仍然会损害您的性能。
根据您想要的效果类型,可能有更好的解决方案。
发布一个单独的答案,因为您的评论表明您实际寻找的内容并不完全是您要问的内容 — 我将留下另一个答案,因为它回答了最初提出的问题,即使它不能解决您的问题。
所以,听起来您真正需要的不是立方体的集合,每个立方体都可以独立移动并且每个都有自己的颜色,而是将 2D 形状挤压成 3D — 具体而言,由非-图像的透明像素:
在那种情况下,制作一堆立方体确实会损害您的性能 - 以及您的渲染结果。查看此详细信息(来自使用大量立方体方法的版本):
那些小白点来自两个立方体相交处的舍入误差。这里的问题——无论你是否使用 flattenedClone
——是被推送到 GPU 的几何数据描述了一大堆该死的多边形,这些多边形要么是不可见的(因为它们位于两个立方体之间),要么是无关紧要的(因为两个相邻立方体的共享面也可以是一个多边形)。这是我对多立方体方法测试的性能指标:
那是 408 次绘制,4.9K 个多边形,14.7K 个顶点。在 iPad Air 上旋转该模型每秒可获得 14 帧 — 糟糕。
您可以使用 SCNShape
class 从贝塞尔路径制作挤压形状。诀窍是根据非透明像素的形状制作最简单的路径。 (你想要的是构成形状轮廓的路径,而不是每个像素的一堆方形路径的并集——否则你又回到了立方体问题。)
这是计算几何学的一个重要部分,但是 there are solutions out there. And there are external tools that can do the tracing for you offline (most of them designed for making sprite-based physics bodies for SpriteKit back before that became a SpriteKit feature in iOS 8 / OS X 10.10): here's one。
一旦你有了路径,你就可以从中制作一个 SCNShape
,将你的图像纹理映射到形状的正面(如果你愿意,也可以在背面),然后使用侧面的纯色:
UIImage *image = [UIImage imageNamed:@"sprite"];
UIBezierPath *path = PathFromImage(image); // Make or load your path here
SCNNode *node = [SCNNode nodeWithGeometry:[SCNShape shapeWithPath:path extrusionDepth:1]];
SCNMaterial *face = [SCNMaterial material];
face.diffuse.contents = image;
face.diffuse.magnificationFilter = SCNFilterModeNone;
SCNMaterial *side = [SCNMaterial material];
side.diffuse.contents = [UIColor blackColor];
side.specular.contents = [UIColor whiteColor];
node.geometry.materials = @[ face, side, side ];
这里是使用 SCNShape
的仪表:
我们将数字减少了两个数量级,相应地帧率也好得多 — 当模型旋转时,我无法将其降至 60 fps 以下。