为了增加 rickster 的回答,我会将您的模型封装在一个类中,该类包含每个模型一个缓冲区(或两个,如果您计算索引缓冲区),并传递一个带有该模型实例数的可选参数你想创建。
然后,保留一个额外的缓冲区,用于存储您想要为每个实例引入的任何变化。通常,它只是变换和不同的材质。例如,
struct PerInstanceUniforms {
var transform : Transform
var material : Material
}
在我的例子中,材质包含 UV 变换,但所有实例的纹理必须相同。
那么你的模型类会是这个样子,
class Model {
fileprivate var indexBuffer : MTLBuffer!
fileprivate var vertexBuffer : MTLBuffer!
var perInstanceUniforms : [PerInstanceUniforms]
let uniformBuffer : MTLBuffer!
// ... constructors, etc.
func draw(_ encoder: MTLRenderCommandEncoder) {
encoder.setVertexBuffer(vertexBuffer, offset: 0, at: 0)
RenderManager.sharedInstance.setUniformBuffer(encoder, atIndex: 1)
encoder.setVertexBuffer(self.uniformBuffer, offset: 0, at: 2)
encoder.drawIndexedPrimitives(type: .triangle, indexCount: numIndices, indexType: .uint16, indexBuffer: indexBuffer, indexBufferOffset: 0, instanceCount: self.numInstances)
}
// this gets called when we need to update the buffers used by the GPU
func updateBuffers(_ syncBufferIndex: Int) {
let uniformB = uniformBuffer.contents()
let uniformData = uniformB.advanced(by: MemoryLayout<PerInstanceUniforms>.size * perInstanceUniforms.count * syncBufferIndex).assumingMemoryBound(to: Float.self)
memcpy(uniformData, &perInstanceUniforms, MemoryLayout<PerInstanceUniforms>.size * perInstanceUniforms.count)
}
}
带有实例的顶点着色器看起来像这样,
vertex VertexInOut passGeometry(uint vid [[ vertex_id ]],
uint iid [[ instance_id ]],
constant TexturedVertex* vdata [[ buffer(0) ]],
constant Uniforms& uniforms [[ buffer(1) ]],
constant Transform* perInstanceUniforms [[ buffer(2) ]])
{
VertexInOut outVertex;
Transform t = perInstanceUniforms[iid];
float4x4 m = uniforms.projectionMatrix * uniforms.viewMatrix;
TexturedVertex v = vdata[vid];
outVertex.position = m * float4(t * v.position, 1.0);
outVertex.uv = float2(0,0);
outVertex.color = float4(0.5 * v.normal + 0.5, 1);
return outVertex;
}
这是我写的一个使用实例化的例子,并进行了性能分析:http://tech.metail.com/performance-quaternions-gpu/
您可以在此处找到完整代码以供参考:https://github.com/endavid/VidEngine
希望对你有帮助。