[MetalKit]3-Using-MetalKit-part-2使用MetalKit2

本系列文章是对 metalkit.org 上面MetalKit内容的全面翻译和学习.c++

MetalKit系统文章目录git


在本系列的第一部分中咱们介绍了MetalKit框架.让咱们回到part1的项目中并继续.再看一遍render() 函数,他应该看起来这样:程序员

func render() {
    let device = MTLCreateSystemDefaultDevice()!
    self.device = device
    let rpd = MTLRenderPassDescriptor()
    let bleen = MTLClearColor(red: 0, green: 0.5, blue: 0.5, alpha: 1)
    rpd.colorAttachments[0].texture = currentDrawable!.texture
    rpd.colorAttachments[0].clearColor = bleen
    rpd.colorAttachments[0].loadAction = .Clear
    let commandQueue = device.newCommandQueue()
    let commandBuffer = commandQueue.commandBuffer()
    let encoder = commandBuffer.renderCommandEncoderWithDescriptor(rpd)
    encoder.endEncoding()
    commandBuffer.presentDrawable(currentDrawable!)
    commandBuffer.commit()
}
复制代码

让咱们改进一下这段代码.首先,既然咱们的类已是MTKView的子类,它就已经有了本身的device,因此没有必要再声明一个.这就能够把头两行变成一行:github

device = MTLCreateSystemDefaultDevice()
复制代码

第二步,上周咱们说到咱们应该确保currentDrawablecurrentRenderPassDescriptor不为空不然应用会崩溃.为了简单点,咱们在第1部分时没作,因此如今来添加一下.这将让咱们能再减小几行代码.最终版看起来会像这样:swift

func render() {
    device = MTLCreateSystemDefaultDevice()
    if let rpd = currentRenderPassDescriptor, drawable = currentDrawable {
        rpd.colorAttachments[0].clearColor = MTLClearColorMake(0, 0.5, 0.5, 1.0)
        let command_buffer = device!.newCommandQueue().commandBuffer()
        let command_encoder = command_buffer.renderCommandEncoderWithDescriptor(rpd)
        command_encoder.endEncoding()
        command_buffer.presentDrawable(drawable)
        command_buffer.commit()
    }
}
复制代码

你可能会好奇colorAttachments[0] 是什么.为了设置rendering pipeline state渲染管线状态,Metal框架提供了3种类型的附件,来让咱们写入:数组

  • colorAttachments颜色附件
  • depthAttachmentPixelFormat像素格式的深度附件
  • stencilAttachmentPixelFormat像素格式的模板附件 咱们目前只关心如何储存颜色数据,colorAttachments是一个纹理数组,里面包含了绘制结果并将他们展现到屏幕上.咱们目前只有一个这样的纹理-数组中的第一个元素(数组下标为0). OK,如今是时候运行程序了,确保你仍然能看到像上次同样背景颜色.很棒!只用9行代码咱们就建立了一个安全运行在GPU上的Metal代码.

接下来,让咱们深刻研究一个新的Metal话题-在屏幕上绘制几何体.全部的图形学教程好比和OpenGL相关的都会以Hello,Triangle类型程序开始,由于三角形是能绘制在屏幕上几何体中最简单的一个.它是2D图形学基本元素,图形学中其余全部对象都是三角形组成的,因此它是个很好的入门切入点.想象屏幕坐标系统拥有本身的贯穿屏幕中心的坐标轴,中心点坐标为 (0,0).相应的屏幕边缘应该为 -11 .让咱们建立一组浮点数和一个缓冲器来保存三角形的顶点.在初始化device后插入下面几行代码:安全

let vertex_data:[Float] = [-1.0, -1.0, 0.0, 1.0,
                            1.0, -1.0, 0.0, 1.0,
                            0.0,  1.0, 0.0, 1.0]
let data_size = vertex_data.count * sizeof(Float)
let vertex_buffer = device!.newBufferWithBytes(vertex_data, length: data_size, options: [])
复制代码

上面的顶点数据是按顺序排列的:左下,右下,中上.咱们注意到每一个顶点使用4个浮点数来表示坐标.前两个是xy轴.本次用不到的浮点数是:第三个深度(z轴)和第四个w坐标用来使坐标系齐次化.咱们将在下一节讨论他们.而后咱们计算这个数组的size大小为12个浮点数长度,最后咱们用数组及其长度来建立一个缓冲器.如今咱们已经储存好了咱们的顶点,还须要找个办法将他们发送到GPU以便能在屏幕上显示他们.让咱们看看绘制到屏幕的整个处理过程(即管线):框架

chapter03_1.png

咱们目前已经完成了第1阶段,储存顶点.下一步要求咱们有一个新的构件称为shader着色器.一个shader就是程序员可以用自定义函数来干预图形管线的地方.Metal提供了几种类型的着色器,但今天咱们只看其中两种:vertex shader顶点着色器负责点的位置,fragment shader片断着色器负责点的颜色.函数

Metal框架提供了一个函数,咱们能够在device中调用,来建立一个函数(shader)组成的Library库,以下:post

let library = device!.newDefaultLibrary()!
let vertex_func = library.newFunctionWithName("vertex_func")
let frag_func = library.newFunctionWithName("fragment_func")
复制代码

咱们建立两个新的函数,将其指向对应的着色器(稍后会建立).下一步是建立一个Render Pipeline Descriptor渲染管线描述符来使用着色器:

let rpld = MTLRenderPipelineDescriptor()
rpld.vertexFunction = vertex_func
rpld.fragmentFunction = frag_func
rpld.colorAttachments[0].pixelFormat = .BGRA8Unorm
复制代码

你可能会好奇 .BGRA8Unorm是什么意思.它设置了像素格式,因此渲染管线中出来的全部东西的颜色组件都会是同一顺序(本例中按Blue,Green,Red,Alpha顺序),同时大小也会一致(本例中是8-bit的颜色值,范围从0255).最后一步是根据上面的descriptor描述符建立一个Render Pipeline State渲染管线状态:

let rps = try! device!.newRenderPipelineStateWithDescriptor(rpld)
复制代码

最后,咱们只须要让命令编码器获取到咱们的三角形就能够了,因此添加下面几行代码到建立encoder编码器以后:

command_encoder.setRenderPipelineState(rps)
command_encoder.setVertexBuffer(vertex_buffer, offset: 0, atIndex: 0)
command_encoder.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: 3, instanceCount: 1) 
复制代码

如今咱们回到两个shaders那里,咱们在建立Library库时说过须要建立的.为了建立shader,咱们须要建立一个Xcode中的新文件.选择Metal File类型,命名为Shaders.metal或者其余相似名字,单击Create.你将看到代码彷佛不是Swift的,由于Metal shading language着色语言实际上是基于C++的.添加下面的代码:

#include <metal_stdlib>

using namespace metal;

struct Vertex {
    float4 position [[position]];
};

vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]], uint vid [[vertex_id]]) {
    return vertices[vid];
}

fragment float4 fragment_func(Vertex vert [[stage_in]]) {
    return float4(0.7, 1, 1, 1);
}
复制代码

代码至关直白.咱们首先建立一个struct结构体命名为Vertex,里面只有一个成员-一个包含位置数组的数组.咱们注意到数组是float4类型,在着色语言中该类型和咱们前面建立顶点时的4个浮点数是同样的.咱们如今先不解释 [[...]] 语法的意思.而后咱们看到vertex_func着色器返回当前顶点的location位置,fragment_func着色器返回当前顶点的color颜色.咱们硬编码了一个特殊的颜色值,可是咱们能够将color结构体成员添加到Vertex上,这样将每一个顶点设置不一样的颜色. 若是你运行应用,将会看到像这样的三角形:

chapter03_2.png

下一部分咱们将学习Metal shading language也就是3D图形是怎样渲染到GPUs的.源代码source code 已发布在Github上.

下次见!

相关文章
相关标签/搜索