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?
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);
}