将浮点或颜色传递到 Swift 的 Metal 片段着色器中



我正在尝试用 Metal 编写一个片段着色器,但无法弄清楚如何传入单个值(例如 float、float4 或 half4)。我的着色器如下:

#include <metal_stdlib>
using namespace metal;
typedef struct {
    float4 renderedCoordinate [[position]];
} FullscreenQuadVertex;
vertex FullscreenQuadVertex fullscreenQuad(unsigned int vertex_id [[ vertex_id ]]) {
    float4x4 renderedCoordinates = float4x4(float4( -1.0, -1.0, 0.0, 1.0 ),
                                            float4(  1.0, -1.0, 0.0, 1.0 ),
                                            float4( -1.0,  1.0, 0.0, 1.0 ),
                                            float4(  1.0,  1.0, 0.0, 1.0 ));
    FullscreenQuadVertex outVertex;
    outVertex.renderedCoordinate = renderedCoordinates[vertex_id];
    return outVertex;
}
fragment float4 displayColor(device float4 *color [[ buffer(0) ]]) {
//    return float4(0.2, 0.5, 0.8, 1);
    return *color;
}

我从 MTKView 子类中传递颜色,如下所示:

import MetalKit
class MetalView: MTKView {
    var color = NSColor(deviceRed: 0.2, green: 0.4, blue: 0.8, alpha: 1)
    var pipeline: MTLRenderPipelineState!
    var colorBuffer: MTLBuffer!
    init() {
        super.init(frame: CGRect.zero, device: nil)
        setup()
    }
    required init(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }
    func setup() {
        device = MTLCreateSystemDefaultDevice()
        colorPixelFormat = .bgra8Unorm
        // setup render pipeline for displaying the off screen buffer
        guard let library = device?.makeDefaultLibrary() else {
            fatalError("Failed to make Metal library")
        }
        let pipelineDescriptor = MTLRenderPipelineDescriptor()
        pipelineDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
        pipelineDescriptor.colorAttachments[0].isBlendingEnabled = false
        pipelineDescriptor.vertexFunction = library.makeFunction(name: "fullscreenQuad")
        pipelineDescriptor.fragmentFunction = library.makeFunction(name: "displayColor")
        do {
            pipeline = try device?.makeRenderPipelineState(descriptor: pipelineDescriptor)
        } catch {
            fatalError("Failed to make render pipeline state")
        }
        colorBuffer = device?.makeBuffer(length: MemoryLayout<float4>.stride, options: .storageModeManaged)
        updateBackgroundColor()
    }
    func updateBackgroundColor() {
        var colorArray = [color.blueComponent, color.greenComponent, color.redComponent, color.alphaComponent].map { Float($0) }
        var data = Data(buffer: UnsafeBufferPointer(start: &colorArray, count: colorArray.count))
        colorBuffer.contents().copyMemory(from: &data, byteCount: data.count)
        colorBuffer.didModifyRange(0..<data.count)
    }
    override func draw(_ dirtyRect: NSRect) {
        drawColor()
    }
    func drawColor() {
        guard let commandQueue = device?.makeCommandQueue() else { return }
        guard let commandBuffer = commandQueue.makeCommandBuffer() else { return }
        guard let renderPassDescriptor = currentRenderPassDescriptor else { return }
        guard let currentDrawable = currentDrawable else { return }
        guard let commandEncoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPassDescriptor) else { return }
        commandEncoder.setRenderPipelineState(pipeline)
        commandEncoder.setFragmentBuffer(colorBuffer, offset: 0, index: 0)
        commandEncoder.drawPrimitives(type: .triangleStrip, vertexStart: 0, vertexCount: 4)
        commandEncoder.endEncoding()
        commandBuffer.present(currentDrawable)
        commandBuffer.commit()
    }
}

尽管经过了我所期望的蓝色阴影,但我看到的只是黑色。测试从片段着色器返回的硬编码颜色工作正常。

我不是最流利地使用数据类型的 UnsafePointer 的人,所以不确定问题是否在于设置缓冲区数据,或者我实际尝试将缓冲区数据传递到着色器的方式。我确实尝试了属性类型为 [[ color(0) ]] 的着色器,但是据我所知,macOS 不支持这些着色器(或者我做错了🤷🏻 ♂️)。

与其对单一颜色使用缓冲区,为什么不直接使用 setFragmentBytes() 发送颜色?下面是它的外观:

var fragmentColor = vector_float4(Float(color.redComponent), Float(color.greenComponent), Float(color.blueComponent), Float(color.alphaComponent))
commandEncoder.setFragmentBytes(&fragmentColor, length: MemoryLayout.size(ofValue: fragmentColor), index: 0)

您的着色器仍将使用:

fragment float4 displayColor(constant float4 &color [[ buffer(0) ]])

最新更新