Преобразование изображения в узел SceneKit

У меня есть изображение с битовой картой: мое изображение битовой карты

(Однако это должно работать с любым произвольным образом)

мой узел набора сцен

Я хочу взять свое изображение и сделать его 3D SCNNode. Я уже много сделал с этим кодом. Это занимает каждый пиксель в изображении и создает SCNNode с геометрией SCNBox.

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

Но проблема в том, что то, что я делаю, занимает много памяти! моя статистика памяти

Я пытаюсь найти способ сделать это с меньшим объемом памяти.

Весь код и ресурсы можно найти по адресу: https://github.com/KonradWright/KNodeFromSprite

Теперь вы рисуете каждый пиксель как SCNBox определенного цвета, а это означает:

  • один ниток в коробке
  • нанесение ненужных двух невидимых граней между соседними ящиками
  • рисование N таких же блоков 1x1x1 в строке, когда может быть нарисовано поле 1x1xN

Похоже, что подобная проблема с оптимизацией Minecraft:

  1. Обработать ваше изображение 3-мерным массивом (где глубина требуется глубина экструзии изображения), каждый элемент представляет куб вокселя определенного цвета.
  2. Используйте алгоритм жадного сетки ( demo ) и пользовательскую SCNGeometry для создания сетки для узла SceneKit.

Псевдокод для алгоритма сетки, который пропускает грани соседних кубов (более простые, но менее эффективные, чем жадные сетки):

 #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 для видимых лиц, а затем результат SCNPlane flattenedClone() .

Подобный подход, предложенный Ef Dot:

  1. Чтобы максимально сократить количество обратных вызовов, вы хотите, чтобы количество материалов было как можно меньше. Здесь вам понадобится один SCNMaterial каждого цвета.
  2. Чтобы максимально SCNGeometryElement вызовов, убедитесь, что два элемента геометрии ( SCNGeometryElement ) не используют один и тот же материал. Другими словами, используйте один элемент геометрии на материал (цвет).

Таким образом, вам нужно будет построить SCNGeometry которой есть N геометрических элементов и N материалов, где N – количество различных цветов вашего изображения.

  1. Для каждого цвета в вашем изображении создайте многоугольник (или группу непересекающихся полигонов) из всех пикселей этого цвета
  2. Триангулируйте каждый многоугольник (или группу полигонов) и создайте геометрический элемент с этой триангуляцией.
  3. Постройте геометрию из элементов геометрии.

Если вы не чувствуете себя комфортно с триангуляцией полигонов, вы можете использовать SCNShape .

  1. Для каждого многоугольника (или группы полигонов) создайте единый UIBezierPath и с ним создайте SCNShape .
  2. Объедините все источники геометрии ваших фигур в одном источнике и повторно используйте элементы геометрии для создания пользовательской SCNGeometry

Обратите внимание, что некоторые вершины будут дублированы, если вы используете коллекцию SCNShape s для построения геометрии. С небольшими усилиями вы можете убедиться, что две конечные точки в вашем конечном источнике не имеют одинаковой позиции. Обновите индексы в элементах геометрии соответственно.

Я также могу направить вас к этому превосходному репо GitHub Ником Локвудом:

https://github.com/nicklockwood/FPSControls

Он покажет вам, как создавать сетки в виде плоскостей (вместо кубов), что является быстрым способом добиться того, что вам нужно для простых сцен, используя «соседнюю» проверку.

Если вам нужны большие сложные сцены, я предлагаю вам пойти на решение, предложенное Ef Dot, используя алчный алгоритм сетки.

  • NSAttributedString не работает с SCNText
  • Камера SceneKit показывает дополнительное пространство, когда оно не должно
  • Можем ли мы сделать тень на прозрачной плоскости в SceneKit
  • Анимация SCNNode навсегда - SceneKit, Swift
  • Напишите текст на сфера поверхности SceneKit?
  • Как построить многоуровневую игру в SceneKit
  • Как программно обернуть текстуру png вокруг куба в SceneKit
  • Объекты SceneKit сталкиваются в своей ограничивающей рамке, а не вдоль сетки
  • Загрузочный узел SceneKit с анимацией из отдельного файла scn
  • Как установить содержимое фона сцены для карты куба
  • как конвертировать файлы .dae в .scn в SceneKit
  • Давайте будем гением компьютера.