我正在使用MacOS MetalKit和ModelIO开发一个MacOS程序。最终目标是使用3D模型来分析性能空间的声学特性。最初,我只是创建一个视觉图像的空间(舞台,墙壁,座位等)。我创建了一个wavefront文件(.obj和附带的.mtl文件)。
我可以为.obj文件中的每个顶点定义顶点颜色。但是我想使用mtl文件的Kd属性来设置颜色,以便特定的颜色与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)我在导入的modelio数据中看到了所有147个子网格的所有mtl属性。
如果我在obj文件中指定顶点颜色,那么我会在147个metalKitMeshes子网格中看到所有这些颜色。但是如果我没有指定obj文件中的所有顶点颜色,那么metalKitMeshes子网格中的所有颜色都是黑色(0,0,0)。在mtl文件中指定的颜色将被忽略。
主要问题:是否有一种方法可以使用mtl文件中的材质颜色来自动设置金属子网格的颜色?
第二个问题:在更一般的情况下,将所有材质参数传递到着色器的最佳方式是什么?
谢谢
我在苹果开发者论坛上问了这个问题,但没有得到任何回应。
我可以将我的wavefront文件导入到Blender中,场景中的所有对象都被渲染为来自材质文件的正确颜色。因此,我的.obj和.mtl文件的结构似乎是正确的。
发布于 2018-12-19 13:45:06
这是一个有趣的问题,因为它突出了模型I/O和MetalKit之间的阻抗不匹配。特别是,它说明了MetalKit是如何处理材料的,也就是说:根本不是。
为简洁起见,我将假定您唯一关心的材质属性是基础颜色( MTL规范称为漫反射,对应于Kd关键字),并且.mtl文件中的每种材质都是唯一命名的,并且具有Kd属性。
我们的任务有三个方面。首先,我们需要从资源中包含的MDLSubmeshes中提取我们关心的材料属性。其次,我们需要将这些材质与其对应的MTKSubmeshes相关联,以便我们知道在绘制时使用哪些材质。第三,我们需要调整我们的着色器,以接受每个子网格的材质信息,而不是每个顶点的材质信息。
由于我们的MTKMesh没有足够的上下文来知道与其对应的MDLMesh关联的材质,所以我们应用fundamental theorem of software engineering并设计我们的网格类型,该网格类型具有一个材质数组,MDLMesh中的每个材质对应一个材质,前提是对应子网格具有具有漫反射颜色的材质:
struct MyMesh {
var mtkMesh: MTKMesh
var materials = [Material?]()
}现在,我们的material struct只是对我们的基色进行包装:
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代码中的索引和着色器同意,您就可以使用现有顶点属性和缓冲区参数没有使用的任何槽。
还请注意,我对struct布局真的很漫不经心。如果Swift决定不想布局Material结构,这样它的地址就可以被类型双关到指向packed_float3的指针,你会得到奇怪的行为。在添加其他材质属性时,这一点尤其需要注意。
https://stackoverflow.com/questions/53839158
复制相似问题