使用Metal繪製一個三角形

import UIKit
import Metal
import GLKit
struct Vertex {
    let position: simd_float4
    let color: simd_float4
}

class MetalView: UIView {
    
    
    let device = MTLCreateSystemDefaultDevice()
    var pipeLineState: MTLRenderPipelineState!
    var commandQueue: MTLCommandQueue!
    var vertexBuffer: MTLBuffer!
    override init(frame: CGRect) {
        super.init(frame: frame)
        let layer = self.layer as! CAMetalLayer
        layer.device = self.device
        layer.pixelFormat = .bgra8Unorm
        layer.drawableSize = CGSize.init(width: frame.width * UIScreen.main.scale , height: frame.height * UIScreen.main.scale)
        self.config()
    }
    
    override class var layerClass: AnyClass{
        return CAMetalLayer.classForCoder()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func config(){
        let vertexData = [Vertex.init(position: simd_float4.init(-1, -0.5, 0.0, 1), color: simd_float4.init(1, 0, 0.0, 1.0)),Vertex.init(position: simd_float4.init(1, -0.5, 0.0, 1.0), color: simd_float4.init(0, 1, 0, 1)),Vertex.init(position: simd_float4.init(0.0,  0.5, 0.0, 1.0), color: simd_float4.init(0, 0, 1, 1))]
        vertexBuffer = device!.makeBuffer(bytes: vertexData, length: vertexData.count * MemoryLayout<Vertex>.size, options: [])!
        
        let library = device?.makeDefaultLibrary()
        let renderPipeDescriptor = MTLRenderPipelineDescriptor.init()
        renderPipeDescriptor.fragmentFunction = library?.makeFunction(name: "basic_fragment")
        renderPipeDescriptor.vertexFunction = library?.makeFunction(name: "basic_vertex")
        renderPipeDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
        do{
            self.pipeLineState = try device!.makeRenderPipelineState(descriptor: renderPipeDescriptor)
            self.commandQueue = self.device!.makeCommandQueue()
            let display = CADisplayLink.init(target: self, selector: #selector(render))
            display.add(to: .current, forMode: .common)
        }catch{
            
        }
        
    }
    
    @objc func render(){
        let renderpassdescriptor = MTLRenderPassDescriptor.init()
        renderpassdescriptor.colorAttachments[0].clearColor = MTLClearColorMake(0, 0, 0, 1.0)
        let layer = self.layer as! CAMetalLayer
        let drawable = layer.nextDrawable()
        renderpassdescriptor.colorAttachments[0].texture = drawable?.texture
        renderpassdescriptor.colorAttachments[0].loadAction = .clear
        let commandbuffer = self.commandQueue.makeCommandBuffer()
        let renderencoder = commandbuffer?.makeRenderCommandEncoder(descriptor: renderpassdescriptor)
        renderencoder?.setVertexBuffer(self.vertexBuffer, offset: 0, index: 0)
        renderencoder?.setRenderPipelineState(self.pipeLineState)
        renderencoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
        renderencoder?.endEncoding()
        commandbuffer?.present(drawable!)
        commandbuffer?.commit()
    }

}

着色器代碼

#include <metal_stdlib>
using namespace metal;
struct Vertex{
    float4 position [[position]];
    float4 color;
};

vertex Vertex basic_vertex(constant Vertex *vertices [[buffer(0)]],unsigned int vid[[vertex_id]]){
    return vertices[vid];
}

fragment float4 basic_fragment(Vertex vert [[stage_in]]){
    return vert.color;
}

這裏有個地方需要注意,就是CAMetalLayer的drawSize一定要設置成view的寬度乘以屏幕的倍數,即scale,不然繪製出的圖形會很不清晰且直線鋸齒現象嚴重,也可以試試乘以其他更大的數值,你會發現乘以一個更大的數值並不會讓鋸齒現象比乘以scale好,而且如果乘以的數值很大,比如20,圖形會重新出現嚴重的鋸齒。着色器代碼可以以純字符串的形式寫在MetalView文件中,但最好是寫在一個.metal文件裏,編譯時系統會自動編譯.metal文件

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章