使用Metal绘制第一个三角形

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

1.MTKView

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

- (void)createMTKView {
    // 创建GPU设备
    id 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 commandQueue = [self.device newCommandQueue];
    _commandQueue = commandQueue;
}

2.创建渲染管道

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

- (void)createRenderPipelineState {
    NSError *error;
    id library = [self.device newDefaultLibrary];
    if (error) {
        NSLog(@"error:%@",error);
    }
    // 创建顶点着色器
    id vertextFunction = [library newFunctionWithName:@"vertextShader"];
    // 创建片段着色器
    id fragmentFunction = [library newFunctionWithName:@"fragmentShader"];

    MTLRenderPipelineDescriptor *renderPipelineDescritior = [[MTLRenderPipelineDescriptor alloc] init];
    renderPipelineDescritior.colorAttachments[0].pixelFormat = self.mtkView.colorPixelFormat;
    renderPipelineDescritior.vertexFunction = vertextFunction;
    renderPipelineDescritior.fragmentFunction = fragmentFunction;

    // 创建渲染管道 id
    id 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 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 commandBuffer = [_commandQueue commandBuffer];
    commandBuffer.label = @"commandBuffer";
    // 获取渲染描述对象,可以理解为拿到真正的渲染对象
    MTLRenderPassDescriptor *renderPassPipeline = view.currentRenderPassDescriptor;
    if (commandBuffer && renderPassPipeline) {
        // 设置背景色
        renderPassPipeline.colorAttachments[0].clearColor = MTLClearColorMake(1, 1, 1, 1);
        // 1.创建渲染编码器
        id 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绘制第一个三角形)