математическая подвижная сетка в Swift с помощью SceneKit

Я математик, который хочет запрограммировать геометрическую игру.

У меня есть точные координаты и математические формулы нескольких мешей, которые мне нужно отобразить, и их единичные нормали.

Мне нужна только одна текстура (цветной отражающий металл) на сетку.

Мне нужно, чтобы пользователь перемещал части, то есть изменял координаты сетки, опять же, с помощью простой математической формулы.

Так что мне не нужно импортировать 3D-файлы, я могу все вычислить.

Представьте себе некий кубик Рубика. Координаты куба вычисляются, а кублеты вращаются пользователем. У меня есть программа, работающая в Mathematica.

У меня очень тяжелое время, уже бессонные дни, я пытаюсь найти, как именно отобразить вычисленный меш в SceneKit - с каждой вершиной и нормалью, анимированными отдельно.

ЛЮБОЙ рабочий пример, скажем, одного треугольника с вычисленными координатами (а не формой, предоставленной запасом), отображаемый с анимируемыми координатами с помощью SceneKit, будет ОЧЕНЬ оценен.

Я посмотрел дальше, и кажется, что отдельные точки меша не могут быть подвижными в SceneKit. Мне нравится в SceneKit (в отличие от OpenGL) возможность получить объекты под пальцем пользователя. Можно ли смешивать OpenGL и SceneKit в одном проекте?

Я мог бы взять на себя оттуда ....


person adrian O    schedule 26.06.2015    source источник
comment
Посмотрите здесь (там метод геометрииWithSources: Я считаю именно то, что вы искали)   -  person Kametrixom    schedule 27.06.2015
comment
Спасибо, Каметриксом. Правда ли, что положение и нормаль отдельных вершин меша нельзя анимировать в SceneKit? Если да, то можно ли смешивать SceneKit с OpenGL для анимации отдельных вершин?   -  person adrian O    schedule 27.06.2015
comment
Я не знаю об этом, но если вам действительно нужно больше контроля над тем, что именно происходит, я предлагаю использовать Metal. Я изучаю его прямо сейчас, и это действительно не так сложно (я думаю, что OpenGL намного сложнее и страннее). Я не смог найти, можно ли смешивать Metal и SceneKit, но я думаю, вы могли бы попробовать.   -  person Kametrixom    schedule 27.06.2015
comment
Каметриксом я нашел в SceneKit ссылку, что его можно назвать Metal. Мне не ясно, нужно ли инициализировать Metal или SceneKit сделает это за вас.   -  person adrian O    schedule 27.06.2015
comment
Kametrixom См. также ссылку Мне непонятно, можно ли нужно инициализировать Metal, или SceneKit сделает это за вас. Где ты изучаешь метал? Просто отсылка к Apple?   -  person adrian O    schedule 27.06.2015


Ответы (1)


Индивидуальная анимация положения вершин, как правило, непростая задача. Но есть хорошие способы приблизиться к этому в SceneKit.

Графический процессор действительно хочет, чтобы все данные вершин были загружены в один блок, прежде чем он начнет рендеринг кадра. Это означает, что если вы постоянно вычисляете новые позиции вершин/нормали/и т. д. на ЦП, у вас возникает проблема переноса всех этих данных на ГП каждый раз, когда меняется хотя бы часть из них.

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

Для этого можно рассмотреть несколько вариантов:

  1. Модификаторы шейдеров. Начните с фиктивной геометрии, имеющей нужную вам топологию (количество вершин и способ их соединения в виде полигонов). Передайте свою входную переменную как дополнительную текстуру и в коде модификатора шейдера (для точки входа геометрии) найдите текстуру, выполните свою функцию и установите положение вершины с результатом.

  2. Металлические вычислительные шейдеры. Создайте источник геометрии с буфером Metal, затем во время рендеринга поставьте в очередь вычислительный шейдер, который записывает данные вершин в этот буфер в соответствии с вашей функцией. (Скелетный код части этого по ссылке.)


Обновление. Судя по вашим комментариям, вам может быть легче.

Если у вас есть геометрия, состоящая из частей, которые являются статичными по отношению друг к другу и перемещаются относительно друг друга — как кубики кубика Рубика — вычисление вершин во время рендеринга является излишним. Вместо этого вы можете один раз загрузить статические части вашей геометрии в GPU и использовать преобразования для их позиционирования относительно друг друга.

Способ сделать это в SceneKit состоит в том, чтобы создать отдельные узлы, каждый со своей собственной (статической) геометрией для каждой части, а затем установить преобразования узлов (или положения/ориентации/масштабы) для перемещения узлов относительно друг друга. Чтобы переместить сразу несколько узлов, используйте иерархию узлов — сделайте несколько из них потомками другого узла. Если некоторые из них должны перемещаться вместе в один момент, а другое подмножество — позже, вы можете изменить иерархию.

Вот конкретный пример идеи кубика Рубика. Сначала создадим кублеты:

// convenience for creating solid color materials
func materialWithColor(color: NSColor) -> SCNMaterial {
    let mat = SCNMaterial()
    mat.diffuse.contents = color
    mat.specular.contents = NSColor.whiteColor()
    return mat
}

// create and arrange a 3x3x3 array of cubelets
var cubelets: [SCNNode] = []
for x in -1...1 {
    for y in -1...1 {
        for z in -1...1 {
            let box = SCNBox()
            box.chamferRadius = 0.1
            box.materials = [
                materialWithColor(NSColor.greenColor()),
                materialWithColor(NSColor.redColor()),
                materialWithColor(NSColor.blueColor()),
                materialWithColor(NSColor.orangeColor()),
                materialWithColor(NSColor.whiteColor()),
                materialWithColor(NSColor.yellowColor()),
            ]
            let node = SCNNode(geometry: box)
            node.position = SCNVector3(x: CGFloat(x), y: CGFloat(y), z: CGFloat(z))
            scene.rootNode.addChildNode(node)
            cubelets += [node]
        }
    }
}

Далее процесс выполнения поворота. Это одно конкретное вращение, но вы можете обобщить его до функции, которая выполняет любое преобразование любого подмножества кубиков:

// create a temporary node for the rotation
let rotateNode = SCNNode()
scene.rootNode.addChildNode(rotateNode)

// grab the set of cubelets whose position is along the right face of the puzzle,
// and add them to the rotation node
let rightCubelets = cubelets.filter { node in 
    return abs(node.position.x - 1) < 0.001 
}
rightCubelets.map { rotateNode.addChildNode($0) }

// animate a rotation
SCNTransaction.begin()
SCNTransaction.setAnimationDuration(2)
rotateNode.eulerAngles.x += CGFloat(M_PI_2)
SCNTransaction.setCompletionBlock {
    // after animating, remove the cubelets from the rotation node,
    // and re-add them to the parent node with their transforms altered
    rotateNode.enumerateChildNodesUsingBlock { cubelet, _ in
        cubelet.transform = cubelet.worldTransform
        cubelet.removeFromParentNode()
        scene.rootNode.addChildNode(cubelet)
    }
    rotateNode.removeFromParentNode()
}
SCNTransaction.commit()

Волшебная часть находится в очистке после анимации. Кублеты начинаются как дочерние элементы корневого узла сцены, и мы временно переназначаем их родителем другому узлу, чтобы мы могли преобразовать их вместе. После того, как они снова станут дочерними узлами корневого узла, мы устанавливаем каждому локальному transform его worldTransform, чтобы он сохранял эффект изменений преобразования временного узла.

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

Я не совсем уверен, насколько ваша проблема похожа на кубик Рубика, но похоже, что вы, вероятно, можете обобщить решение из чего-то подобного.

person rickster    schedule 28.06.2015
comment
Ваше решение 2 великолепно! - person adrian O; 28.06.2015
comment
Спасибо большое. Более подробная информация была бы ОЧЕНЬ оценена. На самом деле анимация вершин состоит из умножения положения каждого 4 вектора на матрицу вращения в 2d. Все вместе нужны только манипуляции с 4-мя матрицами с помощью простых операций (добавление константы в определенную строку). Моя проблема в том, что, хотя я много занимался математическим программированием, в ООП мой опыт - это только онлайн-курс в Стэнфорде и яростное обучение сейчас... У меня есть довольно удивительные математические формы, а математика ПУТЬ слишком медленная. Я смотрел на металкит сейчас. - person adrian O; 28.06.2015
comment
Если я быстро вычислю координаты, как мне собрать их в сетку? Что вы предлагаете, так это то, что, скажем, для кубика Рубика вы можете оставить кубики фиксированными, но во время рендеринга вы можете выполнять вращение и фактическую окраску? Это было бы как раз то, что мне нужно... - person adrian O; 28.06.2015
comment
rickster Извините, ваша ссылка на пример мертва. Вы помните что-нибудь еще об этом, чтобы я мог найти это? - person adrian O; 28.06.2015