Вы на правильном пути, но вы не хотите использовать SCNVector3
для данных, которые вы передаете в металлический шейдер.Векторные типы SceneKit имеют компоненты типа CGFloat
, размер которых зависит от платформы.
Вместо этого ваши данные должны использовать один из векторных типов simd.В Swift and Metal это означает float3
или float4
.Обратите внимание, что 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
и внесите в него индекс согласно идентификатору вершины:
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];
// ...
}
Это предполагает точное соответствие между вершинами в вашей геометрии и точками данных местности.Вместо непосредственного использования идентификатора вершины вы, конечно же, можете выполнять любые необычные операции индексирования, которые вы хотите искать для данных о местности для данной вершины.