0

我有一个数组SCNVector3,每个顶点一个。

var terrainArray = [SCNVector3]()

我需要在我的片段着色器中为每个顶点提供这些数据。像这样的东西:

struct TerrainVertexInput
{
    float3 position [[attribute(SCNVertexSemanticPosition)]];
    float4 color [[attribute(SCNVertexSemanticColor)]];
};

struct TerrainVertexOutput
{
    float4 position [[position]];
    float3 terrain;
    float4 color;
};

vertex TerrainVertexOutput terrainVertex(TerrainVertexInput in [[stage_in]],
                                         constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                                         constant MyNodeBuffer& scn_node [[buffer(1)]],
                                         constant float3 terrain [[buffer(2)]])
{
    TerrainVertexOutput v;

    v.position = scn_node.modelViewProjectionTransform * float4(in.position, 1.0);
    v.terrain = terrain;
    v.color = in.color;

    return v;
}

据我了解,我需要Data使用数组数据创建对象并将其提供给编程,setValue(_:forKey:)但我不确定顶点函数是否会为顶点获取正确的元素。

如何正确地做到这一点?

4

1 回答 1

2

您走在正确的轨道上,但您不想将SCNVector3数据用于传递给金属着色器的数据。SceneKit 的向量类型具有 type 的组件CGFloat,其大小取决于平台。

相反,您的数据应该使用其中一种 simd 矢量类型。在 Swift 和 Metal 中,这意味着float3or float4。注意float3实际占用16字节空间;最后有一个虚拟元素用于对齐目的。如果你想紧密地打包你的数据,每个顶点正好使用 3 个浮点数,你可以在 Metal as 中输入你的缓冲区,packed_float3并将 3 个连续的浮点数写入每个顶点的数据缓冲区。Swift 中没有三元素压缩浮点向量类型。

有很多方法可以将数组复制SCNVector3到适当类型的数据缓冲区中。这是一个:

// Allocate enough memory to store three floats per vertex, ensuring we free it later
let terrainBuffer = UnsafeMutableBufferPointer<Float>.allocate(capacity: terrainArray.count * 3)
defer {
    terrainBuffer.deallocate()
}

// Copy each element of each vector into the buffer
terrainArray.enumerated().forEach { i, v in
    terrainBuffer[i * 3 + 0] = Float(v.x)
    terrainBuffer[i * 3 + 1] = Float(v.y)
    terrainBuffer[i * 3 + 2] = Float(v.z)
}

// Copy the buffer data into a Data object, as expected by SceneKit
let terrainData = Data(buffer: terrainBuffer)

然后,您可以setValue(:forKey:)在几何体或材料上使用:

material.setValue(terrainData, forKey: "terrain")

与其在顶点函数中将单个float3参数作为参数,不如根据顶点 ID获取指针并对其进行索引:packed_float3

vertex TerrainVertexOutput terrainVertex(TerrainVertexInput in              [[stage_in]],
                                         constant SCNSceneBuffer& scn_frame [[buffer(0)]],
                                         constant MyNodeBuffer& scn_node    [[buffer(1)]],
                                         constant packed_float3 *terrain    [[buffer(2)]],
                                         uint vid                           [[vertex_id]]) {
    // ...
    v.terrain = terrain[vid];
    // ...
}

这假设几何中的顶点与地形数据点之间存在精确对应关系。除了直接使用顶点 ID,您当然可以做任何您想要查找给定顶点的地形数据的花哨的索引。

于 2018-12-04T02:28:26.323 回答