Изменить затенение фрагмента металла на основе координаты Y положения вершинного мира - PullRequest
1 голос
/ 12 апреля 2020

Я пытаюсь использовать металлический фрагментный шейдер с SCNTechnique, чтобы изменить цвет фрагмента в зависимости от положения в мире вершины Y.

Насколько я понимаю,

SCNTechnique можно настроить с помощью последовательности рендеринга проходит. Проход рендеринга позволяет внедрить вершину и фрагментный шейдер. Эти шейдеры написаны на металле. В спецификации Metal Shading Language указано, какие входы / выходы поддерживаются для этих двух. Вершинный шейдер вызывается для каждой отображаемой вершины. Мы можем передать дополнительную информацию из вершинного шейдера в фрагментный шейдер (например, положение в трехмерном пространстве, см. MSLS раздел 5.2 ). Фрагментный шейдер находится ближе всего к пикселю и может называться кратным временем для одного «пикселя», если есть несколько треугольников, которые «соответствуют» этому пикселю. (Обычно) после затенения фрагмента фрагмент может быть отброшен, если он не проходит проверку глубины или трафарета.

То, что я пытался

Это то, что я пытался. (Надеюсь, это проясняет, где не хватает моего понимания).

struct VertexOut {
    float4 position [[position]];
};

vertex VertexOut innerVertexShader(VertexIn in [[stage_in]]) {
    VertexOut out;
    out.position = in.position;
    return out;
};

fragment half4 innerFragmentShader(VertexOut in [[stage_in]],
                                   half4 color [[color(0)]]) {
    half4 output;
    output = color;           // test to see if getting rendered color works
    output.g = in.position.y; // test to see if getting y works
    return output;
}

На эти шейдеры есть ссылки в словаре SCNTechnique.

[
    "passes": [
        "innerPass: [
            "draw": "DRAW_NODE",
            "node": "inner",
            "metalVertexShader": "innerVertexShader",
            "metalFragmentShader": "innerFragmentShader"
        ]
    ],
    "sequence": ["innerPass"],
    "symbols": [:],
    "targets": [:],
]

// ...

let technique = SCNTechnique(dictionary: techniqueDictionary)

Это позволяет сделать следующее: метод создан правильно и прикреплен к сцене (потому что это влияет на рендеринг). Но, похоже, что к вершинам не применяется преобразование камеры или преобразование положения узла. И вместо этого каждый узел отображается как просматриваемый из (0,0,1) в позиции (0,0,0). Цвета неправильные. Если я удаляю шейдеры из SCNTechnique, каждый рендеринг выполняется так, как я ожидал.

Как использовать обычное поведение SceneKit (преобразование камеры и т. Д. c.) И изменять только вывод цвета на основе фрагментов ' у мировой позиции? Я ожидаю, что это должно произойти на уровне фрагмента, используя положение мира, каким-либо образом полученное в вершинном шейдере. Я искал такие вещи, как «Вершинный шейдер Metal Basi c» и ничего не нашел. Я видел шейдеры , подобные этим , но я убежден, что смогу положиться на рендеринг SceneKit для таких вещей, как освещение, материалы PBR, преобразования камеры и т. Д. c. В этот момент я чувствую, что всякий раз, когда я ищу какой-нибудь Metal topi c, я оказываюсь на тех же сайтах, которые еще не достигли моего понимания на следующем уровне. Итак, любые новые / дополнительные ресурсы также приветствуются.

Фон

Последние два месяца я работаю над своим собственным игровым проектом, в котором SceneKit используется в качестве основной графической среды. Я обратился к SCNTechnique и Metal шейдерам для пользовательских эффектов. Последние два, в частности, вызвали у меня solid головных болей, оба из-за отсутствия примера кода / документации / обратной связи во время выполнения. Из-за этого я подумывал о переходе на Unity / Unreal или даже об отмене этого проекта. Но поскольку я упрям, а также потому, что я действительно не хочу переносить свой код Swift на C # / C ++, я еще не отказался от SceneKit.

1 Ответ

0 голосов
/ 15 апреля 2020

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

Как отметил @mnuages ​​в комментарии, для в этом случае модификаторы шейдеров используются для go. Они используют затенение SceneKit по умолчанию (в соответствии с запросом OP) и допускают внедрение кода шейдера.

Дополнительная информация

Чтобы компенсировать некоторые ограничения документации SceneKit, я немного поясню для другие люди, изучающие тему.

Для получения дополнительной информации о том, как модификаторы шейдеров t ie в вершинные / фрагментные шейдеры SceneKit по умолчанию, см. мой ответ на связанный вопрос или Шейдеры SceneKit по умолчанию . Вторая ссылка демонстрирует степень логики рендеринга SceneKit c, которую вы получаете бесплатно, используя модификаторы шейдеров вместо того, чтобы писать свой собственный шейдер.

Эта страница помогла мне построить понимание различные этапы преобразования векторов из вершины в фрагмент (пространство модели ➡️ мировое пространство space пространство камеры space пространство проекции).

Альтернативный подход (пользовательский шейдер)

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

// Shaders.metal file in your Xcode project

#include <metal_stdlib>
using namespace metal;
#include <SceneKit/scn_metal>

typedef struct {
    float4x4 modelTransform;
    float4x4 modelViewTransform;
} commonprofile_node;

struct VertexIn {
    float3 position [[attribute(SCNVertexSemanticPosition)]];
};

struct VertexOut {
    float4 fragmentPosition [[position]];
    float height;
};

vertex VertexOut myVertex(
                          VertexIn in [[stage_in]],
                          constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                          constant commonprofile_node& scn_node [[buffer(1)]]
                          ) {
    VertexOut out;

    float4 position = float4(in.position, 1.f);
    out.fragmentPosition = scn_frame.viewProjectionTransform * scn_node.modelTransform * position;

    // store world position for fragment shading
    out.height = (scn_node.modelTransform * position).y;
    return out;
}

fragment half4 myFragment(VertexOut in [[stage_in]]) {
    return half4(in.height);
}
let dictionary: [String: Any] = [
    "passes" : [
        "y" : [
            "draw" : "DRAW_SCENE",
            "inputs" : [:],
            "outputs" : [
                "color" : "COLOR"
            ],
            "metalVertexShader": "myVertex",
            "metalFragmentShader": "myFragment",
        ]
    ],
    "sequence" : ["y"],
    "symbols" : [:]
]

let technique = SCNTechnique(dictionary: dictionary)
scnView.technique = technique

Вы можете объединить этот проход рендеринга с другими проходами (см. SCNTechnique ).

Fighter jet with sphere at low y position Fighter jet with sphere at high y position

...