创建一个 MetalKit 视图和渲染通道以绘制视图的内容。
概述
在这个示例中,您将学习使用 Metal 渲染图形内容的基础知识。您将使用 MetalKit 框架创建一个利用 Metal 绘制视图内容的视图。然后,您将编码一个渲染通道的命令,以背景色擦除视图。
注意
MetalKit 自动化了窗口系统任务、加载纹理和处理 3D 模型数据。更多信息,请参见 MetalKit。
准备一个 MetalKit 视图以进行绘制
MetalKit 提供了一个名为 MTKView 的类,它是 NSView(在 macOS 中)或 UIView(在 iOS 和 tvOS 中)的子类。MTKView 处理了许多与将您使用 Metal 绘制的内容显示到屏幕上的相关细节。
MTKView 需要引用一个 Metal 设备对象,以便内部创建资源,因此您的第一步是将视图的 device 属性设置为现有的 MTLDevice。
objectiveC
_view.device = MTLCreateSystemDefaultDevice();
MTKView 上的其他属性允许您控制其行为。要将视图的内容擦除为纯背景色,您需要设置其 clearColor 属性。您可以通过指定红、绿、蓝和 alpha 值使用 MTLClearColorMake(::::) 函数创建颜色。
objectiveC
_view.clearColor = MTLClearColorMake(0.0, 0.5, 1.0, 1.0);
由于在此示例中您不会绘制动画内容,请配置视图,使其仅在需要更新内容时(例如当视图形状改变时)才进行绘制:
objectiveC
_view.enableSetNeedsDisplay = YES;
委托绘制职责
MTKView 依赖您的应用程序向 Metal 发出命令以生成视觉内容。MTKView 使用委托模式在应该进行绘制时通知您的应用程序。要接收委托回调,请将视图的 delegate 属性设置为符合 MTKViewDelegate 协议的对象。
objectiveC
_view.delegate = _renderer;
委托实现两个方法:
- 当内容大小发生变化时,视图会调用
mtkView(_:drawableSizeWillChange:)
方法。这发生在包含视图的窗口被调整大小时,或在设备方向改变时(在 iOS 上)。这允许您的应用程序适应渲染分辨率以匹配视图的大小。 - 当需要更新视图的内容时,视图会调用
draw(in:)
方法。在此方法中,您创建一个命令缓冲区,编码告诉 GPU 绘制什么内容以及何时在屏幕上显示它的命令,并将该命令缓冲区排队以供 GPU 执行。这有时被称为绘制一帧。您可以将帧视为生成显示在屏幕上的单个图像所需完成的所有工作。在交互式应用程序(如游戏)中,您每秒可能会绘制多帧。
在这个示例中,一个名为 AAPLRenderer 的类实现了委托方法并承担了绘制的责任。视图控制器创建此类的一个实例,并将其设置为视图的委托。
创建渲染通道描述符
当您绘制时,GPU 会将结果存储到纹理中,这些纹理是包含图像数据并可由 GPU 访问的内存块。在此示例中,MTKView 创建了绘制视图所需的所有纹理。它创建了多个纹理,以便能够在显示一个纹理的内容的同时向另一个纹理进行渲染。
要进行绘制,您需要创建一个渲染通道,即绘制到一组纹理的一系列渲染命令。在渲染通道中使用的纹理也称为渲染目标。要创建渲染通道,您需要一个渲染通道描述符,即 MTLRenderPassDescriptor 的实例。在此示例中,不是配置自己的渲染通道描述符,而是请求 MetalKit 视图为您的应用创建一个。
objectiveC
MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor == nil)
{
return;
}
渲染通道描述符描述了一组渲染目标及其在渲染通道开始和结束时应如何处理。渲染通道还定义了一些不属于此示例的其他渲染方面。视图返回具有指向视图纹理之一的颜色附件的渲染通道描述符,并根据视图属性配置渲染通道。默认情况下,这意味着在渲染通道开始时,渲染目标会被擦除为与视图的 clearColor 属性匹配的纯色,在渲染通道结束时,所有更改都会保存回纹理。
因为视图的渲染通道描述符可能为 nil,所以在创建渲染通道之前,应该测试确保渲染通道描述符对象是非 nil 的。
创建渲染通道
通过使用 MTLRenderCommandEncoder 对象将其编码到命令缓冲区中来创建渲染通道。调用命令缓冲区的 makeRenderCommandEncoder(descriptor:)
方法并传入渲染通道描述符。
objectiveC
id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];
在此示例中,您不编码任何绘制命令,因此渲染通道唯一做的事情就是擦除纹理。调用编码器的 endEncoding
方法以指示该通道已完成。
ObjectiveC
[commandEncoder endEncoding];
将可绘制对象呈现到屏幕
绘制到纹理不会自动将新内容显示在屏幕上。实际上,只有某些纹理可以呈现在屏幕上。在 Metal 中,能够显示在屏幕上的纹理由可绘制对象管理,要显示内容,您需要呈现可绘制对象。
MTKView 自动创建可绘制对象来管理其纹理。读取 currentDrawable 属性以获取拥有渲染通道目标纹理的可绘制对象。视图返回一个 CAMetalDrawable 对象,这是一个连接到 Core Animation 的对象。
objectiveC
id<MTLDrawable> drawable = view.currentDrawable;
调用命令缓冲区上的 present(_:)
方法,并传入可绘制对象。
objectiveC
[commandBuffer presentDrawable:drawable];
此方法告诉 Metal 在命令缓冲区计划执行时,Metal 应与 Core Animation 协作在渲染完成后显示纹理。当 Core Animation 呈现纹理时,它将成为视图的新内容。在此示例中,这意味着被擦除的纹理将成为视图的新背景。这一变化与其他 Core Animation 为屏幕上用户界面元素所做的视觉更新同步发生。
提交命令缓冲区
既然已经发出了帧的所有命令,提交命令缓冲区。
objectiveC
[commandBuffer commit];