Why are MDLMesh initializers making degenerate triangles?

88 views Asked by At

This sphere, generated with an MDLMesh initializer, have degenerate triangles at the top and bottom of its geometry (see images below). Why is this happening?

image1

image2

image3

Generating a cylinder with an MDLMesh initializer produces degenerate triangles too. This issue prevents me to shade theses models correctly when using the addNormals() method (when the crease threshold is inferior to 1).

Here is a link to the project so you can reproduce this issue: https://drive.google.com/file/d/17m1eJVp4RF4Aprsecz3IppeEKEV-aF0j/view?usp=share_link

Renderer:

import MetalKit

class Renderer: NSObject {
    let mdlMesh: MDLMesh
    let commandQueue: MTLCommandQueue
    let renderPipelineState: MTLRenderPipelineState
    let depthStencilState: MTLDepthStencilState
    
    init(mtkView: MTKView) {
        guard let device = MTLCreateSystemDefaultDevice() else { fatalError("Failed to create system default device.") }
        mtkView.device = device
        
        mtkView.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
        mtkView.colorPixelFormat = .bgra8Unorm
        
        let allocator = MTKMeshBufferAllocator(device: device)
        let mdlMesh = MDLMesh(sphereWithExtent: [1,1,1], segments: [10,10], inwardNormals: false, geometryType: .triangles, allocator: allocator)
        self.mdlMesh = mdlMesh
        
        guard let commandQueue = device.makeCommandQueue() else { fatalError("Failed to create command queue.") }
        self.commandQueue = commandQueue
        
        guard let library = device.makeDefaultLibrary() else { fatalError("Failed to make default library.") }
        let vertexFunction = library.makeFunction(name: "vertex_function")
        let fragmentFunction = library.makeFunction(name: "fragment_function")
        
        let renderPipelineDescriptor = MTLRenderPipelineDescriptor()
        renderPipelineDescriptor.vertexFunction = vertexFunction
        renderPipelineDescriptor.fragmentFunction = fragmentFunction
        
        renderPipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
        
        renderPipelineDescriptor.vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(mdlMesh.vertexDescriptor)
        guard let renderPipelineState = try? device.makeRenderPipelineState(descriptor: renderPipelineDescriptor) else { fatalError("Failed to make render pipeline state.") }
        self.renderPipelineState = renderPipelineState
        
        let depthStencilDescriptor = MTLDepthStencilDescriptor()
        depthStencilDescriptor.isDepthWriteEnabled = true
        depthStencilDescriptor.depthCompareFunction = .less
        
        guard let depthStencilState = device.makeDepthStencilState(descriptor: depthStencilDescriptor) else { fatalError("Failed to make depth stencil state.") }
        self.depthStencilState = depthStencilState
    }
}

extension Renderer: MTKViewDelegate {
    
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) { }
    
    func draw(in view: MTKView) {
        guard let commandBuffer = commandQueue.makeCommandBuffer() else { fatalError("Failed to make command buffer.") }
        guard let renderPassDescriptor = view.currentRenderPassDescriptor else { fatalError("Failed to get current render pass descriptor.") }
        guard let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { fatalError("Failed to make render command encoder.") }
        renderCommandEncoder.setRenderPipelineState(renderPipelineState)
        
        let mesh = try! MTKMesh(mesh: mdlMesh, device: view.device!)
        for submesh in mesh.submeshes {
            renderCommandEncoder.setVertexBuffer(mesh.vertexBuffers[0].buffer, offset: 0, index: 0)
            renderCommandEncoder.drawIndexedPrimitives(type: .triangle, indexCount: submesh.indexCount, indexType: submesh.indexType, indexBuffer: submesh.indexBuffer.buffer, indexBufferOffset: 0)
        }
        
        renderCommandEncoder.endEncoding()
        guard let drawable = view.currentDrawable else { fatalError("Failed to get current drawable.") }
        commandBuffer.present(drawable)
        commandBuffer.commit()
    }
}

Shader:

#include <metal_stdlib>
using namespace metal;


struct VertexIn {
    float4 position [[ attribute(0) ]];
};

vertex float4 vertex_function(const VertexIn vIn [[ stage_in ]]) {
    return vIn.position;
}

fragment float4 fragment_function(const float4 vIn [[ stage_in ]]) {
    return float4(1,1,1,1);
}
0

There are 0 answers