美文网首页Metal
Metal学习笔记 (一) 绘制三角形

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

作者: 我马上就变成胖子了 | 来源:发表于2020-06-02 10:53 被阅读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三角形

    • p1 获取系统默认的GPU,在iOS设备上只有一个
            device = MTLCreateSystemDefaultDevice()
    
    • p2 创建用来显示的Layer
            metalLayer = CAMetalLayer()
            metalLayer.device = device
            metalLayer.pixelFormat = .bgra8Unorm
            metalLayer.frame = view.layer.frame
    
    • p3 顶点缓冲对象
      Metal坐标系中,屏幕范围是[-1, 1],则屏幕的中心点为坐标原点(0, 0),因为我们绘制的图形是2D三角形,所以不考虑z
      则三角形顶点坐标
            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))
    
    • p4 顶点着色器 Vertex
      在处理完顶点数据后,需要对顶点进行着色处理,这里需要使用到Metal的着色语言MSL (Metal shading language),网上教程很多,我这里就不详细介绍

    创建一个.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)

    • p5 片段着色器 Fragment
    fragment float4 fragment_shader()
    {
        return float4(0.1f, 0.3f, 0.6f, 1.0f);
    }
    

    这里返回的是颜色RGBA

    • p6 渲染管线
      首先我们需要调用.metal文件中的着色器方法
            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)
    
    • p7 MTLCommandQueue
            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),不报错的话应该可以看到屏幕左侧的三角形了

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

    相关文章

      网友评论

        本文标题:Metal学习笔记 (一) 绘制三角形

        本文链接:https://www.haomeiwen.com/subject/mpuyzhtx.html