首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在MetalKit子网格中使用wavefront材质文件颜色参数Kd设置顶点颜色的最佳方法是什么?

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

Stack Overflow用户
提问于 2018-12-19 02:34:35
回答 1查看 377关注 0票数 1

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

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

我使用ModelIO创建资源,然后提取模型网格和金属网格。以下是Swift代码的一小段:

代码语言:javascript
复制
// 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文件的结构似乎是正确的。

EN

回答 1

Stack Overflow用户

发布于 2018-12-19 13:45:06

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

为简洁起见,我将假定您唯一关心的材质属性是基础颜色( MTL规范称为漫反射,对应于Kd关键字),并且.mtl文件中的每种材质都是唯一命名的,并且具有Kd属性。

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

由于我们的MTKMesh没有足够的上下文来知道与其对应的MDLMesh关联的材质,所以我们应用fundamental theorem of software engineering并设计我们的网格类型,该网格类型具有一个材质数组,MDLMesh中的每个材质对应一个材质,前提是对应子网格具有具有漫反射颜色的材质:

代码语言:javascript
复制
struct MyMesh {
    var mtkMesh: MTKMesh
    var materials = [Material?]()
}

现在,我们的material struct只是对我们的基色进行包装:

代码语言:javascript
复制
struct Material {
    var baseColor: float3
}

当从我们的资源中加载网格时,我们需要迭代每个网格中的所有子网格,并构建一个网格集合,每个网格都有它的材质数组:

代码语言:javascript
复制
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)
}

在绘制时,我们枚举子网格,查找子网格的材料并将其绑定到缓冲区参数:

代码语言:javascript
复制
for (idx, submesh) in mesh.mtkMesh.submeshes.enumerated() {

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

    // ... draw ...

最后,在我们的着色器中,我们创建了一个相应的材质结构:

代码语言:javascript
复制
struct Material {
    packed_float3 baseColor;
};

并接受此材质类型的参数,将材质的基础颜色作为顶点颜色传递以进行进一步着色:

代码语言:javascript
复制
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的指针,你会得到奇怪的行为。在添加其他材质属性时,这一点尤其需要注意。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53839158

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档