Metal

Metal学习笔记 (一) 绘制三角形

2020-06-02  本文已影响0人  我马上就变成胖子了

前言

此系列文章仅在记录和分享本人的Metal学习过程,如有错误欢迎指正。

本章代码

iOS 13.5
Xcode 11.5
swift 5

正文

Metal的真正目的是在移动设备上实现前所未有的画面逼真度,例如Epic Games Zen Garden,iPad上以1440×1080分辨率、4xMSAA抗锯齿运行,可以同时展示3500个活动的蝴蝶,平均每帧有4000个绘制调用,这可是OpenGL ES做不到的;目前仅支持Imagination PowerVR Rogue架构,也就是A7、A8、A8X,或者说iPhone 5S、iPad Air及其后的设备

在阅读本系列教程之前,已经默认你有基本的iOSOS X开发经验,但如果你没有计算机图形学的相关知识则可能比较难以理解,建议学习GAMES101或其他图形学相关知识的教程

本节目标:了解基本的Metal API并绘制一个简单的2D三角形

        device = MTLCreateSystemDefaultDevice()
        metalLayer = CAMetalLayer()
        metalLayer.device = device
        metalLayer.pixelFormat = .bgra8Unorm
        metalLayer.frame = view.layer.frame
        let vertexData: [Float] = [-0.5, 0.5,  0.0,
                                   0.0,  -0.5, 0.0,
                                   -1.0, -0.5, 0.0]

由顶点坐标可以看出,三角形是处于屏幕左侧位置,宽度和高度均为屏幕的一半
接下来将 CPU创建的顶点缓冲到GPU中

        vertexBuffer = device?.makeBuffer(bytes: vertexData,
                                          length: vertexData.count * MemoryLayout<Float>.size,
                                          options: MTLResourceOptions(rawValue: 0))

创建一个.metal文件编写MSL代码

Xcode -> File -> New -> File -> Source -> Metal File

编写顶点着色器代码

vertex float4 vertex_shader(constant packed_float3 *vertex_array[[buffer(0)]],
                           unsigned int vid[[vertex_id]])
{
    return float4(vertex_array[vid], 1.0f);
}

这里返回的是(x, y, z, w)齐次坐标, 因为w = 1.0f,所以转化为欧式空间的坐标系中等价于 (x, y, z)

fragment float4 fragment_shader()
{
    return float4(0.1f, 0.3f, 0.6f, 1.0f);
}

这里返回的是颜色RGBA

        let defaultLibrary = device?.makeDefaultLibrary()
        let fragmentFunc = defaultLibrary?.makeFunction(name: "fragment_shader")
        let vertexFunc = defaultLibrary?.makeFunction(name: "vertex_shader")

在渲染管线中设置这些方法以及像素格式等...

        let pipelineStateDescriptor = MTLRenderPipelineDescriptor()
        pipelineStateDescriptor.fragmentFunction = fragmentFunc
        pipelineStateDescriptor.vertexFunction = vertexFunc
        pipelineStateDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm
        pipelineState = try? device?.makeRenderPipelineState(descriptor: pipelineStateDescriptor)
        commandQueue = device?.makeCommandQueue()

绘制

        let drawable = metalLayer.nextDrawable()
        let rpd = MTLRenderPassDescriptor()
        rpd.colorAttachments[0].texture = drawable?.texture
        rpd.colorAttachments[0].loadAction = .clear
        // 背景颜色这里写死
        rpd.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.8, 0.5, 1.0)
        commandBuffer = commandQueue?.makeCommandBuffer()
        
        // 创建一个渲染命令编码器(Render Command Encoder)
        // 创建一个command encoder,并指定你之前创建的pipeline和顶点
        let re: MTLRenderCommandEncoder? = commandBuffer?.makeRenderCommandEncoder(descriptor: rpd)
        re?.setRenderPipelineState(pipelineState!)
        re?.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
        re?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3, instanceCount: 1)
        re?.endEncoding()
        commandBuffer?.present(drawable!)
        commandBuffer?.commit()

运行代码(必须真机运行,模拟器没有GPU),不报错的话应该可以看到屏幕左侧的三角形了

思考: 如何在屏幕右侧再绘制出一个三角形?

上一篇 下一篇

猜你喜欢

热点阅读