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

5
我使用blender创建了一个3D对象,并将其导出为OBJ文件,然后我按照这个http://metalbyexample.com/modern-metal-1教程所示尝试使用Metal来渲染它。但我的一些3D部件丢失了,它们没有被正确地渲染出来。
以下是我的blender 3D对象: enter image description here 以下是我在Metal中渲染的对象: enter image description here 以下是我的blender文件: https://gofile.io/?c=XfQYLK 我应该如何解决这个问题?
我已经成功地渲染了一些其他形状,比如矩形、圆形和星形。但是这个形状有问题。我没有改变创建形状的方式,也没有改变从blender导出的方式。即使我以同样的方式进行了所有操作,问题仍然存在。
以下是我加载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对象?


1
这可能是由于背面剔除引起的。在绘制之前,您是否尝试在渲染命令编码器上调用 setCullMode(.none)?如果是这样,那么您的模型导出器正在导出具有不一致面向的三角形。您可以通过在建模软件中选择有问题的三角形并将其“翻转”来修复它。 - warrenm
@warrenm 是的,我尝试过使用setCullMode(.none),但它没有起作用。因为当我从blender导出OBJ文件时,我没有对所有面进行三角剖分。Metal处理了三角剖分。所以这就引起了问题。在我启用从blender导出时的三角剖分后,它就正常工作了。在我的情况下,还有另一个问题,因为三角剖分会改变3D对象的顶点顺序。但是它显示得很好。所以你的建议很好。你能否将“启用三角剖分导出”添加到你的评论中,并将其发布为答案。这样我就可以标记它了。谢谢。 - Nilupul Sandeepa
2个回答

2
我创建了这个公共代码库: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)

    }



}

1
如果3D模型没有正确三角剖分,则在Metal中会出现问题。为了正确渲染3D模型,从建模软件导出OBJ文件时,请打开“三角剖分面”选项。这将把所有面都转换成三角形。因此,Metal不必重新对面进行三角剖分。但是,这个过程可能会改变顶点的顺序。但是3D模型不会改变,只有顶点的顺序会改变。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接