在之前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];
}
网友评论