【发布时间】:2017-11-01 18:51:08
【问题描述】:
我将一个结构数组传递给我的 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 和带有属性的着色器结构,以便它处理来自我的结构的“打包”数据?
非常感谢。
【问题讨论】:
-
你所有的顶点描述符属性对我来说都是正确的。您是否三重检查了枚举定义在您的 Swift 和 Metal 代码之间是否一致?
-
是的,非常确定——swift 和 metal 使用相同的枚举,来自一个 .h 文件,该文件具有 Metal/Swift 的#if 开关。在我看来,问题在于让它认识到 float3/4/etc 是打包数据。
-
您是否尝试过使用 GPU 帧捕获来查看 Metal 认为顶点数据的布局方式?通过检查应该可以看出问题是与包装/对齐相关还是其他问题。
-
@bsabiston:对于它的价值,Metal Programming Guide 中讨论顶点描述符的部分显示了一个带有“未对齐”
float4数据的示例。错位是由于在float2之后打包造成的,但没有理由与float3的情况不同。所以,正如沃伦所说,它应该可以工作。 -
@warrenm 谢谢!我忘记了我可以查看帧捕获中的顶点数据。起初我认为它看起来正确,但当我与工作版本比较时,它们实际上是不同的。它确实看起来确实跳过了第一个颜色浮动,认为它是 float3 的第四个看不见的组件。 Ken Thomases——它可能与 float2 不同,因为 float2 确实只有两个浮点数,对吧?而 float3 有四个浮点数。但是Metal肯定能够转换吗?
标签: metal vertex-shader vertex-attributes