我将一个结构数组传递给我的Metal shader顶点函数。结构看起来像这样:
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函数用于将顶点组装成一个大的Floats数组。我可以通过使用使用“压缩”数据类型的结构定义将它传递给我的着色器函数,如下所示:
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和带有属性的着色器结构,以便它处理我的结构中的“打包”数据?
非常感谢。
答案 0 :(得分:1)
关键在于您问题的这一部分:“请注意,我将第一个属性指定为float3,但是我指定了3个float的偏移量,而不是float3通常使用的4个偏移量。”
SIMD float3类型占用16个字节,与未打包的Metal float3类型具有相同的内存布局。因此,当您将偏移量设置为仅3 * MemoryLayout.stride时,您将丢失最后4个字节,这些字节仍然存在,从而导致下一个字段从这些额外的字节中拉出,而其余数据将被偏移。
要真正使用打包类型将数据传输到Metal(或任何图形API),您必须坚持以前的工作,并在数组的三个独立Float中指定x,y,z,或者必须定义你自己的结构是这样的:
systemctl start docker
Swift不能保证此结构将紧密地封装在一起的三个Float,但是在现在和可预见的将来,它仍然可以正常工作,并且在大多数平台上的大小为12个字节。
如果您希望能够在这样的结构上执行向量运算,那么我建议您寻找一个定义此类类型的库,以节省一些时间,因为您也会遇到3x3矩阵的相同类型的问题
我遇到了同样的问题,所以最终我自己滚动了: https://github.com/jkolb/Swiftish