swift - 在顶点着色器之前将计算/内核函数应用于顶点缓冲区

标签 swift shader metal compute-shader metalkit

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

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

背景

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

最佳答案

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

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

// 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)]]) 

关于swift - 在顶点着色器之前将计算/内核函数应用于顶点缓冲区,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53970204/

相关文章:

swift - 为什么三角形渲染时边缘粗糙而不是平滑边缘? Metal 、Swift、Xcode

ios - SceneKit - 在不使用单独的 SCNRenderer 的情况下从 SCNView 获取渲染场景作为 MTLTexture

ios - swift 中字典键的访问值

ios - 将大型数据集导入核心数据,在 Swift 中建立关系

ios - swift - UIImageWriteToSavedPhotosAlbum 导致 EXC_BAD_ACCESS

java - 着色高度图的面而不是顶点

python - 使用 GLSL 的 OpenGL 阴影贴图

c++ - D3DReflect 不检测常量缓冲区

iOS Swift 给出错误 : Garbage at end when serializing data from json

iOS Metal Shader - 纹理读写访问?