如何使用Metal IOS正确渲染3D模型?

时间:2019-08-22 11:30:44

标签: ios swift metal metalkit

我使用Blender创建了3D对象,并将其导出为OBJ文件,并按照此http://metalbyexample.com/modern-metal-1教程尝试使用Metal渲染它。但是我的某些3D对象零件丢失了。它们显示不正确。

这是我在Blender中的3D对象:- enter image description here

这是我在Metal中渲染的对象: enter image description here

这是我的搅拌器文件:- https://gofile.io/?c=XfQYLK

我该如何解决?

我已经成功渲染了其他一些形状,例如矩形,圆形,星形。但是问题在于这种形状。我没有改变创建形状的方式,也没有改变从搅拌机中导出形状的方式。即使我以相同的方式完成所有操作,问题仍然存在。

这是我加载OBJ文件的方式

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

这是我的顶点和片段着色器:-

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

这是我的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()
}

呈现可绘制对象的位置在另一个位置。

如何使用Metal正确渲染3D对象?

2 个答案:

答案 0 :(得分:0)

如果未正确对3D模型进行三角剖分,则会错过Metal中的行为。为了正确地渲染3D模型,从建模软件导出到OBJ文件时,请启用Triangulate Faces选项。这会将所有面变成三角形。因此,Metal不必重新对这些面进行三角剖分。但是此过程可能会更改顶点顺序。但是3D模型不会改变。只有顶点的顺序会改变。

答案 1 :(得分:0)

我发布了这个公共仓库:https://github.com/danielrosero/ios-touchingMetal,我认为这是使用Metal,纹理和计算功能进行3d渲染的一个很好的起点。

您应该只更改内部模型,检查Renderer.swift init(view: MTKView)方法。

//       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)


//        ****

这是我在实现中导入模型的方式,请检查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)

    }



}