使用波前材质文件颜色参数Kd在MetalKit子网格中设置顶点颜色的最佳方法是什么?

时间:2018-12-18 18:34:35

标签: macos metal wavefront

我正在使用MacOS MetalKit和ModelIO开发MacOS程序。最终目标是使用3D模型来分析表演空间的声学特性。最初,我只是创建空间的视觉图像(舞台,墙壁,座位等)。我创建了一个波前文件(.obj和随附的.mtl文件)。

我可以为.obj文件中的每个顶点定义顶点颜色。但是我想使用mtl文件的Kd属性设置颜色,以便将特定颜色与mtl文件中的特定命名材料相关联。

我使用ModelIO创建资产,然后提取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)

我在导入的modelio数据中看到了所有147个子网格的所有mtl属性。

如果我在obj文件中指定了顶点颜色,那么我会在147 metalKitMeshes子网格中看到所有这些颜色。但是,如果我没有在obj文件中指定所有顶点颜色,那么metalKitMeshes子网格中的所有颜色都是黑色(0,0,0)。在mtl文件中指定的颜色将被忽略。

主要问题:是否可以使用mtl文件中的材质颜色自动设置金属子网格颜色?

第二个问题:在更一般的情况下,将所有材质参数传递到着色器的最佳方法是什么?

谢谢

我在Apple Developer论坛上问了这个问题,但没有得到答复。

我可以将波前文件导入Blender,并使用材质文件中的正确颜色渲染场景中的所有对象。因此,.obj和.mtl文件的结构似乎是正确的。

1 个答案:

答案 0 :(得分:0)

这是一个有趣的问题,因为它突出了Model I / O和MetalKit之间的阻抗不匹配。特别是,它说明了MetalKit如何处理材料,也就是说:完全没有。

为了简洁起见,我将假设您关心的唯一材料属性是基色(MTL规范称为漫反射率,与{{1} }关键字),并且.mtl文件中的每种材料都具有唯一的名称,并具有Kd属性。

我们的任务是三个方面。首先,我们需要从资产中包含的Kd中提取我们关心的材料属性。其次,我们需要将这些材质与其对应的MDLSubmesh关联起来,以便我们知道在绘制时要使用哪种材质。第三,我们需要调整着色器以基于每个网格而不是基于顶点接受此材质信息。

由于我们的MTKSubmesh没有足够的上下文来了解与其对应的MTKMesh相关联的材质,因此我们应用fundamental theorem of software engineering并设计出具有一系列材质的网格类型,即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;
};

请注意,我为参数选择了任意绑定点;只要您的Swift代码和着色器中的索引一致,就可以使用现有顶点属性和缓冲区参数未使用的任何插槽。

还要注意,我在结构布局方面确实很勇敢。如果Swift决定不想布局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; } 结构,以便可以将其地址类型化为指向Material的指针,那么您将得到奇怪的行为。添加其他材料属性时,尤其要注意这一点。