3

我正在将一组结构传递给我的 Metal 着色器顶点函数。该结构如下所示:

struct Vertex {

  var x,y,z: Float     // position data
  var r,g,b,a: Float   // color data
  var s,t: Float       // texture coordinates
  var nX,nY,nZ: Float  // normal

  func floatBuffer() -> [Float] {
    return [x,y,z,r,g,b,a,s,t,nX,nY,nZ]
  }

};

floatBuffer 函数用于将顶点组装成一个大的浮点数组。我可以通过使用使用“打包”数据类型的结构定义将其传递到我的着色器函数中,如下所示:

struct VertexIn {
    packed_float3 position;
    packed_float4 color;
    packed_float2 texCoord;
    packed_float3 normal;
};


vertex VertexOut basic_vertex(
                          const device VertexIn* vertex_array [[ buffer(0) ]],
.
.
.

这行得通。但是,我想知道如何使用 MTLVertexAttributeDescriptors 和相关的语法来做同样的事情。现在我得到了变形的多边形,大概是因为 float3 和 packed_float3 的字节对齐差异?

这就是我现在尝试定义它并获取垃圾多边形的方式。我收到“packed_float3”对属性无效的错误,所以我试图弄清楚如何使常规 float3、float4 等工作。

struct VertexIn {
  float3 position [[attribute(RayVertexAttributePosition)]];
  float4 color [[attribute(RayVertexAttributeColor)]];
  float2 texCoord [[attribute(RayVertexAttributeTexCoord)]];
  float3 normal [[attribute(RayVertexAttributeNormal)]];
};

class func buildMetalVertexDescriptor() -> MTLVertexDescriptor {

    let mtlVertexDescriptor = MTLVertexDescriptor()
    var offset = 0

    mtlVertexDescriptor.attributes[RayVertexAttribute.position.rawValue].format = MTLVertexFormat.float3
    mtlVertexDescriptor.attributes[RayVertexAttribute.position.rawValue].offset = offset
    mtlVertexDescriptor.attributes[RayVertexAttribute.position.rawValue].bufferIndex = RayBufferIndex.positions.rawValue
    offset += 3*MemoryLayout<Float>.stride

    mtlVertexDescriptor.attributes[RayVertexAttribute.color.rawValue].format = MTLVertexFormat.float4
    mtlVertexDescriptor.attributes[RayVertexAttribute.color.rawValue].offset = offset
    mtlVertexDescriptor.attributes[RayVertexAttribute.color.rawValue].bufferIndex = RayBufferIndex.positions.rawValue
    offset += MemoryLayout<float4>.stride

    mtlVertexDescriptor.attributes[RayVertexAttribute.texCoord.rawValue].format = MTLVertexFormat.float2
    mtlVertexDescriptor.attributes[RayVertexAttribute.texCoord.rawValue].offset = offset
    mtlVertexDescriptor.attributes[RayVertexAttribute.texCoord.rawValue].bufferIndex = RayBufferIndex.positions.rawValue
    offset += MemoryLayout<float2>.stride

    mtlVertexDescriptor.attributes[RayVertexAttribute.normal.rawValue].format = MTLVertexFormat.float3
    mtlVertexDescriptor.attributes[RayVertexAttribute.normal.rawValue].offset = offset
    mtlVertexDescriptor.attributes[RayVertexAttribute.normal.rawValue].bufferIndex = RayBufferIndex.positions.rawValue
    offset += 3*MemoryLayout<Float>.stride

    print("stride \(offset)")
    mtlVertexDescriptor.layouts[RayBufferIndex.positions.rawValue].stride = offset
    mtlVertexDescriptor.layouts[RayBufferIndex.positions.rawValue].stepRate = 1
    mtlVertexDescriptor.layouts[RayBufferIndex.positions.rawValue].stepFunction = MTLVertexStepFunction.perVertex


    return mtlVertexDescriptor
}

请注意,我将第一个属性指定为 float3,但我指定了 3 个浮点的偏移量,而不是 float3 通常使用的 4 个。但这显然还不够。我想知道如何设置 MTLVertexDescriptor 和带有属性的着色器结构,以便它处理来自我的结构的“打包”数据?

非常感谢。

4

1 回答 1

1

关键在于您问题的这一部分:“请注意,我将第一个属性指定为 float3,但我指定了 3 个浮点数的偏移量,而不是 float3 通常使用的 4 个浮点数”。

SIMD float3 类型占用 16 个字节,它的内存布局与 non-packed Metal float3 类型相同。因此,当您将偏移量设置为仅 3*MemoryLayout.stride 时,您会丢失仍然存在的最后 4 个字节,从而导致下一个字段从这些额外字节中提取并且其余数据被偏移。

要真正使用打包类型将数据传输到 Metal(或任何图形 API),您要么必须坚持以前所做的并在数组中的三个单独的浮点数中指定 x、y、z,要么必须定义自己的结构像这样:

struct Vector3 {
  var x: Float
  var y: Float
  var z: Float
}

Swift 不保证这个结构将是三个紧密组合在一起的浮点数,但在现在和可预见的未来,它可以工作并且在大多数平台上大小为 12 字节。

如果您希望能够对这样的结构进行向量操作,那么我建议您寻找一个定义此类类型的库,以节省一些时间,因为您也会遇到与 3x3 矩阵相同类型的问题。

我遇到了同样的问题,所以我最终自己动手: https ://github.com/jkolb/Swiftish

于 2018-09-22T02:05:11.390 回答