Включение смешивания приводит к сбою сборки renderPipelineState в MetalKit MTKView

Я визуализирую геометрию с некоторыми полупрозрачными областями (альфа ‹1) в металлическом метале MTKView. Если isBlendingEnabled в дескрипторе состояния конвейера рендеринга оставлено как false, тогда все будет отображаться так, как должно (хотя и сплошным цветом).

Я знаю, что рендеринг полупрозрачных объектов зависит от порядка рисования. На данный момент я просто хочу проверить, как выглядит альфа-смешение с полупрозрачными областями, смешанными с тем, что уже находится в буфере рендеринга, даже если оно просто сливается с фоном (на данный момент все еще только чистый цвет).

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

Компилятору не удалось создать запрос Error Domain = CompilerError Code = 1 «Фрагментный шейдер не записывает для рендеринга целевой цвет (0), индекс (1), необходимый для смешивания»

Вот код, который пытается построить состояние конвейера в делегате MTKView. Если он наследует свойства от MTKView, я поместил значение этих свойств в комментарии.

do {


let descriptor = MTLRenderPipelineDescriptor()
descriptor.vertexFunction = vertex
descriptor.fragmentFunction = fragment
descriptor.sampleCount = view.sampleCount // 4
descriptor.depthAttachmentPixelFormat = view.depthStencilPixelFormat //.depth32Float

let renderAttachment = descriptor.colorAttachments[0]
renderAttachment?.pixelFormat = view.colorPixelFormat //.bgra8Unorm

// following 7 lines cause makeRenderPipelineState to fail
renderAttachment?.isBlendingEnabled = true
renderAttachment?.alphaBlendOperation = .add
renderAttachment?.rgbBlendOperation = .add
renderAttachment?.sourceRGBBlendFactor = .sourceAlpha
renderAttachment?.sourceAlphaBlendFactor = .sourceAlpha
renderAttachment?.destinationRGBBlendFactor = .oneMinusSourceAlpha
renderAttachment?.destinationAlphaBlendFactor = .oneMinusSource1Alpha

computePipelineState = try device.makeComputePipelineState(function: kernel)
renderPipelineState = try device.makeRenderPipelineState(descriptor: descriptor)

} catch {
    print(error)
}

Учитывая, что ошибка связана с color(0), я добавил привязку color[0] к выводу фрагментного шейдера:

constant float3 directionalLight = float3(-50, -30, 80);

struct FragOut {
    float4 solidColor [[ color(0) ]];
};

fragment FragOut passThroughFragment(Vertex fragIn [[ stage_in ]]) {
    FragOut fragOut;
    fragOut.solidColor = fragIn.color;
    fragOut.solidColor.rgb *= max(0.4, dot(fragIn.normal, normalize(directionalLight)));
    return fragOut;
};

И, наконец, код отрисовки:

if let renderPassDescriptor = view.currentRenderPassDescriptor,
    let drawable = view.currentDrawable {
    let commandBuffer = queue.makeCommandBuffer()
    renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.8, green: 0.8, blue: 1, alpha: 1)
    let renderEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor)
    renderEncoder.setDepthStencilState(depthStencilState)
    renderEncoder.setRenderPipelineState(renderPipelineState)
    //renderEncoder.setTriangleFillMode(.lines)
    renderEncoder.setVertexBuffer(sceneBuffer, offset: 0, at: 0)
    renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, at: 1)           
    renderEncoder.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: Int(vertexCount) ) 

    renderEncoder.endEncoding()
    commandBuffer.present(drawable)
    commandBuffer.commit()
}

Я не устанавливаю явно какие-либо текстуры во фрагментном шейдере. Означает ли это, что currentDrawable неявно является прикреплением цвета с индексом 0? Почему в сообщении об ошибке нужно видеть color(0) в индексе 1? Требуется ли для смешивания двухцветная насадка? (он не может просто аддитивно смешиваться с тем, что уже было визуализировано?)

Спасибо.


person OliverD    schedule 01.05.2017    source источник


Ответы (1)


Похоже, вы непреднамеренно вызываете смешивание с двумя источниками. Вместо того, чтобы устанавливать destinationAlphaBlendFactor на oneMinusSource1Alpha, попробуйте oneMinusSourceAlpha (обратите внимание на отсутствующий 1).

Кроме того, ваша интуиция, что Metal по умолчанию записывает в приложение первого цвета, верна (текущий объект для рисования настроен MTKView как текстура первого вложения цвета). Вместо того, чтобы возвращать структуру с членом, которому присвоено значение [[color(0)]], вы можете просто вернуть float4 (или half4) из функции фрагмента, и этот цвет будет записан во вложение основного цвета. Однако то, как вы написали, должно работать, если ваши коэффициенты смешивания настроены правильно.

person warrenm    schedule 02.05.2017
comment
Ух ты, я этого не видел 1! Спасибо, теперь отлично работает. Значит, трафарет глубины также является неявной привязкой цвета к шейдеру? И могу ли я пробовать неявные вложения внутри шейдера, если я добавлю их в сигнатуру функции? Спасибо также за серию статей «Metal By Example», это отличный ресурс. Есть ли у вас планы на добавление MetalKit, Model I / O и т. Д.? - person OliverD; 02.05.2017
comment
Карта глубины - это отдельный тип прикрепления, но если вы установили формат пикселей глубины на вашем MTKView, текстура глубины также будет автоматически настроена как прикрепление глубины к дескриптору прохода рендеринга, предоставленному представлением. Я не верю, что вы можете использовать выборку из текущей карты глубины, как вы можете с выборкой из цветного фреймбуфера (для выполнения программируемого смешивания), хотя вы можете указать явное значение глубины, а не использовать значение, подразумеваемое позицией фрагмента в пространстве клипа. - person warrenm; 04.05.2017