5

I created a 3D object using blender and exported it as an OBJ file and I tried to render it using Metal by following this http://metalbyexample.com/modern-metal-1 tutorial. But some of my 3D object parts are missing. They are not rendered properly.

Here is my 3D object in blender :- enter image description here

Here is my rendered object in Metal :- enter image description here

Here is my blender file :- https://gofile.io/?c=XfQYLK

How should i fix this?

I already rendered some other shapes like, rectangle, Circle, Star successfully. But the problem is with this shape. I did not change the way i create the shape nor the way it is exported from the blender. Even though I did everything in same way problem still there.

Here is how i load the OBJ file

private var vertexDescriptor: MTLVertexDescriptor!
private var meshes: [MTKMesh] = []

private func loadResource() {
        let modelUrl = Bundle.main.url(forResource: self.meshName, withExtension: "obj")
        let vertexDescriptor = MDLVertexDescriptor()
        vertexDescriptor.attributes[0] = MDLVertexAttribute(name: MDLVertexAttributePosition, format: .float3, offset: 0, bufferIndex: 0)
        vertexDescriptor.attributes[1] = MDLVertexAttribute(name: MDLVertexAttributeNormal, format: .float3, offset: MemoryLayout<Float>.size * 3, bufferIndex: 0)
        vertexDescriptor.attributes[2] = MDLVertexAttribute(name: MDLVertexAttributeTextureCoordinate, format: .float2, offset: MemoryLayout<Float>.size * 6, bufferIndex: 0)
        vertexDescriptor.layouts[0] = MDLVertexBufferLayout(stride: MemoryLayout<Float>.size * 8)
        self.vertexDescriptor = MTKMetalVertexDescriptorFromModelIO(vertexDescriptor)

        let bufferAllocator = MTKMeshBufferAllocator(device: self.device)
        let asset = MDLAsset(url: modelUrl, vertexDescriptor: vertexDescriptor, bufferAllocator: bufferAllocator)
        (_, meshes) = try! MTKMesh.newMeshes(asset: asset, device: device)
}

Here is my vertex and fragment shaders :-

struct VertexOut {
    float4 position [[position]];
    float4 eyeNormal;
    float4 eyePosition;
    float2 texCoords;
};

vertex VertexOut vertex_3d(VertexIn vertexIn [[stage_in]])
{
    VertexOut vertexOut;
    vertexOut.position = float4(vertexIn.position, 1);
    vertexOut.eyeNormal = float4(vertexIn.normal, 1);
    vertexOut.eyePosition = float4(vertexIn.position, 1);
    vertexOut.texCoords = vertexIn.texCoords;
    return vertexOut;
}

fragment float4 fragment_3d(VertexOut fragmentIn [[stage_in]]) {
    return float4(0.33, 0.53, 0.25, 0.5);
}

And here my CommandEncoder :-

func render(commandEncoder: MTLRenderCommandEncoder) {
    commandEncoder.setRenderPipelineState(self.renderPipelineState)
    let mesh = meshes[0]
    let vertexBuffer = mesh.vertexBuffers.first!
    commandEncoder.setVertexBuffer(vertexBuffer.buffer, offset: vertexBuffer.offset, index: 0)
    let indexBuffer = mesh.submeshes[0].indexBuffer
    commandEncoder.drawIndexedPrimitives(type: mesh.submeshes[0].primitiveType,
                                                 indexCount: mesh.submeshes[0].indexCount,
                                                 indexType: mesh.submeshes[0].indexType,
                                                 indexBuffer: indexBuffer.buffer,
                                                 indexBufferOffset: indexBuffer.offset)
    commandEncoder.endEncoding()
}

Presenting to the drawable is handled in a different place.

How should i properly render my 3D object using Metal?

Ramprasath Selvam
  • 3,868
  • 3
  • 25
  • 41
Nilupul Sandeepa
  • 748
  • 6
  • 20
  • 1
    This might be due to backface culling. Have you tried calling `setCullMode(.none)` on your render command encoder before drawing? If so, your model exporter is exporting triangles with inconsistent facing. You might be able to fix it by selecting the offending triangles in your modeler and "flipping" them. – warrenm Aug 22 '19 at 18:06
  • @warrenm yes i tried with ```setCullMode(.none)``` but it was not working. Because when I am exporting OBJ file from blender I do not triangulate all the faces. Metal handled triangulation. So it causes the problem. After I enable triangulation when exporting from blender it worked fine. In my context there is an another problem because triangulation changes the vertex order of the 3D object. But it shown properly. So your suggestion works fine. Can you add 'exporting with triangulation enabled' to your comment and post it as an answer. So I can mark it. Thanks You – Nilupul Sandeepa Aug 23 '19 at 04:41

2 Answers2

2

I made this public repo: https://github.com/danielrosero/ios-touchingMetal, and I think is a great starting point for 3d Rendering with Metal, with textures and a compute function.

You should just change the models inside, check Renderer.swift init(view: MTKView) method.

//       Create the MTLTextureLoader options that we need according to each model case. Some of them are flipped, and so on.


let textureLoaderOptionsWithFlip: [MTKTextureLoader.Option : Any] = [.generateMipmaps : true, .SRGB : true, .origin : MTKTextureLoader.Origin.bottomLeft]

let textureLoaderOptionsWithoutFlip: [MTKTextureLoader.Option : Any] = [.generateMipmaps : true, .SRGB : true]



//        ****




//        Initializing the models, set their position, scale and do a rotation transformation

//        Cat model

cat = Model(name: "cat",vertexDescriptor: vertexDescriptor,textureFile: "cat.tga", textureLoaderOptions: textureLoaderOptionsWithFlip)
cat.transform.position = [-1, -0.5, 1.5]
cat.transform.scale = 0.08

cat.transform.rotation = vector_float3(0,radians(fromDegrees: 180),0)


//        ****


//        Dog model

dog = Model(name: "dog",vertexDescriptor: vertexDescriptor,textureFile: "dog.tga", textureLoaderOptions: textureLoaderOptionsWithFlip)
dog.transform.position = [1, -0.5, 1.5]
dog.transform.scale = 0.018

dog.transform.rotation = vector_float3(0,radians(fromDegrees: 180),0)


//        ****

This is the way I import models in my implementation, check Model.swift

//
//  Model.swift
//  touchingMetal
//
//  Created by Daniel Rosero on 1/8/20.
//  Copyright © 2020 Daniel Rosero. All rights reserved.
//
import Foundation
import MetalKit

//This extension allows to create a MTLTexture attribute inside this Model class
//in order to be identified and used in the Renderer. This is to ease the loading in case of multiple models in the scene
extension Model : Texturable{

}

class Model {

    let mdlMeshes: [MDLMesh]
    let mtkMeshes: [MTKMesh]
    var texture: MTLTexture?
    var transform = Transform()
    let name: String

    //In order to create a model, you need to pass a name to use it as an identifier,
    //    a reference to the vertexDescriptor, the imagename with the extension of the texture,
    //the dictionary of MTKTextureLoader.Options

    init(name: String, vertexDescriptor: MDLVertexDescriptor, textureFile: String, textureLoaderOptions: [MTKTextureLoader.Option : Any]) {
        let assetUrl = Bundle.main.url(forResource: name, withExtension: "obj")
        let allocator = MTKMeshBufferAllocator(device: Renderer.device)

        let asset = MDLAsset(url: assetUrl, vertexDescriptor: vertexDescriptor, bufferAllocator: allocator)

        let (mdlMeshes, mtkMeshes) = try! MTKMesh.newMeshes(asset: asset, device: Renderer.device)
        self.mdlMeshes = mdlMeshes
        self.mtkMeshes = mtkMeshes
        self.name = name
        texture = setTexture(device: Renderer.device, imageName: textureFile, textureLoaderOptions: textureLoaderOptions)

    }



}
danielrosero
  • 596
  • 8
  • 14
1

If the 3D model is not triangulated properly it will miss behave in Metal. In order to render 3D model correctly, When exporting from modeling software to an OBJ file turn on Triangulate Faces option. This will turn all the faces to triangles. So Metal will not have to re triangulate the faces. But this process may change the vertex order. But 3D model will not change. Only the order of vertices will change.

Nilupul Sandeepa
  • 748
  • 6
  • 20