Это интересный вопрос, поскольку он подчеркивает несоответствие импеданса между моделями ввода / вывода и 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
, вы получите странное поведение.Это особенно важно учитывать при добавлении дополнительных свойств материала.