使用initWithVertexBuffers进行MDLMesh对象的程序化生成

11
如果您正在使用iOS Metal,并且有自定义的编程对象模型,而且(像我一样)您的模型在Metal2和iOS 11出现后停止工作,那么您可能已经开始研究如何以编程方式生成MDLMesh。
苹果文档称:“通常情况下,您通过遍历MDLAsset对象的对象层次结构来获得网格,但您也可以从自己的顶点数据创建网格或创建参数化网格。”不幸的是,它没有提供任何指导或示例代码。
您可以快速找到MDLMesh初始化调用的两个双胞胎方法:initWithVertexBuffer和initWithVertexBuffers。但很快你会发现网上没有示例代码或讨论...至少我没有成功找到任何内容。
由于这对普通观察者来说并不直观,因此在此请求示例代码。

回答自己的问题并没有什么不妥,这在这里是相当普遍的。你应该继续这样做(并确保将其标记为已接受)。 - user5226582
1个回答

13

有很多创建MDLMesh的示例,可以使用其中一个工厂参数方法,例如创建一个立方体:

[MDLMesh newBoxWithDimensions:...

使用其中最简单的方式,对于一个“平面”(矩形),我生成了一个顶点数量最少的1x1矩形:

MDLMesh *mdlMesh = [MDLMesh newPlaneWithDimensions:(vector_float2){1.0, 1.0}
                                          segments:(vector_uint2){1, 1}
                                      geometryType:MDLGeometryTypeTriangles
                                         allocator:metalAllocator];

我随后使用Xcode调试器来研究生成的MDLMesh的情况,以指导我创建一个更简单的对象——编程平等三角形。

以下代码适用于我。我相信比我更懂Metal的人可以提供更好的解决方案。但这将希望让你朝着正确的方向开始…

因此,在出现新的工厂参数化方法之前,

[MDLMesh newEquilateralTriangleWithEdgeLength:...

下面的代码似乎能够解决问题...

static const float equilateralTriangleVertexData[] =
{
      0.000000,  0.577350,  0.0,
     -0.500000, -0.288675,  0.0,
      0.500000, -0.288675,  0.0,
};

static const vector_float3 equilateralTriangleVertexNormalsData[] =
{
    { 0.0,  0.0,  1.0 },
    { 0.0,  0.0,  1.0 },
    { 0.0,  0.0,  1.0 },
};

static const vector_float2 equilateralTriangleVertexTexData[] =
{
    { 0.50, 1.00 },
    { 0.00, 0.00 },
    { 1.00, 0.00 },
};

int numVertices = 3;

int lenBufferForVertices_position          = sizeof(equilateralTriangleVertexData);
int lenBufferForVertices_normal            = numVertices * sizeof(vector_float3);
int lenBufferForVertices_textureCoordinate = numVertices * sizeof(vector_float2);

MTKMeshBuffer *mtkMeshBufferForVertices_position          = (MTKMeshBuffer *)[metalAllocator newBuffer:lenBufferForVertices_position          type:MDLMeshBufferTypeVertex];
MTKMeshBuffer *mtkMeshBufferForVertices_normal            = (MTKMeshBuffer *)[metalAllocator newBuffer:lenBufferForVertices_normal            type:MDLMeshBufferTypeVertex];
MTKMeshBuffer *mtkMeshBufferForVertices_textureCoordinate = (MTKMeshBuffer *)[metalAllocator newBuffer:lenBufferForVertices_textureCoordinate type:MDLMeshBufferTypeVertex];

// Now fill the Vertex buffers with vertices.

NSData *nsData_position          = [NSData dataWithBytes:equilateralTriangleVertexData        length:lenBufferForVertices_position];
NSData *nsData_normal            = [NSData dataWithBytes:equilateralTriangleVertexNormalsData length:lenBufferForVertices_normal];
NSData *nsData_textureCoordinate = [NSData dataWithBytes:equilateralTriangleVertexTexData     length:lenBufferForVertices_textureCoordinate];

[mtkMeshBufferForVertices_position          fillData:nsData_position          offset:0];
[mtkMeshBufferForVertices_normal            fillData:nsData_normal            offset:0];
[mtkMeshBufferForVertices_textureCoordinate fillData:nsData_textureCoordinate offset:0];

NSArray <id<MDLMeshBuffer>> *arrayOfMeshBuffers = [NSArray arrayWithObjects:mtkMeshBufferForVertices_position, mtkMeshBufferForVertices_normal, mtkMeshBufferForVertices_textureCoordinate, nil];

static uint16_t indices[] =
{
    0,  1,  2,
};

int numIndices = 3;

int lenBufferForIndices = numIndices * sizeof(uint16_t);
MTKMeshBuffer *mtkMeshBufferForIndices = (MTKMeshBuffer *)[metalAllocator newBuffer:lenBufferForIndices type:MDLMeshBufferTypeIndex];

NSData *nsData_indices = [NSData dataWithBytes:indices length:lenBufferForIndices];
[mtkMeshBufferForIndices fillData:nsData_indices offset:0];

MDLScatteringFunction *scatteringFunction = [MDLPhysicallyPlausibleScatteringFunction new];
MDLMaterial *material = [[MDLMaterial alloc] initWithName:@"plausibleMaterial" scatteringFunction:scatteringFunction];

// Not allowed to create an MTKSubmesh directly, so feed an MDLSubmesh to an MDLMesh, and then use that to load an MTKMesh, which makes the MTKSubmesh from it.
MDLSubmesh *submesh = [[MDLSubmesh alloc] initWithName:@"summess" // Hackspeke for @"submesh"
                                           indexBuffer:mtkMeshBufferForIndices 
                                            indexCount:numIndices 
                                             indexType:MDLIndexBitDepthUInt16 
                                          geometryType:MDLGeometryTypeTriangles 
                                              material:material];

NSArray <MDLSubmesh *> *arrayOfSubmeshes = [NSArray arrayWithObjects:submesh, nil];

MDLMesh *mdlMesh = [[MDLMesh alloc] initWithVertexBuffers:arrayOfMeshBuffers
                                              vertexCount:numVertices
                                               descriptor:mdlVertexDescriptor
                                                submeshes:arrayOfSubmeshes];

更改了 equilateralTriangleVertexData 的定义,因为当它是 vector_float3 数组时,三角形无法正确显示。不知道为什么,但将其更改为 float 数组可以解决问题。其他数据似乎没有问题。 - Mike Roberts
我对ObjC不太确定,但在Swift中,当你将它们的数组放入缓冲区时,你需要使用stride而不是结构体的size,因为否则数据在内存中不对齐。在Metal方面,它会将一些填充数据读取为实际数据。 - undefined

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