Swift 使用金属绘制矩形
我正在尝试使用金属渲染矩形。但是矩形是倾斜的,如屏幕截图所示。我想知道这里出了什么问题 似乎没有使用顶点索引正确加载矩形的顶点。我试着效仿本文中的例子- 下面是MetalView的代码和使用的着色器-Swift 使用金属绘制矩形,swift,macos,metal,Swift,Macos,Metal,我正在尝试使用金属渲染矩形。但是矩形是倾斜的,如屏幕截图所示。我想知道这里出了什么问题 似乎没有使用顶点索引正确加载矩形的顶点。我试着效仿本文中的例子- 下面是MetalView的代码和使用的着色器- import Cocoa import Metal // Swift doesn't allow to extend a protocol with another protocol; however, we can do default implementation for a specif
import Cocoa
import Metal
// Swift doesn't allow to extend a protocol with another protocol; however, we can do default implementation for a specific protocol.
extension NSObjectProtocol {
/// Makes the receiving value accessible within the passed block parameter.
/// - parameter block: Closure executing a given task on the receiving function value.
public func setUp(_ block: (Self)->Void) {
block(self)
}
/// Makes the receiving value accessible within the passed block parameter and ends up returning the modified value.
/// - parameter block: Closure executing a given task on the receiving function value.
/// - returns: The modified value
public func set(_ block: (Self)->Void) -> Self {
block(self)
return self
}
}
extension MetalView {
private struct VertexInput {
var position: SIMD4<Float>
var rgba: SIMD4<Float>
}
}
/// `NSView` handling the first basic metal commands.
final class MetalView: NSView {
private let device: MTLDevice
private let queue: MTLCommandQueue
private let vertexBuffer: MTLBuffer
private let indexCount: Int
private let indexBuffer: MTLBuffer
// private let rectBuffer: MTLBuffer
private let renderPipeline: MTLRenderPipelineState
init?(frame: NSRect, device: MTLDevice, queue: MTLCommandQueue) {
// Setup the Device and Command Queue (non-transient objects: expensive to create. Do save it)
(self.device, self.queue) = (device, queue)
self.queue.label = App.bundleIdentifier + ".queue"
// Setup shader library
guard let library = device.makeDefaultLibrary(),
let vertexFunc = library.makeFunction(name: "rect_vertex"),
let fragmentFunc = library.makeFunction(name: "rect_fragment") else { return nil }
// Setup pipeline (non-transient)
let pipelineDescriptor = MTLRenderPipelineDescriptor().set {
$0.vertexFunction = vertexFunc
$0.fragmentFunction = fragmentFunc
$0.colorAttachments[0].pixelFormat = .bgra8Unorm // 8-bit unsigned integer [0, 255]
}
guard let pipelineState = try? device.makeRenderPipelineState(descriptor: pipelineDescriptor) else { return nil }
self.renderPipeline = pipelineState
// Setup buffer (non-transient). Coordinates defined in clip space: [-1,+1]
let vertices = [VertexInput(position: SIMD4(-0.5, -0.5, 0.0, 1.0), rgba: SIMD4(1.0, 1.0, 1.0, 1.0)),
VertexInput(position: SIMD4(0.5, 0.5, 0.0, 1.0), rgba: SIMD4(0.0, 1.0, 1.0, 1.0)),
VertexInput(position: SIMD4(-0.5, 0.5, 0.0, 0.0), rgba: SIMD4(1.0, 0.0, 0.0, 1.0)),
VertexInput(position: SIMD4(0.5, -0.5, 0.0, 1.0), rgba: SIMD4(0.0, 1.0, 1.0, 1.0))]
let size = vertices.count * MemoryLayout<VertexInput>.stride
guard let buffer = device.makeBuffer(bytes: vertices, length: size, options: .cpuCacheModeWriteCombined) else { return nil }
self.vertexBuffer = buffer.set { $0.label = App.bundleIdentifier + ".buffer" }
// set index info
let indexInfo : [UInt16] = [2, 1, 0, 0, 3, 1];
let indexCount = indexInfo.count * MemoryLayout<UInt16>.stride
guard let indexBuffer = device.makeBuffer(bytes: indexInfo, length: indexCount, options: .cpuCacheModeWriteCombined) else { return nil }
self.indexBuffer = indexBuffer.set { $0.label = App.bundleIdentifier + ".buffer" }
self.indexCount = indexInfo.count
super.init(frame: frame)
// Setup layer (backing layer)
self.wantsLayer = true
self.metalLayer.setUp { (layer) in
layer.device = device
layer.pixelFormat = .bgra8Unorm
layer.framebufferOnly = true
}
}
required init?(coder aDecoder: NSCoder) { fatalError() }
private var metalLayer: CAMetalLayer { self.layer as! CAMetalLayer }
override func makeBackingLayer() -> CALayer { CAMetalLayer() }
override func viewDidMoveToWindow() {
super.viewDidMoveToWindow()
guard let window = self.window else { return }
self.metalLayer.contentsScale = window.backingScaleFactor
self.redraw()
}
override func setBoundsSize(_ newSize: NSSize) {
super.setBoundsSize(newSize)
self.metalLayer.drawableSize = convertToBacking(bounds).size
self.redraw()
}
override func setFrameSize(_ newSize: NSSize) {
super.setFrameSize(newSize)
self.metalLayer.drawableSize = convertToBacking(bounds).size
self.redraw()
}
}
extension MetalView {
/// Draws a triangle in the metal layer drawable.
private func redraw() {
// Setup Command Buffer (transient)
guard let drawable = self.metalLayer.nextDrawable(),
let commandBuffer = self.queue.makeCommandBuffer() else { return }
let renderPass = MTLRenderPassDescriptor().set {
$0.colorAttachments[0].setUp { (attachment) in
attachment.texture = drawable.texture
attachment.clearColor = MTLClearColor(red: 0.6, green: 0.6, blue: 0.6, alpha: 1)
attachment.loadAction = .clear
attachment.storeAction = .store
}
}
guard let encoder = commandBuffer.makeRenderCommandEncoder(descriptor: renderPass) else { return }
encoder.setRenderPipelineState(self.renderPipeline)
encoder.setVertexBuffer(self.vertexBuffer, offset: 0, index: 0)
encoder.drawIndexedPrimitives(type: .triangle, indexCount: self.indexCount, indexType: .uint16, indexBuffer: self.indexBuffer, indexBufferOffset: 0)
// encoder.setFrontFacing(.counterClockwise)
encoder.endEncoding()
// Present drawable is a convenience completion block that will get executed once your command buffer finishes, and will output the final texture to screen.
commandBuffer.present(drawable)
commandBuffer.commit()
}
}
导入可可粉
进口金属
//Swift不允许使用其他协议扩展协议;但是,我们可以为特定协议执行默认实现。
扩展NSObjectProtocol{
///使接收值在传递的块参数内可访问。
///-参数块:对接收函数值执行给定任务的闭包。
公共函数设置(uu-block:(Self)->Void){
块(自身)
}
///使接收值在传递的块参数内可访问,并最终返回修改后的值。
///-参数块:对接收函数值执行给定任务的闭包。
///-返回:修改后的值
公共函数集(uu:(Self)->Void)->Self{
块(自身)
回归自我
}
}
扩展MetalView{
私有结构VertexInput{
变量位置:SIMD4
变量rgba:SIMD4
}
}
///`NSView`处理第一个基本金属命令。
最终类MetalView:NSView{
专用出租设备:MTLDevice
私有let队列:MTLCommandQueue
私有let vertexBuffer:MTLBuffer
私有let indexCount:Int
私人出租索引缓冲:MTLBuffer
//专用缓冲区:MTLBuffer
私有let renderPipeline:MTLRenderPipelineState
初始化?(帧:NSRect,设备:MTLDevice,队列:MTLCommandQueue){
//设置设备和命令队列(非临时对象:创建成本高。请保存)
(self.device,self.queue)=(设备,队列)
self.queue.label=App.bundleIdentifier+“.queue”
//设置着色器库
guard let library=device.makeDefaultLibrary(),
设vertexFunc=library.makeFunction(名称:“rect_vertex”),
让fragmentFunc=library.makeFunction(名称:“rect_fragment”)else{return nil}
//设置管道(非瞬态)
让pipelineDescriptor=MTLRenderPipelineDescriptor().set{
$0.vertexFunction=vertexFunc
$0.fragmentFunction=fragmentFunc
$0.colorAttachments[0].pixelFormat=.bgra8Unorm//8位无符号整数[0,255]
}
guard let pipelineState=try?device.makeRenderPipelineState(描述符:pipelineDescriptor)else{return nil}
self.renderPipeline=pipelineState
//设置缓冲区(非瞬态)。在剪辑空间中定义的坐标:[-1,+1]
设顶点=[VertexInput(位置:SIMD4(-0.5,-0.5,0.0,1.0),rgba:SIMD4(1.0,1.0,1.0,1.0)),
顶点输入(位置:SIMD4(0.5,0.5,0.0,1.0),rgba:SIMD4(0.0,1.0,1.0,1.0)),
顶点输入(位置:SIMD4(-0.5,0.5,0.0,0.0),rgba:SIMD4(1.0,0.0,0.0,1.0)),
顶点输入(位置:SIMD4(0.5,-0.5,0.0,1.0),rgba:SIMD4(0.0,1.0,1.0,1.0))]
让大小=顶点数*MemoryLayout.stride
guard let buffer=device.makeBuffer(字节:顶点,长度:大小,选项:.cpucachodewritecombined)else{return nil}
self.vertexBuffer=buffer.set{$0.label=App.bundleIdentifier+“.buffer”}
//设置索引信息
让indexInfo:[UInt16]=[2,1,0,0,3,1];
让indexCount=indexInfo.count*MemoryLayout.stride
guard let indexBuffer=device.makeBuffer(字节:indexInfo,长度:indexCount,选项:.cpucachedWriteCombined)else{return nil}
self.indexBuffer=indexBuffer.set{$0.label=App.bundleIdentifier+“.buffer”}
self.indexCount=indexInfo.count
super.init(frame:frame)
//设置层(背衬层)
self.wantsLayer=true
self.metalLayer.setUp{(层)位于
层设备=设备
layer.pixelFormat=.bgra8Unorm
layer.framebufferOnly=true
}
}
必需的初始化?(编码器aDecoder:NSCoder){fatalError()}
私有变量metalLayer:CAMetalLayer{self.layer as!CAMetalLayer}
重写func makeBackingLayer()->CALayer{CAMetalLayer()}
重写func VIEWDIDMOVETOW(){
super.viewdimovetowindow()
guard let window=self.window else{return}
self.metalayer.contentsScale=window.backingScaleFactor
self.redraw()
}
重写func setBoundsSize(uSize:NSSize){
super.setBoundsSize(新闻大小)
self.metalayer.drawableSize=ConvertToBack(bounds).size
self.redraw()
}
重写func setFrameSize(\uNewSize:NSSize){
super.setFrameSize(新闻大小)
self.metalayer.drawableSize=ConvertToBack(bounds).size
self.redraw()
}
}
扩展MetalView{
///在可绘制的金属层中绘制三角形。
私有函数重绘(){
//设置命令缓冲区(瞬态)
guard let drawable=self.metalLayer.nextDrawable(),
让commandBuffer=self.queue.makeCommandBuffer()else{return}
让renderPass=MTLRenderPassDescriptor().set{
$0.colorAttachments[0]。安装程序{(附件)位于
attachment.texture=drawable.texture
附件.clearColor=MTLClearColor(红色:0.6,绿色:0.6,蓝色:0.6,alpha:1)
附件.loadAction=.clear
附件.storeAction=.store
}
}
guard let encoder=commandBuffer.makeRenderCommandEncoder(描述符:renderPass)else{return}
编码器.setRenderPipelineState(self.renderPipeline)
编码器.setVertexBuffer(self.vertexBuffer,o
#include <metal_stdlib>
using namespace metal;
struct VertexInput {
float4 position [[ position ]];
float4 rgba;
};
vertex VertexInput rect_vertex(device VertexInput const* const vertices [[buffer(0)]], uint vid [[vertex_id]]) {
return vertices[vid];
}
fragment float4 rect_fragment(VertexInput vert [[stage_in]]) {
return vert.rgba;
}