• swift使用metal加载三角形、平面图片、立体图像


    Metal 是一个和 OpenGL ES 类似的面向底层的图形编程接口,通过使用相关的 api 可以直接操作 GPU ,最早在 2014 年的 WWDC 的时候发布,并于今年发布了 Metal 2。

     Metal 是 iOS 平台独有的,意味着它不能像 OpenGL ES 那样支持跨平台,但是它能最大的挖掘苹果移动设备的 GPU 能力,进行复杂的运算,像 Unity 等游戏引擎都通过 Metal 对 3D 能力进行了优化, App Store 还有相应的运用 Metal 技术的游戏专题。

     Metal 具有特点

     GPU 支持的 3D 渲染

     和 CPU 并行处理数据 (深度学习)

     提供低功耗接口

     可以和 CPU 共享资源内存

     网上好多介绍Metal加载图像的文章都是用的OC语言,本篇用swift,以供参考。

    Metal基本流程:

    • 配置 Device 和 Queue
    • 获取 CommandBuffer
    • 配置 CommandBufferEncoder
    • 配置 PipelineState
    • 创建资源
    • Encoder Buffer 【如有需要的话可以用 Threadgroups 来分组 Encoder 数据】
    • 提交到 Queue 中

    一。Metal简单加载一个三角形

    //创建顶点结构体
    struct VertexColor {
        var vex:vector_float2
        var color:vector_float4
    }
    
    class DzqRender: NSObject {
        var view : MTKView
        var commandQueue : MTLCommandQueue
        var device :MTLDevice
        var pipelineState:MTLRenderPipelineState?
        var viewSize :CGSize = CGSize.zero
        init(view:MTKView) {
            self.view = view
            //1.拿到 GPU 对象,Metal 中提供了 MTLDevice 的接口,代表了 GPU。
            self.device = MTLCreateSystemDefaultDevice()!
            self.commandQueue = device.makeCommandQueue()!
            super.init()
            view.preferredFramesPerSecond = 60
            viewSize = view.drawableSize
            var library = device.makeDefaultLibrary()
    //
            if let url = Bundle.main.url(forResource: "", withExtension: ""){
                library = try? device.makeLibrary(URL: url)
            }
            
            let vfunc:MTLFunction? = library?.makeFunction(name: "vertexShader")
            let fFunc:MTLFunction? = library?.makeFunction(name: "fragmentShader")
            
            let description = MTLRenderPipelineDescriptor()
            description.vertexFunction = vfunc
            description.fragmentFunction = fFunc
            description.colorAttachments[0].pixelFormat = view.colorPixelFormat
            
            do {
                pipelineState = try device.makeRenderPipelineState(descriptor: description)
            } catch let error {
                print(error.localizedDescription)
            }
            
            
        }
        //1. 增加颜色/减小颜色的 标记
         var growing = true;
        //2.颜色通道值(0~3)
         var primaryChannel = 0;
        //3.颜色通道数组colorChannels(颜色值)
         var colorChannels = [1.0, 0.0, 0.0, 1.0]
        //设置颜色
        //4.颜色调整步长
        let  DynamicColorRate = 0.015;
        func makeFancyColor() -> Color
        {
            //5.判断
            if(growing)
            {
                //动态信道索引 (1,2,3,0)通道间切换
                let  dynamicChannelIndex = (primaryChannel+1)%3;
                
                //修改对应通道的颜色值 调整0.015
                colorChannels[dynamicChannelIndex] += DynamicColorRate;
                
                //当颜色通道对应的颜色值 = 1.0
                if(colorChannels[dynamicChannelIndex] >= 1.0)
                {
                    //设置为NO
                    growing = false;
                    
                    //将颜色通道修改为动态颜色通道
                    primaryChannel = dynamicChannelIndex;
                }
            }
            else
            {
                //获取动态颜色通道
                let  dynamicChannelIndex = (primaryChannel+2)%3;
                
                //将当前颜色的值 减去0.015
                colorChannels[dynamicChannelIndex] -= DynamicColorRate;
                
                //当颜色值小于等于0.0
                if(colorChannels[dynamicChannelIndex] <= 0.0)
                {
                    //又调整为颜色增加
                    growing = true;
                }
            }
            
            //创建颜色
            var color = Color()
            
            //修改颜色的RGBA的值
            color.red   = colorChannels[0]
            color.green = colorChannels[1]
            color.blue  = colorChannels[2]
            color.alpha = colorChannels[3]
            
            //返回颜色
            return color;
        }
    }
    extension DzqRender : MTKViewDelegate{
        // 当MTKView视图发生大小改变时调用
        func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
            viewSize = size
            
        }
        //每当视图需要渲染时调用
        func draw(in view: MTKView) {
    //        let color = makeFancyColor()
    //        view.clearColor = MTLClearColorMake(color.red, color.green, color.blue, color.alpha)
            //为当前渲染的每个渲染传递创建一个新的命令缓冲区
            let commandBuffer = commandQueue.makeCommandBuffer()
            commandBuffer?.label = "buffer"
            
            
            //5.判断renderPassDescriptor 渲染描述符是否创建成功,否则则跳过任何渲染.
            if let passDescriptor = view.currentRenderPassDescriptor, let state = pipelineState {
                let renderEncoder = commandBuffer?.makeRenderCommandEncoder(descriptor: passDescriptor)
                
                let viewpoint:MTLViewport = MTLViewport(originX: 0, originY: 0,  Double(viewSize.width), height: Double(viewSize.height), znear: -1, zfar: 1)
                renderEncoder?.setViewport(viewpoint)
                
                renderEncoder?.setRenderPipelineState(state)
                /*
                 在 Metal 中是归一化的坐标系,以屏幕中心为原点(0, 0, 0),且是始终不变的。面对屏幕,你的右边是x正轴,上面是y正轴,屏幕指向你的为z正轴。长度单位这样来定:窗口范围按此单位恰好是(-1,-1)到(1,1),即屏幕左下角坐标为(-1,-1),右上角坐标为(1,1)
    
                 */
    //            let vertex:[Float] = [
    //                -1.0, 0.0, 1, 0, 0, 1,
    //                2.0,  1.0, 0, 1, 0, 1,
    //                1.0, 0.5,  0, 0, 1, 1
    //            ]
                
                let vertex :[VertexColor] = [
                    VertexColor(vex: vector_float2(-1, 0), color: vector_float4(1, 0, 0, 1)),
                    VertexColor(vex: vector_float2(1, 0), color: vector_float4(0, 1, 0, 1)),
                    VertexColor(vex: vector_float2(0, 0.5), color: vector_float4(0, 0, 1, 1)),
                ]
                renderEncoder?.setVertexBytes(vertex, length: MemoryLayout<VertexColor>.size * 3, index: 0)
                
                
                //renderEncoder?.setVertexBytes(&Vpoint, length: MemoryLayout<Float>.size * 2, index: 1)
                
                renderEncoder?.drawPrimitives(type: .triangle, vertexStart: 0, vertexCount: 3)
                
                //7.我们可以使用MTLRenderCommandEncoder 来绘制对象,但是这个demo我们仅仅创建编码器就可以了,我们并没有让Metal去执行我们绘制的东西,这个时候表示我们的任务已经完成.
                //即可结束MTLRenderCommandEncoder 工作
                renderEncoder?.endEncoding()
                /*
                 当编码器结束之后,命令缓存区就会接受到2个命令.
                 1) present
                 2) commit
                 因为GPU是不会直接绘制到屏幕上,因此你不给出去指令.是不会有任何内容渲染到屏幕上.
                */
                //8.添加一个最后的命令来显示清除的可绘制的屏幕
                commandBuffer?.present(view.currentDrawable!)
            }
            //9.在这里完成渲染并将命令缓冲区提交给GPU
            commandBuffer?.commit()
            
        }
        
        
    }

    着色器代码:

    资源有了,我们要告诉 GPU 怎么去使用这些数据,这里就需要 Shader 了,这部分代码是在 GPU 中执行的,所以要用特殊的语言去编写,即 Metal Shading Language,它是 C++ 14的超集,封装了一些 Metal 的数据格式和常用方

    #include <metal_stdlib>

    //#include "DQRenderType.swift"
    using namespace metal;
    
    typedef struct
    {
        float2 position;
        float4 color;
    } VertexIn;
    
    typedef struct {
        
        float4 clipSpacePosition [[position]];
        
        float4 color;
        
    } RasterizerData;
    
    
    vertex RasterizerData vertexShader(uint vertexid [[vertex_id]],constant VertexIn *vertexColor [[buffer(0)]]){
        RasterizerData out;
        out.clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0);
        
        out.clipSpacePosition.xy = vertexColor[vertexid].position;
    //    out.clipSpacePosition.y = vertexColor[vertexid + 1];
        out.color = vertexColor[vertexid].color;
    //    out.color.r = vertexColor[vertexid + 2];
    //    out.color.g = vertexColor[vertexid + 3];
    //    out.color.b = vertexColor[vertexid + 4];
    //    out.color.a = vertexColor[vertexid + 5];
        
        return out;
    }
    
    fragment float4 fragmentShader(RasterizerData in [[stage_in]]){
        return in.color;
    }

    二、Metal加载平面图像

    class ImageRender: NSObject {
        var view : MTKView
        var commandQueue : MTLCommandQueue?
        var device :MTLDevice
        var pipelineState:MTLRenderPipelineState?
        var viewSize :CGSize = CGSize.zero
        
        var vertexBuffer :MTLBuffer?
        var vertexIndex:MTLBuffer?
        var texture:MTLTexture?
        var loadtga:Bool = false
        init(view:MTKView,loadTga:Bool = false) {
            self.view = view
            self.device = view.device!
            self.commandQueue = device.makeCommandQueue()!
            super.init()
            view.preferredFramesPerSecond = 60
            viewSize = view.drawableSize
            
            setUpPipeLineState()
           
            setUpVertex()
            
            setUpImageTexture(loadTga:loadTga)
            
        }
    
        func setUpPipeLineState() {
            var library = try? device.makeDefaultLibrary()
            if let url = Bundle.main.url(forResource: "TextureRender", withExtension: "metal"){
                library = try? device.makeLibrary(URL: url)
            }
            
            let vfunc:MTLFunction? = library?.makeFunction(name: "vertexShader1")
            let fFunc:MTLFunction? = library?.makeFunction(name: "fragmentShader1")
            
            let description = MTLRenderPipelineDescriptor()
            description.vertexFunction = vfunc
            description.fragmentFunction = fFunc
            description.colorAttachments[0].pixelFormat = view.colorPixelFormat
            
            do {
                pipelineState = try device.makeRenderPipelineState(descriptor: description)
            } catch let error {
                print(error.localizedDescription)
                
            }
            
            commandQueue = device.makeCommandQueue()
        }
        
        func setUpVertex() {
            let vertexTex:[VertexTexture] = [
                VertexTexture(vex: vector_float2(1, -1), tex: vector_float2(1, 0)),
                VertexTexture(vex: vector_float2(-1, -1), tex: vector_float2(0, 0)),
                VertexTexture(vex: vector_float2(-1, 1), tex: vector_float2(0, 1)),
                
    //            VertexTexture(vex: vector_float2(1, -1), tex: vector_float2(1, 0)),
    //            VertexTexture(vex: vector_float2(-1, 1), tex: vector_float2(0, 1)),
                VertexTexture(vex: vector_float2(1, 1), tex: vector_float2(1, 1)),
            ]
            vertexBuffer = device.makeBuffer(bytes: vertexTex, length: MemoryLayout<VertexTexture>.size * vertexTex.count, options: MTLResourceOptions.storageModeShared)
            
            func scaleShowImage(){
                var vertexs:[Float] = [
                    1,-1,  1,0,
                    -1,-1, 0,0,
                    -1,1,  0,1,
                    1,1,   1,1
                ]
                var imageScale:(CGFloat,CGFloat) = (1,1)
                if let image = UIImage(named: imageName)?.cgImage {
                    let width = image.width
                    let height = image.height
                           
                    let scaleF = CGFloat(view.frame.height)/CGFloat(view.frame.width)
                    let scaleI = CGFloat(height)/CGFloat(width)
                           
                    imageScale = scaleF>scaleI ? (1,scaleI/scaleF) : (scaleI/scaleF,1)
                }
                for (i,v) in vertexs.enumerated(){
                    if i % 4 == 0 {
                        vertexs[i] = v * Float(imageScale.0)
                    }
                    if i % 4 == 1{
                        vertexs[i] = v * Float(imageScale.1)
                    }
    
                }
                vertexBuffer = device.makeBuffer(bytes: vertexs, length: MemoryLayout<Float>.size * vertexs.count, options: MTLResourceOptions.storageModeShared)
            }
            //按图片比例显示
            scaleShowImage()
            
            //索引绘图
            let index:[Int32] = [
                0,1,2,
                0,2,3
            ]
            vertexIndex = device.makeBuffer(bytes: index, length: MemoryLayout<Int32>.size * 6, options: .storageModeShared)
            
            
        }
        func setUpImageTexture(loadTga:Bool = false) {
            var imageSoruce = UIImage(named: imageName)
            if loadTga {
                let url = Bundle.main.url(forResource: "Image", withExtension: "tga")
                imageSoruce = self.tgaTOImage(url: url!)
            }
            
            guard let image = imageSoruce?.cgImage else {
                return
            }
            
            let width = image.width
            let height = image.height
            
            //开辟内存,绘制到这个内存上去
            let spriteData: UnsafeMutablePointer = UnsafeMutablePointer<GLubyte>.allocate(capacity: MemoryLayout<GLubyte>.size * width * height * 4)
            UIGraphicsBeginImageContext(CGSize( width, height: height))
            //获取context
            let spriteContext = CGContext(data: spriteData,  width, height: height, bitsPerComponent: 8, bytesPerRow: width * 4, space: image.colorSpace!, bitmapInfo: image.bitmapInfo.rawValue)
            spriteContext?.translateBy(x:0 , y: CGFloat(height))
            spriteContext?.scaleBy(x: 1, y: -1)
            spriteContext?.draw(image, in: CGRect(x: 0, y: 0,  width, height: height))
            
            UIGraphicsEndImageContext()
            
    //        spriteData
            
            let textureDescriptor = MTLTextureDescriptor()
            textureDescriptor.pixelFormat = .rgba8Unorm //MTLPixelFormatRGBA8Unorm defoat
            textureDescriptor.width = image.width
            textureDescriptor.height = image.height
            texture = device.makeTexture(descriptor: textureDescriptor)
            
            texture?.replace(region: MTLRegion(origin: MTLOrigin(x: 0, y: 0, z: 0), size: MTLSize( image.width, height: image.height, depth: 1)), mipmapLevel: 0, withBytes: spriteData, bytesPerRow: 4 * image.width)
            
            free(spriteData)
        }
        
        
        func tgaTOImage(url:URL) -> UIImage? {
            if url.pathExtension.caseInsensitiveCompare("tga") != .orderedSame {
                return nil
            }
            guard let fileData = try? Data.init(contentsOf: url) else {
                print("打开tga文件失败!")
                return nil
            }
            let image = UIImage(data: fileData)
            return image
        }
        
    }
    extension ImageRender:MTKViewDelegate{
        func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
            
        }
        
        func draw(in view: MTKView) {
            
            guard let queue = commandQueue,
                let buffer = queue.makeCommandBuffer(),
                let renderPassDiscriptor = view.currentRenderPassDescriptor,
                let encoder = buffer.makeRenderCommandEncoder(descriptor: renderPassDiscriptor),
                let pipeState = pipelineState
                
                else {
                    return
            }
            
            
            encoder.label = "renderEncoder"
            encoder.setRenderPipelineState(pipeState)
            
            encoder.setViewport(MTLViewport(originX: 0, originY: 0,  Double(viewSize.width), height: Double(viewSize.height), znear: -1, zfar: 1))
            
            encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
            
            encoder.setFragmentTexture(texture, index: 0)
            
    //        encoder.drawPrimitives(type: MTLPrimitiveType.triangleStrip, vertexStart: 0, vertexCount: 6)//不实用索引绘图绘制
            encoder.drawIndexedPrimitives(type: .triangleStrip, indexCount: 6, indexType: .uint32, indexBuffer: vertexIndex!, indexBufferOffset: 0)//使用索引绘图绘制
            
            encoder.endEncoding()
            
            buffer.present(view.currentDrawable!)
            
            buffer.commit()
            
        }
        
        
    }

    shader代码:

    #include <metal_stdlib>
    //#include "DQRenderType.swift"
    using namespace metal;
    
    typedef struct
    {
        float2 position;
        float2 color;
    } VertexIn;
    
    typedef struct {
        
        float4 clipSpacePosition [[position]];
        
        float2 color;
        
    } RasterizerData;
    
    
    vertex RasterizerData vertexShader1(uint vertexid [[vertex_id]],constant VertexIn *vertexColor [[buffer(0)]]){
        RasterizerData out;
        out.clipSpacePosition = vector_float4(0.0, 0.0, 0.0, 1.0);
        
        out.clipSpacePosition.xy = vertexColor[vertexid].position;
    
        out.color = vertexColor[vertexid].color;
    
        
        return out;
    }
    
    fragment float4 fragmentShader1(RasterizerData in [[stage_in]],texture2d<float> texture [[texture(0)]]){
        
        constexpr sampler textureSampler234(mag_filter::linear,min_filter::linear);
        float4 color = texture.sample(textureSampler234, in.color);
        return color;
    }

    三、Metal加载立体图像

    class CubeRender: NSObject {
        var view : MTKView
        var commandQueue : MTLCommandQueue?
        var device :MTLDevice
        var pipelineState:MTLRenderPipelineState?
        var viewSize :CGSize = CGSize.zero
        
        var vertexBuffer :MTLBuffer?
        var vertexIndex:MTLBuffer?
        var texture:MTLTexture?
        var indexCount :Int = 0
        var button : UIButton!
        var switchX,switchY,switchZ :UISwitch
        
        init(view:MTKView) {
            self.view = view
            self.device = view.device!
            self.commandQueue = device.makeCommandQueue()!
            switchX = UISwitch(frame: CGRect(x: 20 , y: view.frame.size.height - 100,  100, height: 60))
            
            switchY = UISwitch(frame: CGRect(x: 10 , y: view.frame.size.height - 100,  100, height: 60))
            switchY.center.x = view.center.x
            switchZ = UISwitch(frame: CGRect(x: view.frame.size.width - 110 , y: view.frame.size.height - 100,  100, height: 60))
            view.addSubview(switchX)
            view.addSubview(switchY)
            view.addSubview(switchZ)
            super.init()
            view.preferredFramesPerSecond = 60
            viewSize = view.drawableSize
            
            switchX.backgroundColor = .gray
            switchY.backgroundColor = .gray
            switchZ.backgroundColor = .gray
            
            button = UIButton(frame: CGRect(x: 0, y: view.frame.size.height - 160,  100, height: 50))
            button.setTitle("旋转", for: UIControl.State.normal)
            button.center.x = view.center.x
            button.backgroundColor = .gray
            button.addTarget(self, action: #selector(rotate(btn:)), for: .touchUpInside)
            view.addSubview(button)
            
            setUpPipeLineState()
            
            setUpVertex()
            
            setUpImageTexture()
            
        }
        @objc func rotate(btn:UIButton){
            btn.isSelected = !btn.isSelected
            if btn.isSelected {
                btn.setTitle("停止", for: .normal)
            }else {
                btn.setTitle("旋转", for: .normal)
            }
            
        }
        func setUpPipeLineState() {
            var library = try? device.makeDefaultLibrary()
            if let url = Bundle.main.url(forResource: "CubeRender", withExtension: "metal"){
                library = try? device.makeLibrary(URL: url)
            }
            
            let vfunc:MTLFunction? = library?.makeFunction(name: "cubeVertexShader")
            let fFunc:MTLFunction? = library?.makeFunction(name: "cubeFragmentShader")
            
            let description = MTLRenderPipelineDescriptor()
            description.vertexFunction = vfunc
            description.fragmentFunction = fFunc
            description.colorAttachments[0].pixelFormat = view.colorPixelFormat
            
            do {
                pipelineState = try device.makeRenderPipelineState(descriptor: description)
            } catch let error {
                print(error.localizedDescription)
                
            }
            
            commandQueue = device.makeCommandQueue()
        }
        func setUpVertex() {
            
            let vertexs1:[Float] = [
              
                -0.5, 0.5, 0, 1.0,
                 0.5, 0.5, 0, 1.0,
                -0.5,-0.5, 0, 1.0,
                 0.5,-0.5, 0, 1.0,
                 0.0, 0.0, 0.5, 1
                
            ]
            let vertexs3:[Float] = [
    
               -0.5, 0.5, 0, 1.0,0, 1,
                0.5, 0.5, 0, 1.0,1, 1,
               -0.5,-0.5, 0, 1.0,0, 0,
                0.5,-0.5, 0, 1.0,1, 0,
                0.0, 0.0, 1, 1,0.5,0.5
    
            ]
            
            let  vertexs2 : [CubeVertexTexture] = [
                CubeVertexTexture(vex: vector_float4(-0.5, 0.5, 0, 1.0), tex: vector_float4(0, 1, 0, 0), color: vector_float4(1, 0,0,1)),//左上
                CubeVertexTexture(vex: vector_float4( 0.5, 0.5, 0, 1.0), tex: vector_float4(1, 1, 0, 0), color: vector_float4(0, 1,0,1)),//右上
                CubeVertexTexture(vex: vector_float4(-0.5,-0.5, 0, 1.0), tex: vector_float4(0, 0, 0, 0), color: vector_float4(0, 0,1,1)),//左下
                CubeVertexTexture(vex: vector_float4( 0.5,-0.5, 0, 1.0), tex: vector_float4(1, 0, 0, 0), color: vector_float4(0, 1,1,1)),//右下
                CubeVertexTexture(vex: vector_float4( 0.0, 0.0, 0.5, 1.0), tex: vector_float4(0.5,0.5,0,0), color: vector_float4(1, 1,0,1))
            ]
            //坑:顶点坐标值的个数必须是4的倍数,vertexs1可以,vertexs3不行,vertexs2可以
            vertexBuffer = device.makeBuffer(bytes: vertexs2, length: MemoryLayout<CubeVertexTexture>.size * (vertexs2.count), options: .storageModeShared)
            
            //索引
            let index:[uint] = [
                0, 3, 2,
                0, 1, 3,
                0, 2, 4,
                0, 4, 1,
                2, 3, 4,
                1, 4, 3
            ]
            self.indexCount = index.count
            vertexIndex = device.makeBuffer(bytes: index, length: MemoryLayout<uint>.size * index.count, options: .storageModeShared)
            
            
        }
        func setUpImageTexture() {
            
            guard let image = UIImage(named: imageName)?.cgImage else {
                return
            }
            
            let width = image.width
            let height = image.height
            
            //开辟内存,绘制到这个内存上去
            let spriteData: UnsafeMutablePointer = UnsafeMutablePointer<GLubyte>.allocate(capacity: MemoryLayout<GLubyte>.size * width * height * 4)
            UIGraphicsBeginImageContext(CGSize( width, height: height))
            //获取context
            let spriteContext = CGContext(data: spriteData,  width, height: height, bitsPerComponent: 8, bytesPerRow: width * 4, space: image.colorSpace!, bitmapInfo: image.bitmapInfo.rawValue)
            spriteContext?.translateBy(x:0 , y: CGFloat(height))
            spriteContext?.scaleBy(x: 1, y: -1)
            spriteContext?.draw(image, in: CGRect(x: 0, y: 0,  width, height: height))
            
            UIGraphicsEndImageContext()
            
            //        spriteData
            
            let textureDescriptor = MTLTextureDescriptor()
            textureDescriptor.pixelFormat = .rgba8Unorm //MTLPixelFormatRGBA8Unorm defoat
            textureDescriptor.width = image.width
            textureDescriptor.height = image.height
            texture = device.makeTexture(descriptor: textureDescriptor)
            
            texture?.replace(region: MTLRegion(origin: MTLOrigin(x: 0, y: 0, z: 0), size: MTLSize( image.width, height: image.height, depth: 1)), mipmapLevel: 0, withBytes: spriteData, bytesPerRow: 4 * image.width)
            
            free(spriteData)
        }
        
        var x:Float = 0.0
        var y:Float = 0.0
        var z:Float = 0.0
        
        
        //设置矩阵变换
        func setMatrix(encode:MTLRenderCommandEncoder) {
            let size = self.view.bounds.size
            let perspectM = GLKMatrix4MakePerspective(Float.pi/2, Float(size.width/size.height), 0.1, 50.0)
            var modelViewM = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -2)
            if button.isSelected {
                if switchX.isOn {
                    x += 1/180 * Float.pi
                }
                if switchY.isOn {
                    y += 1/180 * Float.pi
                }
                
                if switchZ.isOn {
                    z += 1/180 * Float.pi
                }
            }
            
            modelViewM = GLKMatrix4RotateX(modelViewM, x)
            modelViewM = GLKMatrix4RotateY(modelViewM, y)
            modelViewM = GLKMatrix4RotateZ(modelViewM, z)
            
            var matrix = DqMatrix(pMatix: perspectM.toMatrix_float4x4(), mvMatrix: modelViewM.toMatrix_float4x4())
            
            encode.setVertexBytes(&matrix, length: MemoryLayout<DqMatrix>.size, index: 1)
            
        }
        
    }
    struct DqMatrix {
        var pMatix : matrix_float4x4
        var mvMatrix :matrix_float4x4
        
    }
    extension GLKMatrix4{
        func toMatrix_float4x4() -> matrix_float4x4{
            let matrix = self
            return matrix_float4x4(
                simd_make_float4(matrix.m00, matrix.m01, matrix.m02, matrix.m03),
                simd_make_float4(matrix.m10, matrix.m11, matrix.m12, matrix.m13),
                simd_make_float4(matrix.m20, matrix.m21, matrix.m22, matrix.m23),
                simd_make_float4(matrix.m30, matrix.m31, matrix.m32, matrix.m33)
            )
        }
    }
    extension CubeRender :MTKViewDelegate{
        func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
            
        }
        
        func draw(in view: MTKView) {
            
            guard let queue = commandQueue,
                let buffer = queue.makeCommandBuffer(),
                let renderPassDiscriptor = view.currentRenderPassDescriptor,
    
                let pipeState = pipelineState
                
                else {
                    return
            }
            renderPassDiscriptor.colorAttachments[0].loadAction = .clear
            renderPassDiscriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.6, green: 0.2, blue: 0.5, alpha: 1)
            guard
                let encoder = buffer.makeRenderCommandEncoder(descriptor: renderPassDiscriptor)
                else {
                    return
            }
            
            
            encoder.label = "renderEncoder"
            encoder.setRenderPipelineState(pipeState)
            
            encoder.setViewport(MTLViewport(originX: 0, originY: 0,  Double(viewSize.width), height: Double(viewSize.height), znear: -1, zfar: 1))
            
            encoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0)
           
    //        encoder.setVertexBytes(vertexs1, length: MemoryLayout<Float>.size * vertexs1.count, index: 0)
            self.setMatrix(encode: encoder)
            encoder.setFragmentTexture(texture, index: 0)
    
            encoder.setFrontFacing(MTLWinding.counterClockwise)
            encoder.setCullMode(MTLCullMode.back)
            
    //        encoder.drawPrimitives(type: MTLPrimitiveType.triangleStrip, vertexStart: 0, vertexCount: 5)
            
            encoder.drawIndexedPrimitives(type: .triangle, indexCount: self.indexCount, indexType: .uint32, indexBuffer: vertexIndex!, indexBufferOffset: 0)//使用索引绘图绘制
            
            encoder.endEncoding()
            
            buffer.present(view.currentDrawable!)
            
            buffer.commit()
            
        }
    }

    shader代码:

    #include <metal_stdlib>
    using namespace metal;
    
    typedef struct{
        float4 position;
        float4 textureCoord;
        float4 color;
    }VertexIn;
    typedef struct {
        
        float4 clipSpacePosition [[position]];
        float2 textureCoord;
        float4 color;
        
    }VertexOut;
    
    typedef struct {
        float4x4 persMatrix;
        float4x4 mvMatrix;
        
    }MvpMatrix;
    
    vertex VertexOut cubeVertexShader(uint vertexIndex [[vertex_id]], constant VertexIn *ver [[buffer(0)]],constant MvpMatrix *matrixs [[buffer(1)]]){
        
        VertexOut out;
        out.textureCoord = ver[vertexIndex].textureCoord.xy;
        out.clipSpacePosition = ver[vertexIndex].position;
    
        out.clipSpacePosition =  matrixs->persMatrix * matrixs->mvMatrix * out.clipSpacePosition;
        
        out.color = ver[vertexIndex].color;
        
        return out;
    }
    
    fragment float4 cubeFragmentShader(VertexOut in [[stage_in]],texture2d<float> texture [[texture(0)]]){
        
        
        constexpr sampler cubeSampler(mag_filter::linear,min_filter::linear);
        return  texture.sample(cubeSampler, in.textureCoord);
    //    return float4(0,1,0,1);
    //    return in.color;
        
    }

    Metal 提供以下特性:

    • 低开销接口。Metal 被设计用于消灭像状态检查一类的隐性性能瓶颈,你可以控制 GPU 的异步行为,以实现用于并行创建和提交命令缓冲区的高效多线程操作
    • 内存和资源管理。Metal 框架提供了表示 GPU 内存分配的缓冲区和纹理对象,纹理对象具有确切的像素格式,能被用于纹理图像或附件
    • 集成对图形和计算操作的支持。Metal 对图形操作和计算操作使用了相同的数据结构和资源(如 buffer、texture、command queue),Metal 的着色器语言同时支持图形函数和计算函数,Metal 框架支持在运行时接口(CPU)、图形着色器和计算方法间共享资源
    • 预编译着色器。Metal 的着色器函数能与代码一同在编译器编译,并在运行时加载,这样的流程能提供更方便的着色器调试功能。

    demo详细代码github地址:https://github.com/duzhaoquan/MetalRender

    参考文献:


  • 相关阅读:
    (8)闭包函数(函数的传参方式)
    (7)名称空间和作用域
    (6)函数嵌套
    (5)函数对象
    (4)进阶函数(作用域、闭包、生成器、迭代器)
    (3)什么是函数(函数的定义、形参、实参、默认形参、可变长函数args kwargs,私有地址)
    (1)三元运算、字符编码
    (2)字符编码关系和转换(bytes类型)
    java技术学习网址收藏
    springmvc工作原理和环境搭建
  • 原文地址:https://www.cnblogs.com/duzhaoquan/p/13305900.html
Copyright © 2020-2023  润新知