Применение функции вычисления / ядра к буферу вершин перед вершинным шейдером - PullRequest
0 голосов
/ 29 декабря 2018

Я бы хотел использовать вычислительный шейдер, чтобы изменить мои вершины, прежде чем они будут переданы в вершинный шейдер.Я не могу найти никаких примеров или объяснений этого, за исключением того, что это, кажется, упомянуто здесь: Metal эмулируют геометрические шейдеры, используя вычислительные шейдеры .Это не помогает мне, так как не объясняет часть процессора.

Я видел много примеров, когда буфер текстур читается и записывается в вычислительный шейдер, но мне нужно прочитать и изменить буфер вершин, который содержит пользовательские структуры вершин с нормалями и создается MDLMesh,Я был бы всегда благодарен за пример кода!

ПРЕДПОСЫЛКИ

То, чего я на самом деле хочу достичь, - это действительно возможность изменять нормали вершин в графическом процессоре.Другой вариант был бы, если бы я мог получить доступ ко всему треугольнику из вершинного шейдера, как в связанном ответе.По какой-то причине я могу получить доступ только к одной вершине, используя атрибут stage_in.Использование всего буфера не работает для меня в этом конкретном случае, это, вероятно, связано с использованием сетки, предоставляемой Model I / O и MDLMesh.Когда я создаю вершины вручную, я могу получить доступ к массиву буферов вершин.Сказав это, с этим решением мне придется вычислять новый вектор нормали вершины три раза для каждого треугольника, который кажется расточительным, и в любом случае я хочу иметь возможность применять вычислительные шейдеры к буферу вершин!

1 Ответ

0 голосов
/ 31 декабря 2018

Благодаря комментариям Кена Томаса, мне удалось найти решение.Он заставил меня понять, что это довольно просто:

Я использую структуру вершин, которая выглядит следующим образом:

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