Как передать нетекстурные данные в шейдеры SCNTechnique Metal

Я могу передать пользовательский параметр типа sampler2D к Metal fragment function из SCNTechnique и у меня есть рабочий 2-й проход:

Plist:

<key>inputs</key>
<dict>
    <key>imageFromPass1</key>
    <string>COLOR</string>
    <key>myCustomImage</key>
    <string>myCustomImage_sym</string>
</dict>

...

<key>symbols</key>
<dict>
    <key>myCustomImage_sym</key>
    <dict>
        <key>type</key>
        <string>sampler2D</string>
    </dict>
</dict>

Соответствующий код Obj-C:

[technique setValue: UIImagePNGRepresentation(myCustomTexture) forKey:@"myCustomImage_sym"];

Металлические функциональные параметры:

fragment half4 myFS(out_vertex_t vert [[stage_in]],
    texture2d<float, access::sample> imageFromPass1 [[texture(0)]],
    texture2d<float, access::sample> myCustomImage [[texture(1)]],
    constant SCNSceneBuffer& scn_frame [[buffer(0)]]) { ...

Я получаю доступ и использую все эти входы в функции шейдера. Оно работает!

Все идет нормально!

Тем не менее, когда я добавляю другой пользовательский параметр типа float...

<key>blob_pos</key>
<string>blob_pos_sym</string>

...

<key>blob_pos_sym</key>
<dict>
    <key>type</key>
    <string>float</string>
</dict>

[_sceneView.technique setValue:[NSNumber numberWithFloat:0.5f] forKey:@"blob_pos_sym"];

 constant float& blob_pos [[buffer(2)]]

... переданные значения никогда не достигают функции шейдера.

я пытался

  • используя разные значения буфера (N) до 6
  • имеющий пользовательский параметр в функции вершины
  • введите vec3 и float3 вместо типа float
  • различные способы кодирования моего float в NSData
  • оборачивая мой поплавок в структуру

    [technique setValue:[NSValue valueWithSCNVector3: SCNVector3Make(0.5, 0.5, 0.5)] forKey:@"blob_pos_"];
    
    SCNVector3 xx = SCNVector3Make(0.5, 0.5, 0.5);
    [technique setValue:[NSData dataWithBytes:&xx length:sizeof(xx)] forKey:@"blob_pos_"];
    [technique setValue:[NSData dataWithBytesNoCopy:&xx length:sizeof(xx)] forKey:@"blob_pos_"];
    
    simd_float3 x = simd_make_float3(0.5, 0.5, 0.5);
    [technique setValue:[NSData dataWithBytes:&x length:sizeof(x)] forKey:@"blob_pos_"];
    
    float y = 0.5;
    [technique setValue:[NSData dataWithBytes:&y length:sizeof(y)] forKey:@"blob_pos_"];
    
    struct MyStruct {
        float x;
    };
    struct MyStruct myStruct = {
        0.5
    };
    
    [technique setValue:[NSValue valueWithBytes:&myStruct objCType:@encode(struct MyStruct)] forKey:@"blob_pos_"];
    [technique setObject:[NSValue valueWithBytes:&myStruct objCType:@encode(struct MyStruct)] forKeyedSubscript:@"blob_pos_"];
    

... и все это не удалось.

Затем я посмотрел на handleBindingOfSymbol: usingBlock:... но это только GLSL.

Я обнаружил, что это металлический аналог handleBindingOfBufferNamed:quency: usingBlock:... который недоступен в SCNTechnique.

Я гуглил SCNTechnique Metal... и понял, что во всех проектах используются только параметры sampler2D.

В конце концов я узнал, что это не ново, но ошибки разработчиков в течение многих лет.

Прежде чем я начну кодировать этот float в текстуре, дайте мне знать недостающий бит, чтобы заставить его работать так, как задумано.

1 ответ

Решение

Вы должны использовать структуру, чтобы обернуть ваши входные символы и обязательно использовать [SCNTechnique setObject:forKeyedSubscript:] передать значения вашего символа в вашу технику. В документации для setObject: forKeyedSubscript: упоминается Metal, однако в нем не объясняется, как получить значение в функции Metal, что не очень хорошо.

Используя ваш пример:

Определение техники:

"inputs": [
    "imageFromPass1": "COLOR",
    "myCustomImage": "myCustomImage_sym",
    "blob_pos": "blob_pos_sym",
],
...

"symbols": [
    "myCustomImage_sym": ["type": "sampler2D"],
    "blob_pos_sym": ["type": "float"]
 ]

Obj-C:

[_sceneView.technique setObject:[NSNumber numberWithFloat:0.5f] forKeyedSubscript:@"blob_pos_sym"];

Металл:

typedef struct {
    float blob_pos; // Must be spelled exactly the same as in inputs dictionary.
} Inputs;

fragment half4 myFS(out_vertex_t vert [[stage_in]],
    texture2d<float, access::sample> imageFromPass1 [[texture(0)]],
    texture2d<float, access::sample> myCustomImage [[texture(1)]],
    constant Inputs& myInputs [[buffer(0)]]) { 

    float blob_pos = myInputs.blob_pos;

    ...
Другие вопросы по тегам