如何向SCNProgram提供自定义数据?

时间:2018-12-03 20:35:17

标签: ios shader scenekit metal

我有一个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:)进行编程,但是我不确定顶点函数是否会为顶点获取正确的元素。

如何正确执行此操作?

1 个答案:

答案 0 :(得分:0)

您处在正确的轨道上,但是您不想使用SCNVector3来传递到Metal着色器中的数据。 SceneKit的矢量类型具有CGFloat类型的组件,其大小取决于平台。

相反,您的数据应使用simd向量类型之一。在Swift和Metal中,这表示float3float4。请注意,float3实际上占用了16个字节的空间;末尾有一个虚拟元素用于对齐目的。如果要紧密打包数据,每个顶点使用3个浮点数,则可以在Metal中以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作为参数,而是将一个{em> pointer 指向packed_float3并根据顶点ID对其进行索引:

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];
    // ...
}

这假定几何图形中的顶点与terrain数据点之间存在确切的对应关系。当然,除了直接使用顶点ID外,您还可以执行任何形式的花式索引操作,以查找给定顶点的地形数据。