美文网首页
Metal(三)- Swift案例:三角形绘制

Metal(三)- Swift案例:三角形绘制

作者: Henry________ | 来源:发表于2020-09-01 22:58 被阅读0次

    相比于上一篇helloWorld,这一篇内容增加了顶点数据和Metal的内容。

    效果图

    绘制流程:


    绘制流程

    具体代码实现

    1,Metal文件

    #import "HrShaderType.h"
    
    typedef struct {
        //处理空间的顶点信息
        //position是关键字,类似于GLSL中的gl_Position
        float4 clipSpacePosition [[position]];
    
        //颜色
        float4 color;
    } RasterizerData;
    
    vertex函数
    vertex RasterizerData vertexShader(uint vertexId [[vertex_id]],
                                       constant HRVertex *vertexs [[buffer(VertexInputIndexVertices)]],
                                       constant vector_float2 *viewportSize [[buffer(VertexInputIndexViewPortSize)]]) 
    {
        RasterizerData out;
        
        out.clipSpacePosition = vertexs[vertexId].position;
        //把我们输入的颜色直接赋值给输出颜色.通过这种方式将颜色数据桥接到片元着色器
        out.color = vertexs[vertexId].color;
        
        return out;
    }
    
    • vertex:函数限定符,限定该函数为顶点函数
    • RasterizerData:函数返回值,会将该参数经过光栅化后传递到片元函数
    • vertexShader:函数自定义名称
    • uint vertexId [[vertex_id]]:
      • uint变量类型:无符号32位整型;
      • vertexId变量名;
      • [[vertex_id]]属性修饰符:代表顶点编号固定写法,开发者不得修改
    • constant HRVertex *vertexs [[buffer(VertexInputIndexVertices)]]:
      • constant变量限定符:存储在GPU的常量缓存区中;
      • HRVertex:变量类型; vertexs:变量名;
    fragment函数
    fragment float4 fragmentShader(RasterizerData in [[stage_in]]) {
        //返回该像素点的色值
        return in.color;
    }
    
    • fragment函数限定符:片元函数
    • float4:返回值,代表颜色RGBA
    • fragmentShader:函数名
    • RasterizerData in [[stage_in]]:
      • RasterizerData变量类型;
      • in变量名;
      • [[stage_in]]属性修饰符:片元着色函数使用的单个片元输入数据是由顶点着色函数输出.然后经过光栅化生成的.

    2,桥接文件

    由于需要在Swift文件中使用OC头文件,需要通过桥接文件XXX-Bridging-Header来导入.h文件

    //定义了基本的向量、矩阵、四元数,该头文件同时存在于Metal Shader / swift | Objc中,方便相互传递数据
    #include <simd/simd.h>
    
    //该文件作用:通过文件引入的方式,将一些自定义的类型声明既传递到swift文件,同时也传递到metal文件中
    typedef struct {
        vector_float4 position;
        vector_float4 color;
    } HRVertex;
    
    typedef enum {
        //顶点数据
        VertexInputIndexVertices = 0,
        //视图大小
        VertexInputIndexViewPortSize = 1,
    }VertexInputIndex;
    

    3,自定义Render渲染类

    后续用到的相关全局参数
        private var _device : MTLDevice?
        private var commandQueue : MTLCommandQueue?
        private var pielineState : MTLRenderPipelineState!
        private var viewPortSize : vector_uint2 = vector_uint2(x: 0, y: 0)
    
    初始化
    init(view: MTKView) {
            super.init()
            _device = view.device
            
            //1. 通过device创建commandQueue
            commandQueue = _device?.makeCommandQueue()
            
            //2. 加载metal文件
            //2.1 makeDefaultLibrary:加载项目中所有.metal文件,当然也可以使用其他API来指定metal文件
            let library = _device?.makeDefaultLibrary()
            //2.2 从库中加载顶点函数、片元函数
            let vertexShader = library?.makeFunction(name: "vertexShader")
            let fragShader = library?.makeFunction(name: "fragmentShader")
            
            //3. 创建渲染管道描述符
            //3.1
            let pielineDes = MTLRenderPipelineDescriptor()
            //3.2 管道名称:可用于调试
            pielineDes.label = "MyMTLRenderPipelineDescriptor"
            //3.2 可编程函数,用于处理渲染过程中每个顶点、片元
            pielineDes.vertexFunction = vertexShader
            pielineDes.fragmentFunction = fragShader
            //3.3 确定渲染管线中颜色附着点0的颜色组件;使用当前view颜色组件
            pielineDes.colorAttachments[0].pixelFormat = view.colorPixelFormat
            
            //4 创建渲染管线状态
            do {
                try pielineState = _device?.makeRenderPipelineState(descriptor: pielineDes)
            } catch {
                //如果我们没有正确设置管道描述符,则管道状态创建可能失败
                print("pielineState failed \(error)")
            }  
        }
    
    Delegate-drawSize
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
            viewPortSize.x = uint(size.width)
            viewPortSize.y = uint(size.height)
        }
    
    Delegate-draw
    func draw(in view: MTKView) {
            //设置view的clearColor
            view.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 1.0)
            
            //5. 为每一次渲染创建一个新的命令缓冲区
            let commandBuffer = commandQueue?.makeCommandBuffer()
            commandBuffer?.label = "MyCommandBuffer"
            
            //6. 用于保存渲染过程中的一组结果,渲染命令编码器描述符
            if let des = view.currentRenderPassDescriptor {
                //7. 创建渲染命令编码器,通过它来进行渲染的配置
                let encoder = commandBuffer?.makeRenderCommandEncoder(descriptor: des)
                encoder?.label = "MyCommandEncoder"
                
                //8. 设置视口
                encoder?.setViewport(MTLViewport(originX: 0, originY: 0,
                                                 width: Double(viewPortSize.x),
                                                 height: Double(viewPortSize.y),
                                                 znear: -1.0, zfar: 1.0))
                //9. 设置当前渲染管道状态对象
                encoder?.setRenderPipelineState(pielineState)
                
                //10. 载入顶点数据
                //通过VertexInputIndexVertices将数据传递到顶点函数的对应buffer中
                encoder?.setVertexBytes(triangleVertices,
                                        length: triangleVertices.count * MemoryLayout<HRVertex>.size,
                                        index: Int(VertexInputIndexVertices.rawValue))
                
                encoder?.setVertexBytes(&viewPortSize,
                                        length: MemoryLayout<vector_uint2>.size,
                                        index: Int(VertexInputIndexViewPortSize.rawValue))
                
                
                
                //11. 绘制动作
                /*
                    type: 设置图元链接方式
                        case point = 0
                        case line = 1
                        case lineStrip = 2  //线环
                        case triangle = 3   //三角形
                        case triangleStrip = 4  //三角形扇
                 */
                encoder?.drawPrimitives(type: .triangle,
                                        vertexStart: 0,
                                        vertexCount: triangleVertices.count)
                
                //12. 结束编码
                encoder?.endEncoding()
                
                //13. 锁定缓存区, 等待缓冲区处理完成后绘制
                if let currentDrawable = view.currentDrawable{
                    commandBuffer?.present(currentDrawable)
                }
            }
            
            //14. 将命令缓存区提交给GPU
            commandBuffer?.commit()
        }
    
    Buffer方式导入顶点数据

    上方代码使用的是直接导入的方式将顶点数据导入顶点函数。当然还有其他方式,比如使用Buffer的方式来导入。

    init(view: MTKView) {
    ...
    // 将数据放入buffer中,但是buffer是有大小上限:4KB
            vertexBuffer = _device?.makeBuffer(bytes: triangleVertices,
                                             length: triangleVertices.count * MemoryLayout<HRVertex>.size,
                                             options: .storageModeShared)
    }
    
    func draw(in view: MTKView) {
    // 通过buffer的方式载入顶点数据
        encoder?.setVertexBuffer(vertexBuffer,
                                         offset: 0,
                                         index: Int(VertexInputIndexVertices.rawValue))
    }
    
    • 由于buffer最大可存储4KB的数据,所以不适合大量数据的导入

    4,渲染类的使用

    override func viewDidLoad() {
            super.viewDidLoad()
            
            //1.获取MTKView
            if let vv = self.view as? MTKView{
                _view = vv
                //2.创建设备(GPU)
                _view?.device = MTLCreateSystemDefaultDevice()
                
                //3.render工具类创建
                _render = HrRender(view: _view)
                
                //4.mtkview的代理设置
                _view?.delegate = _render
                
                //5.初始化视图大小
                //drawableSize当前view的可视区域
                _render?.mtkView(vv, drawableSizeWillChange: _view.drawableSize)
            }
        }
    

    demo-github地址

    相关文章

      网友评论

          本文标题:Metal(三)- Swift案例:三角形绘制

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