美文网首页Metal图形绘制
使用Metal绘制第一个三角形

使用Metal绘制第一个三角形

作者: lenka01 | 来源:发表于2021-10-06 10:54 被阅读0次

    在之前OpenGL系列基础之上,我们又对iOS原生Metal做了一些探索,这里主要是记录一下学习的过程。

    1.MTKView

    在学习metal之前我们需要了解一下什么是MTKView,它是苹果基于自家的Metal渲染架构上构建的一个渲染视图载体,类似于前面提到过的GLKView。它能够为我们提供原OpenGL ES的类似效果。下面介绍下MTKView的初始化代码:

    - (void)createMTKView {
        // 创建GPU设备
        id<MTLDevice> device = MTLCreateSystemDefaultDevice();
        _device = device;
        // 创建MTKView
        self.mtkView = [[MTKView alloc] initWithFrame:self.view.bounds device:device];
        self.mtkView.delegate = self;
        self.mtkView.backgroundColor = UIColor.clearColor;
        [self.view addSubview:self.mtkView];
    
        // 记录mtkView视口的大小
        self.viewportSize = (vector_uint2){self.mtkView.drawableSize.width, self.mtkView.drawableSize.height};
    
        // 创建命令队列
        id<MTLCommandQueue> commandQueue = [self.device newCommandQueue];
        _commandQueue = commandQueue;
    }
    

    2.创建渲染管道

    这里解释下MTLRenderPipelineDescriptor对象,为了我们能操控渲染管道,苹果提供了这个对象。它可以关联自定义着色器等操作。

    - (void)createRenderPipelineState {
        NSError *error;
        id<MTLLibrary> library = [self.device newDefaultLibrary];
        if (error) {
            NSLog(@"error:%@",error);
        }
        // 创建顶点着色器
        id<MTLFunction> vertextFunction = [library newFunctionWithName:@"vertextShader"];
        // 创建片段着色器
        id<MTLFunction> fragmentFunction = [library newFunctionWithName:@"fragmentShader"];
    
        MTLRenderPipelineDescriptor *renderPipelineDescritior = [[MTLRenderPipelineDescriptor alloc] init];
        renderPipelineDescritior.colorAttachments[0].pixelFormat = self.mtkView.colorPixelFormat;
        renderPipelineDescritior.vertexFunction = vertextFunction;
        renderPipelineDescritior.fragmentFunction = fragmentFunction;
    
        // 创建渲染管道 id<MTLRenderPipelineState>
        id<MTLRenderPipelineState> renderPipelineState = [self.device newRenderPipelineStateWithDescriptor:renderPipelineDescritior error:nil];
        _renderPipelineState = renderPipelineState;
    }
    

    3.初始化顶点缓存

    在前面的准备工作都做好之前,我们需要设置一些顶点数据来告诉MTKView,我们需要绘制的内容的顶点数据。

    - (void)createVertextBuffer {
        // 顶点数据 (顶点坐标 + 顶点颜色)
        BBVertex vertext[] = {
            { {-1, -0.5, 0, 1}, {1, 0, 0, 1} },
            { {0, 0.5, 0, 1}, {0, 1, 0, 1} },
            { {1, -0.5, 0, 1}, {0, 0, 1, 1} },
        };
    
        // 顶点数目
        _vertextCount = sizeof(vertext) / sizeof(BBVertex);
    
        // 构建顶点缓存,这里options需要设置MTLResourceStorageModeShared,方便顶点数据在CPU与GPU之间快速存取
        id<MTLBuffer> vertextBuffer = [self.device newBufferWithBytes:vertext length:sizeof(vertext) options:MTLResourceStorageModeShared];
    
        _vertextBuffer = vertextBuffer;
    }
    

    4.渲染方法

    在开始渲染之前,我们必须实现MTKView的两个代理方法:

    /**
    MTKView视口大小发生变化时候回调
    */
    - (void)mtkView:(nonnull MTKView *)view drawableSizeWillChange:(CGSize)size {
        self.viewportSize = (vector_uint2){size.width, size.height};
    }
    
    /*!
    这个方法执行的频率和MTKView默认刷新频率一致,默认60帧。
    可以通过设置MTKView的属性preferredFramesPerSecond来调整回调频率
    */
    - (void)drawInMTKView:(nonnull MTKView *)view {
        id<MTLCommandBuffer> commandBuffer = [_commandQueue commandBuffer];
        commandBuffer.label = @"commandBuffer";
        // 获取渲染描述对象,可以理解为拿到真正的渲染对象
        MTLRenderPassDescriptor *renderPassPipeline = view.currentRenderPassDescriptor;
        if (commandBuffer && renderPassPipeline) {
            // 设置背景色
            renderPassPipeline.colorAttachments[0].clearColor = MTLClearColorMake(1, 1, 1, 1);
            // 1.创建渲染编码器
            id<MTLRenderCommandEncoder> renderCommandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassPipeline];
            // 2.设置视口大小
            [renderCommandEncoder setViewport:(MTLViewport){0,0, self.viewportSize.x, self.viewportSize.y,-1,1}];
            // 3.设置渲染管道状态
            [renderCommandEncoder setRenderPipelineState:self.renderPipelineState];
            // 4.设置顶点缓存区
            [renderCommandEncoder setVertexBuffer:self.vertextBuffer offset:0 atIndex:BBVertexInputIndexVertexes];
            // 5.设置图元连接方式
            [renderCommandEncoder drawPrimitives:MTLPrimitiveTypeTriangle vertexStart:0 vertexCount:self.vertextCount];
            // 6.结束命令编码
            [renderCommandEncoder endEncoding];
            // 7.开始绘制
            [commandBuffer presentDrawable:view.currentDrawable];
        }
        // 提交命令缓存
        [commandBuffer commit];
    }
    

    5.渲染结果

    三角形.png

    相关文章

      网友评论

        本文标题:使用Metal绘制第一个三角形

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