二、使用Metal绘制视图内容

本文Demo

环境:

mac os 10.14.5

xcode 10.3

此系列文章源自官方案例,详情至 此处

专用名词虽有汉字翻译,但会保留原有英文形式名词。

 

概览

本示例中,将学习用Metal渲染图形内容的基础知识。用 Metal Framework创建视图,用 Metal绘制视图内容。然后,为 渲染过程 编码命令,清除视图生成背景色。

 

1.准备绘制 MetalKit 视图

MetalKit提供 MTKView类,继承自 NSView(macOS)或者 UIView(iOS 或 tvOS)。MTKView 处理许多用Metal绘制的内容在屏幕上相关的细节  。

MTKView需要对 Metal device object(设备对象)引用,才能在内部创建资源。因此第一步设置视图的 device属性。

_view.device = MTLCreateSystemDefaultDevice();

通过 MTKView 的其他属性去控制它的行为。设置 clearColor属性,清除视图的内容生成纯背景色,用 MTLClearColorMake函数创建,指定 red、green、blue 和 alpha。

_view.clearColor = MTLClearColorMake(0.0, 0.5, 1.0, 1.0);

示例不会绘制动画内容,因此配置视图需要更新时才绘制,如形状改变:

_view.enableSetNeedsDisplay = YES;

 

2.委托绘制责任

MTKView 依赖应用向Metal发送命令生成可视内容。MTKView 需要绘制时使用委托模式通知应用。为了能够收到委托的回调,设置视图的 delegate属性为遵守 MTKView Delegate协议的对象。

_view.delegate = _renderer;

 

委托实现了两个方法:

a)当内容大小改变的时候,视图调用mtkView:drawbleSizeWillChange: 方法。当调整包含视图的窗口大小时或者设备的方向改变(在 iOS上)时发生。允许应用根据呈现的分辨率调整视图大小。

b)当更新视图内容的时候,调用 drawInMTKView: 方法。在此方法中,创建命令缓冲区,编码命令来告知GPU在屏幕上绘制何物以及何时显示,将命令缓冲区放入队列等待GPU执行。有时也称作绘制 frame(帧),可以把帧想象成生成一张图片的工作。像在游戏类的应用中,每秒绘制很多帧。

示例中,AAPLRenderer 类实现委托方法,并且承担绘制责任。控制器将其实例化设置成视图的delegate。

 

3.创建Render Pass Descriptor(渲染过程描述符)

绘制时,GPU 存储结果到 textures纹理),它是包含图片数据和可供GPU访问的内存块。MTKView创建了需要绘制视图的所有纹理。它创建了多个纹理,以便在渲染一个纹理时可以显示另一个纹理的内容。

为了要绘制,需要创建 render pass(渲染过程),它是一系列的渲染命令绘制成的一组纹理。当在 render pass上使用时,纹理也被叫做 render targets(渲染目标)。为了创建 render pass,需要 MTLRenderPassDescriptor实例。示例中,不要配置自己的渲染过程描述符,而是要 MetalKit 视图创建。

MTLRenderPassDescriptor *renderPassDescriptor = view.currentRenderPassDescriptor;
if (renderPassDescriptor == nil)
{
    return;
}

渲染过程描述符描述 render targets(渲染目标即纹理),以及如何在渲染过程开始和结束时处理它们。视图返回一个渲染过程描述符,其中包含指向纹理之一的单一颜色附件,否则根据视图的属性配置渲染过程。默认情况下,意味着在渲染过程的开始,纹理被清除成匹配视图的 clearColor属性的纯色,渲染过程结束时,所有的变化被存储到纹理中。

由于渲染过程描述符可能为空,在创建渲染过程之前,需要确保其不为空。

 

4.创建 render pass(渲染过程)

可以通过 MTLRender CommandEncoder 对象将其编码进命令缓冲区来创建渲染过程。调用命令缓冲区的 renderCommandEncoderWithDescriptor: 方法并传入 渲染过程描述符

id<MTLRenderCommandEncoder> commandEncoder = [commandBuffer renderCommandEncoderWithDescriptor:renderPassDescriptor];

此示例中,不需要编码任何绘制命令,因此渲染过程唯一要做的是清除纹理。调用 endEncoding 方法表明该过程完毕。

[commandEncoder endEncoding];

 

5.呈现可绘制对象到屏幕

对纹理的绘制不会自动显示在屏幕的新内容上。事实上,仅有一些纹理可以展现。在 Metal 中,呈现在屏幕上的纹理被 drawable对象管理,要显示内容,将呈现可绘制对象。

MTKView 自动创建 drawable 对象去管理纹理。通过 currentDrawable属性获取渲染过程目标拥有纹理的可绘制对象。视图返回一个和 Core Animation 关联的 CAMetalDrawable对象。

id<MTLDrawable> drawable = view.currentDrawable;

在命令缓冲区调用 presentDrawable: 方法,并传递 drawable(可绘制对象)

[commandBuffer presentDrawable:drawable];

此方法旨在告诉Metal,当命令缓冲区计划执行时,应协调Core Animation在渲染完成后去显示纹理。当Core Animation呈现纹理时,它将成为视图的新内容。示例中,清除纹理成为视图的新背景色。此改变随着Core Animation为屏幕用户界面元素进行的其他可视化更新一起发生。

 

6.提交命令缓冲区

现在已经为 frame(帧)下发所有的命令,提交命令缓冲区。

[commandBuffer commit];

个人博客:https://blog.csdn.net/Crazy_SunShine

Github:https://github.com/cxymq

个人公众号:Flutter小同学

个人网站:http://chenhui.today/

在苹果的Metal框架中,创建一个立方体并在窗口上显示通常涉及以下几个步骤: 1. **环境设置**: 首先,你需要初始化Metal渲染管线、设备和命令队列。确保已经导入`MTLKit.h`文件,并创建一个`MTLLibrary`来加载着色器。 ```swift import MetalKit let device = MTLCreateSystemDefaultDevice() let commandQueue = device.makeCommandQueue() let library = try! MTLCreateSystemLibrary() ``` 2. **绘制缓冲区和视图**: 创建一个`MTLBuffer`来存储顶点数据(例如一个6面立方体的顶点坐标),并准备一个`MTKView`用于渲染Metal内容。 ```swift let vertexData = createCubeVertices() // 函数自行编写,生成立方体顶点数据 let verticesBuffer = device.makeBuffer(bytesNoCopy: vertexData, length: MemoryLayout<Vertex>.size * vertexData.count, options: .storageModeBuffer) let view = MTKView(frame: window.frame, device: device) view.delegate = self // 添加Metal视图的委托 ``` 3. **着色器和管线状态**: 编译顶点函数和片段函数(如果你已经有了的话),然后创建`MTLRenderPipelineState`,配置颜色格式等属性。 ```swift let vertexFunction = compileShaderFromSource(library, .vertex, "your_vertex_function") let fragmentFunction = compileShaderFromSource(library, .fragment, "your_fragment_function") let pipelineDescriptor = MTLRenderPipelineDescriptor() pipelineDescriptor.vertexFunction = vertexFunction pipelineDescriptor.fragmentFunction = fragmentFunction // 更多配置... let renderPipelineState = try! device.makeRenderPipelineState(descriptor: pipelineDescriptor) ``` 4. **绘制循环**: 在`draw(in:commandEncoder:)`方法中,设置当前管线状态,绑定缓冲区,然后进行渲染。 ```swift func draw(_ commandEncoder: MTLRenderCommandEncoder) { let vertexBuffer = verticesBuffer.contents().assumingMemoryBound(to: Vertex.self) let descriptorSet = makeDescriptorSet(verticesBuffer) commandEncoder.setRenderPipelineState(renderPipelineState) commandEncoder.setVertexBuffer(vertexBuffer, offset: 0, index: 0) commandEncoder.drawPrimitives(type: .triangle, primitiveCount: 36, instanceCount: 1, baseVertex: 0, baseInstance: 0) // 渲染完成后的清理工作... } func makeDescriptorSet(buffer: MTLBuffer) -> MTLDescriptorSet { // 创建并配置descriptor set... } ``` 5. **展示**: 将`MTKView`添加到窗口,开始帧循环来更新和渲染。 ```swift window.addSubview(view) RunLoop.main.addUIObserver(self, forKeyPath: "displayLink.status") { _ in if view.displayLink.status == .running { // 开始渲染 view drawable.makeCurrent() draw(view.currentRenderCommandEncoder) } } ``` 记得处理`makeDescriptorSet`方法,根据需要配置着色器和纹理等资源。这就是在Metal中画立方体的基本流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值