将图像转换为 SceneKit 节点 [英] Convert an image to a SceneKit Node

查看:24
本文介绍了将图像转换为 SceneKit 节点的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个位图图像:

(但是这应该适用于任何任意图像)

( However this should work with any arbitrary image )

我想拍摄我的图像并使其成为 3D SCNNode.我已经用这段代码完成了这么多.这需要图像中的每个像素并创建一个具有 SCNBox 几何形状的 SCNNode.

I want to take my image and make it a 3D SCNNode. I've accomplished that much with this code. That takes each pixel in the image and creates a SCNNode with a SCNBox geometry.

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;
}

但问题是我正在做的事情占用了太多内存!

But the problem is that what I'm doing takes up way to much memory!

我正在尝试找到一种方法来用更少的内存来做到这一点.

I'm trying to find a way to do this with less memory.

可以在以下位置找到所有代码和资源:https://github.com/KonradWright/KNodeFromSprite

All Code and resources can be found at: https://github.com/KonradWright/KNodeFromSprite

推荐答案

现在你把每个像素画成某种颜色的 SCNBox,这意味着:

Now you drawing each pixel as SCNBox of certain color, that means:

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

似乎是常见的类似 Minecraft 的优化问题:

Seems like common Minecraft-like optimization problem:

  1. 处理你的图像是一个 3 维数组(其中深度是想要的图像挤压深度),每个元素代表某种颜色的立方体素.
  2. 使用贪婪网格划分算法(demo) 和 自定义 SCNGeometry 为 SceneKit 节点创建网格.
  1. Treat your image is 3-dimensional array (where depth is wanted image extrusion depth), each element representing cube voxel of certain color.
  2. Use greedy meshing algorithm (demo) and custom SCNGeometry to create mesh for SceneKit node.

跳过相邻立方体的面的网格划分算法的伪代码(更简单,但不如贪婪网格划分有效):

Pseudo-code for meshing algorithm that skips faces of adjancent cubes (simplier, but less effective than greedy meshing):

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

    }
}

很大程度上取决于输入图像.如果它不大而且没有各种各样的颜色,我会用 SCNNode 添加 SCNPlane 的可见面,然后 flattenedClone()结果.

A lot of depends on input image. If it is not big and without wide variety of colors, it I would go with SCNNode adding SCNPlane's for visible faces and then flattenedClone()ing result.

这篇关于将图像转换为 SceneKit 节点的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆