【问题标题】:How to provide custom data to SCNProgram?如何向 SCNProgram 提供自定义数据?
【发布时间】:2019-05-05 04:51:55
【问题描述】:

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

如何正确地做到这一点?

【问题讨论】:

    标签: ios shader scenekit metal


    【解决方案1】:

    您在正确的轨道上,但您不想将SCNVector3 用于传递到金属着色器的数据。 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 作为顶点函数中的参数,而是将指针 指向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];
        // ...
    }
    

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-02-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-08-07
      • 1970-01-01
      • 2019-04-19
      • 1970-01-01
      相关资源
      最近更新 更多