【问题标题】:Applying compute/kernel function to vertex buffer before vertex shader在顶点着色器之前将计算/内核函数应用于顶点缓冲区
【发布时间】:2019-05-26 23:14:08
【问题描述】:

我想使用计算着色器来修改我的顶点,然后再将它们传递给顶点着色器。我找不到任何例子或解释,除了似乎在这里提到:Metal emulate geometry shaders using compute shaders。这对我没有帮助,因为它没有解释它的 CPU 部分。

我已经看到了许多在计算着色器中读取和写入纹理缓冲区的示例,但我需要读取和修改顶点缓冲区,其中包含具有法线的自定义顶点结构,并由 MDLMesh 创建。我将永远感谢一些示例代码!

背景

我真正想要实现的是能够在 GPU 上修改顶点法线。另一种选择是如果我可以从顶点着色器访问整个三角形,就像在链接的答案中一样。出于某种原因,我只能使用 stage_in 属性访问单个顶点。在这种特殊情况下,使用整个缓冲区对我不起作用,这可能与使用模型 I/O 和 MDLMesh 提供的网格有关。当我手动创建顶点时,我可以访问顶点缓冲区数组。话虽如此,使用该解决方案我将不得不为每个三角形计算三次新的顶点法线向量,这似乎很浪费,并且无论如何我希望能够将计算着色器应用于顶点缓冲区!

【问题讨论】:

  • 在某个时候,您已经设置了 Metal 渲染管线的 vertexDescriptor,对吧?您还设置了与该顶点描述符的布局相对应的缓冲区。您将不得不使用它们直接访问顶点数据。我认为没有任何简单的方法可以做你想做的事。您可以将 vertexDescriptor 转换为 stageInputDescriptor 用于计算管道,但您仍然一次只能看到一个顶点。
  • 感谢您的回答。当然,我已经发现如果我不使用 metalkit 网格制作的顶点描述符,那么我可以放弃 stage_in 并访问 Metal 中的整个缓冲区,这样该解决方案现在就可以工作了,但这很浪费,因为我需要这样做我的计算每个顶点 3 或 4 次,而且不是很灵活。我真正想做的是读取计算内核中的顶点缓冲区,修改它们并写入第二个缓冲区或类似的缓冲区,然后在顶点着色器中读取调整后的顶点缓冲区,但我发现的所有示例都修改了一个 texture2d在计算着色器中。
  • 在计算着色器中修改缓冲区没有问题。让它接受一个像device VertexIn *buffer [[buffer(0)]] 这样的参数。然后,在函数中,只需读取和/或写入buffer[vertexIndex].field。您通常会根据网格位置计算顶点索引(对于一维计算“网格”,可能就像一对一一样简单)。
  • 好的,我认为不可能在“第 1 层”设备上写入顶点缓冲区。此外,在他们写入内核函数中的纹理的情况下,他们似乎通过 access::read 传入引用并通过 access::write 写入另一个引用,顶点缓冲区不需要吗?我还担心金属会确保特定顶点在进入计算着色器之前不会发送到顶点着色器。理想情况下,我想以 4 个为一组修改顶点,因为四边形中的每个顶点都应该具有相同的调整法线!你有任何示例代码吗?
  • 您似乎混淆了缓冲区和纹理。内核和顶点函数始终能够读取和写入缓冲区。写入缓冲区的片段函数存在限制。此外,在图形函数中写入 textures 也有局限性。关于顶点缓冲区,Metal 并没有什么特别之处。它们只是像其他缓冲区一样的缓冲区。不用担心计算着色器和渲染命令在 GPU 上的竞争。没有示例代码。对不起。

标签: swift shader metal compute-shader metalkit


【解决方案1】:

感谢 Ken Thomases 的 cmets,我设法找到了解决方案。他让我意识到这很简单:

我正在使用如下所示的顶点结构:

// Metal side
struct Vertex {
    float4 position;
    float4 normal;
    float4 color;
};

// Swift side
struct Vertex {
    var position: float4
    var normal: float4
    var color: float4
}

在我通常创建顶点缓冲区、索引缓冲区和渲染管道状态的设置期间,我现在还创建了计算管道状态:

// Vertex buffer
let dataSize = vertexData.count*MemoryLayout<Vertex>.stride
vertexBuffer = device.makeBuffer(bytes: vertexData, length: dataSize, options: [])!

// Index buffer
indexCount = indices.count
let indexSize = indexCount*MemoryLayout<UInt16>.stride
indexBuffer = device.makeBuffer(bytes: indices, length: indexSize, options: [])!

// Compute pipeline state
let adjustmentFunction = library.makeFunction(name: "adjustment_func")!
cps = try! device.makeComputePipelineState(function: adjustmentFunction)

// Render pipeline state
let rpld = MTLRenderPipelineDescriptor()
rpld.vertexFunction = library.makeFunction(name: "vertex_func")
rpld.fragmentFunction = library.makeFunction(name: "fragment_func")
rpld.colorAttachments[0].pixelFormat = .bgra8Unorm
rps = try! device.makeRenderPipelineState(descriptor: rpld)

commandQueue = device.makeCommandQueue()!

那么我的渲染函数是这样的:

let black = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1)
rpd.colorAttachments[0].texture = drawable.texture
rpd.colorAttachments[0].clearColor = black
rpd.colorAttachments[0].loadAction = .clear

let commandBuffer = commandQueue.makeCommandBuffer()!

let computeCommandEncoder = commandBuffer.makeComputeCommandEncoder()!
computeCommandEncoder.setComputePipelineState(cps)
computeCommandEncoder.setBuffer(vertexBuffer, offset: 0, index: 0)
computeCommandEncoder.dispatchThreadgroups(MTLSize(width: meshSize*meshSize, height: 1, depth: 1), threadsPerThreadgroup: MTLSize(width: 4, height: 1, depth: 1))
computeCommandEncoder.endEncoding()

let renderCommandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: rpd)!
renderCommandEncoder.setRenderPipelineState(rps)
renderCommandEncoder.setFrontFacing(.counterClockwise)
renderCommandEncoder.setCullMode(.back)

updateUniforms(aspect: Float(size.width/size.height))
renderCommandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
renderCommandEncoder.setVertexBuffer(uniformBuffer, offset: 0, index: 1)
renderCommandEncoder.setFragmentBuffer(uniformBuffer, offset: 0, index: 1)
renderCommandEncoder.drawIndexedPrimitives(type: .triangle, indexCount: indexCount, indexType: .uint16, indexBuffer: indexBuffer, indexBufferOffset: 0)
renderCommandEncoder.endEncoding()

commandBuffer.present(drawable)
commandBuffer.commit()

最后我的计算着色器看起来像这样:

kernel void adjustment_func(const device Vertex *vertices [[buffer(0)]], uint2 gid [[thread_position_in_grid]]) {
    vertices[gid.x].position = function(pos.xyz);
}

这是我的顶点函数的签名:

vertex VertexOut vertex_func(const device Vertex *vertices [[buffer(0)]], uint i [[vertex_id]], constant Uniforms &uniforms [[buffer(1)]]) 

【讨论】:

  • 在这种情况下meshSize 是什么?
猜你喜欢
  • 1970-01-01
  • 2013-11-23
  • 1970-01-01
  • 1970-01-01
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 2021-04-18
  • 1970-01-01
相关资源
最近更新 更多