简体   繁体   中英

Metal - How to adapt the vertexDescriptor created by Metal I/O to my MTLRenderPipelineState?

I am developing a Metal macOS app that displays my own 3D models and some other 3D model.obj I import through the Model I/O . I am getting some conflict between my own vertexDescriptor and the vertexDescriptor embedded into the model.obj.

I have found a patch to the trouble creating a brand new MTLRenderPipelineState per each model.obj imported (with its own vertexDescriptor ), but I think this is not the proper way to do. I cannot imagine that I have to create a brand new MTLRenderPipelineState per each model (read each "different vertexDescriptor ") imported. Any clue to work with a unique MTLRenderPipelineState for all the models in the app?

Here's a sample case. I create my own vertexDescriptor

Buffer 0: 
            stepFunction = MTLVertexStepFunctionPerVertex 
            stride = 64 
            Attribute 0:     // Position
                offset = 0 
                format = MTLAttributeFormatFloat3 
            Attribute 1:     // Normal
                offset = 16 
                format = MTLAttributeFormatFloat3 
            Attribute 2:     // Texture Coords
                offset = 32 
                format = MTLAttributeFormatFloat2 
            Attribute 3:     // Vertex Color
                offset = 48 
                format = MTLAttributeFormatFloat4

and I use it to configure my MTLRenderPipelineState accordingly with my vertex shader MyVertexShader's argument

struct VertexIn
{
    float4 position[[attribute(0)]];
    float4 normal[[attribute(1)]];
    float2 uvTex[[attribute(2)]];
    float4 color[[attribute(3)]];
};

Everything works well with the models I create myself using always the same VertexIn structure. But when I import a model.obj file with Model I/O and automatically get this vertexDescriptor

Buffer 0: 
        stepFunction = MTLVertexStepFunctionPerVertex 
        stride = 32 
        Attribute 0:     // Position
            offset = 0 
            format = MTLAttributeFormatFloat3 
        Attribute 1:     // Normal 
            offset = 12 
            format = MTLAttributeFormatFloat3 
        Attribute 2:     // Texture Coords
            offset = 24 
            format = MTLAttributeFormatFloat2

the stride is 32 (mine above is 64), the 4th attribute (color) is missed, so the model.obj looks jerky on the MTKView .

So, I created a brand new MTLRenderPipelineState only to draw this model.obj using the model.obj's vertexDescriptor and my own vertex shader function MyVertexShader . Well, I got this error

*Error: Vertex attribute color(3) is missing from the vertex descriptor*

So I added the 4th attribute color to the model.obj vertexDescriptor

if(objVertexDescriptor.attributes[3].format == MTLVertexFormatInvalid)
{
    // add the color attribute to the model.obj vertexDescriptor

    objVertexDescriptor.attributes[3].format = MTLVertexFormatFloat4;
    objVertexDescriptor.attributes[3].offset = sizeof(simd_float3) + sizeof(simd_float3) + sizeof(simd_float3);
    objVertexDescriptor.attributes[3].bufferIndex = 0;
}

then I created a brand new MTLRenderPipelineState with this objVertexDescriptor and my own vertex shader function MyVertexShader , and the model.obj finally looked well on the MTKView .

I feel that what I'm doing it's just a bad patch to this trouble and I think there should be a better and reusable way to solve the conflict hopefully using the same MTLRenderPipelineState for all the models. I can't accept I should create a new pipeline per each model the user will import. Any idea?

I have found the solution. When I create the MDLAsset , I pass my own mDefaultMDLVertexDescriptor I created earlier and use all over my code. This forces the asset to adopt that vertexDescriptor.

MDLAsset    *objAsset = [[MDLAsset alloc] initWithURL:objURL
                vertexDescriptor:mDefaultMDLVertexDescriptor bufferAllocator:bufferAllocator];

So even the MDLMesh and the MTKMesh created from the asset get that vertexDescriptor

MDLMesh     *objMDLMesh = (MDLMesh*)[objAsset objectAtIndex:0];
    
    self.mMTKMesh = [[MTKMesh alloc] initWithMesh:objMDLMesh device:self.mDevice error:&error];

You could verify the mesh has that vertexDescriptor with

NSLog(@"self.mMTKMesh.vertexDescriptor %@\r", [MTKMetalVertexDescriptorFromModelIO(self.mMTKMesh.vertexDescriptor) description]);

and even with

NSLog(@"self.mMTKMesh.vertexDescriptor.attributes %@", self.mMTKMesh.vertexDescriptor.attributes);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM