从 Swift 将浮点数或颜色传递到金属片段着色器
Passing a float or color into a Metal fragment shader from Swift
我正在尝试用 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([=12=]) }
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()
}
}
尽管传递了我期望的蓝色阴影,但我看到的都是黑色。测试从片段着色器返回的硬编码颜色工作正常。
我对数据类型使用 UnsafePointers 不是最流利的,所以不确定问题是在设置缓冲区数据,还是我实际尝试将缓冲区数据传递到着色器的方式.我确实尝试了属性类型为 [[ 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) ]])
我正在尝试用 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([=12=]) }
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()
}
}
尽管传递了我期望的蓝色阴影,但我看到的都是黑色。测试从片段着色器返回的硬编码颜色工作正常。
我对数据类型使用 UnsafePointers 不是最流利的,所以不确定问题是在设置缓冲区数据,还是我实际尝试将缓冲区数据传递到着色器的方式.我确实尝试了属性类型为 [[ 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) ]])