Как лучше всего использовать параметр цвета Kd файла материала волнового фронта для установки цвета вершин в подсетях MetalKit? - PullRequest
0 голосов
/ 18 декабря 2018

Я разрабатываю программу MacOS, используя MacOS MetalKit и ModelIO.Конечная цель - использовать трехмерную модель для анализа акустических свойств пространства исполнения.Изначально я просто создаю визуальный образ пространства (сцена, стены, сидения и т. Д.).Я создаю файл волнового фронта (.obj и сопровождающий его файл .mtl).

Я могу определить цвет вершины для каждой вершины в файле .obj.Но я хотел бы использовать свойства Kd файла mtl, чтобы установить цвет так, чтобы определенные цвета ассоциировались с конкретными именованными материалами в файле mtl.

Я использую ModelIO для создания ресурса, а затем извлекаю обамоделио сетка и металлическая сетка.Вот фрагмент кода Swift:

// Extract both the MetalKit meshes and the original ModelIO meshes
    var meshes: (modelIOMeshes: [MDLMesh], metalKitMeshes: [MTKMesh])
    meshes = try MTKMesh.newMeshes(asset: asset, device: device)

Я вижу все свойства mtl для всех 147 подмеш в импортированных данных modelio.

Если я укажу цвета вершин в файле obj, то я увижу все эти цвета в 147 подметах metalKitMeshes.Но если я не укажу все цвета вершин в файле obj, тогда все цвета в подсетях metalKitMeshes будут черными (0,0,0).Цвета, указанные в файле mtl, игнорируются.

Основной вопрос: есть ли способ использовать цвета материала в файле mtl для автоматической установки цветов подшивки металла?

Вторичный вопрос:В более общем случае, как лучше всего передать все параметры материала в шейдер?

Спасибо

Я спрашивал об этом на форуме разработчиков Apple, но не получил ответа.

Я могу импортировать файл волнового фронта в Blender, и все объекты на сцене отображаются с правильными цветами из файла материалов.Таким образом, структура моих файлов .obj и .mtl кажется правильной.

1 Ответ

0 голосов
/ 19 декабря 2018

Это интересный вопрос, поскольку он подчеркивает несоответствие импеданса между моделями ввода / вывода и MetalKit.В частности, он иллюстрирует, как MetalKit обрабатывает материалы, то есть совсем нет.

В целях краткости я собираюсь предположить, что единственное свойство материала, о котором вы заботитесь, это базовый цвет (что спецификация MTL называет диффузной отражательной способностью , что соответствует ключевому слову Kd, и что каждый материал в файле .mtl имеет уникальное имя и свойство Kd.

Нашзадача в три раза.Во-первых, нам нужно извлечь свойства материала, которые нас интересуют, из MDLSubmesh es, содержащихся в активе.Во-вторых, нам нужно связать эти материалы с соответствующими MTKSubmesh, чтобы мы знали, какой материал использовать при рисовании.В-третьих, нам нужно адаптировать наш шейдер так, чтобы он принимал эту информацию о материале на основе отдельных подсетей, а не отдельных вершин.

Поскольку у нашего MTKMesh недостаточно контекста, чтобы знать материалы, связанные с егосоответствующий MDLMesh, мы применяем фундаментальную теорему разработки программного обеспечения и разрабатываем наш тип сетки, который имеет массив материалов, по одному для каждого материала в MDLMesh, при условии, что соответствующая подшивка имеет материал сдиффузный цвет:

struct MyMesh {
    var mtkMesh: MTKMesh
    var materials = [Material?]()
}

На данный момент структура нашего материала - не что иное, как обертка вокруг нашего основного цвета:

struct Material {
    var baseColor: float3
}

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

var myMeshes = [MyMesh]()
for (mdlMesh, mtkMesh) in zip(mdlMeshes, mtkMeshes) {
    guard let mdlSubmeshes = mdlMesh.submeshes as? [MDLSubmesh] else { continue }
    var materials = [Material?]()
    for mdlSubmesh in mdlSubmeshes {
        if let mdlMaterial = mdlSubmesh.material, let mdlBaseColor = mdlMaterial.property(with: .baseColor) {
            let baseColor = mdlBaseColor.float3Value
            let material = Material(baseColor: baseColor)
            materials.append(material)
        } else {
            materials.append(nil)
        }
    }
    let myMesh = MyMesh(mtkMesh: mtkMesh, materials: materials)
    myMeshes.append(myMesh)
}

При рисовании мы перечисляем подсеми, ищем материал подрешетки и привязываем его к буферуаргумент:

for (idx, submesh) in mesh.mtkMesh.submeshes.enumerated() {

    if var material = mesh.materials[idx] {
        renderEncoder.setVertexBytes(&material, length: MemoryLayout<Material>.size, index: 3)
    }

    // ... draw ...

Наконец, в нашем шейдере мы создаем соответствующую структуру материала:

struct Material {
    packed_float3 baseColor;
};

и примите параметр этого типа материала, передав базовый цвет материала в качестве цвета вершины для дальнейшего затенения:

vertex VertexOut vertexShader(VertexIn in [[stage_in]],
                               constant InstanceConstants &instanceConstants [[buffer(2)]],
                               constant Material &material [[buffer(3)]])
{
    VertexOut out;
    out.position = instanceConstants.modelViewProjectionMatrix * float4(in.position, 1);
    out.color = float4(material.baseColor, 1);
    out.texCoord = in.texCoord;
    return out;
}

Обратите внимание, что я выбрал произвольные точки привязки для аргументов;вы можете использовать те слоты, которые не используются вашими существующими атрибутами вершин и параметрами буфера, при условии, что индексы в вашем Swift-коде и ваших шейдерах согласуются.

Также обратите внимание, что я действительно кавалер со структуройраскладка.Если Swift решит, что он не хочет размещать структуру Material так, чтобы его адрес мог быть привязан к указателю на packed_float3, вы получите странное поведение.Это особенно важно учитывать при добавлении дополнительных свойств материала.

...