本系列文章是对 metalkit.org 上面MetalKit内容的全面翻译和学习.c++
在本系列的第一部分中咱们介绍了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()
复制代码
第二步,上周咱们说到咱们应该确保currentDrawable和currentRenderPassDescriptor不为空不然应用会崩溃.为了简单点,咱们在第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
是一个纹理数组,里面包含了绘制结果并将他们展现到屏幕上.咱们目前只有一个这样的纹理-数组中的第一个元素(数组下标为0
). OK,如今是时候运行程序了,确保你仍然能看到像上次同样背景颜色.很棒!只用9行代码咱们就建立了一个安全运行在GPU上的Metal
代码.接下来,让咱们深刻研究一个新的Metal
话题-在屏幕上绘制几何体.全部的图形学教程好比和OpenGL
相关的都会以Hello,Triangle
类型程序开始,由于三角形是能绘制在屏幕上几何体中最简单的一个.它是2D图形学
基本元素,图形学中其余全部对象都是三角形组成的,因此它是个很好的入门切入点.想象屏幕坐标系统拥有本身的贯穿屏幕中心的坐标轴,中心点坐标为 (0,0).相应的屏幕边缘应该为 -1 和 1 .让咱们建立一组浮点数和一个缓冲器来保存三角形的顶点.在初始化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个浮点数来表示坐标.前两个是x和y轴.本次用不到的浮点数是:第三个深度(z轴)
和第四个w坐标
用来使坐标系齐次化.咱们将在下一节讨论他们.而后咱们计算这个数组的size大小为12个浮点数长度,最后咱们用数组及其长度来建立一个缓冲器.如今咱们已经储存好了咱们的顶点,还须要找个办法将他们发送到GPU
以便能在屏幕上显示他们.让咱们看看绘制到屏幕的整个处理过程(即管线
):框架
咱们目前已经完成了第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
的颜色值,范围从0
到255
).最后一步是根据上面的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
上,这样将每一个顶点设置不一样的颜色. 若是你运行应用,将会看到像这样的三角形:
下一部分咱们将学习Metal shading language
也就是3D图形
是怎样渲染到GPUs
的.源代码source code 已发布在Github上.
下次见!