在开始之前,你可以先参考一下本系列的第一篇教程:
- iOS 8 Metal Swift教程(一) :开始学习
在本篇教程中,你将应用到3D图形中的一系列矩阵变换,并会学习到如下内容:
如何使用模型(model),视图(view)以及投影变换(projection transformations)。
如何使用矩阵运算变换几何图形
如何在着色器(shader)间传递统一数据
如何使用背面剔除(backface culling)来优化渲染
开篇
首先,你需要的下载一个工程,这和此前的教程中用到的是一样的。
构建并运行,请注意你的测试设备需要兼容Metal,然后确认你能看到下面这个三角形。
现在你需要下载一个Matrix4类,这是事先写好的,然后你需要将其加入你的工程中。这个时候因为你交叉使用了Swift和Objective-C,Xcode会提示你是否要配置一个桥接头文件(Bridging Header),这时候只要选择YES就行。
待会儿要在很多地方用到矩阵,所以你最好先看一遍Matrix4.m和Matrix4.h文件,对这个类有个清晰的认识。
iOS的内建库GLKMatrix中包含了一个用于常见3D运算的GLKMath库,其中包括可以矩阵运算的GLKMatrix4类。
本教程涉及大量的矩阵运算,使用这个库会很方便许多,不过GLKMatrix4是一个C语言结构体,所以在Swift中你不能直接调用它。
呐,所以呢,我就给各位用Objective-C封装了一下C的结构体,这样我们就能愉快的在Swift中使用GLKMatrix4了,下面是这一调用封装过程的图解:
再次提醒一下,下面内容真的会有很多矩阵运算,所以你现在还是好好看一下Matrix4类的代码吧。
重构一个节点类(Node Class)
所有的内容在一开始的工程里面的ViewController.swift文件中已经都设置好了,这的确是最简便上手的方式,不过等到你的App变得越来越大越来越复杂那可就说不定了。
在这一小节中,你需要通过以下步骤来重构项目:
1.创建顶点结构(Vertex Structure)
2.创建节点类(Node Class)
3.创建三角形子类(Triangle Subclass)
4.重构视图控制器(View Controller)
5.重构着色器(Shader)
提示:这一节是可以不看的,因为到本节最后只是让你对整个工程有个清晰的认识,如果你想要直接看3D处理的部分,那么可以直接跳过此节,而在下一节的开头你可以下载全新的开始项目——当然那是完全配好了的。
1.创建顶点结构(Vertex Structure)
新建一个文件并以 iOS/Source/Swift File为模板创建一个类,命名为Vertex.swift.
打开该文件,用下面代码覆盖它:
1
2
3
4
5
6
7
8
9
|
struct
Vertex{
var x,y,z: Float
// position data
var r,g,b,a: Float
// color data
func floatBuffer() -> [Float] {
return
[x,y,z,r,g,b,a]
}
};
|
这个结构体会存储每一个顶点的颜色信息和位置信息。其中floatBuffer()方法会按照规定的顺序返回一个float型数组,其中包含的是结构体的位置和颜色的信息。
2.创建节点类(Node Class)
同上创建一个Swif文件命名为Node.swift.
同上步骤,代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import Foundationimport Metalimport QuartzCore
class
Node {
let name: String
var vertexCount: Int
var vertexBuffer: MTLBuffer var device: MTLDevice
init(name: String, vertices: Array<Vertex>, device: MTLDevice){
// 遍历每个顶点并将其序列化为一堆float数据放在一个buffer中
var vertexData = Array<Float>()
for
vertex in vertices{
vertexData += vertex.floatBuffer()
}
// 用上面的buffer中的数据来创建一个新的顶点buffer
let dataSize = vertexData.count * sizeofValue(vertexData[0])
vertexBuffer = device.newBufferWithBytes(vertexData, length: dataSize, options: nil)
// 为实例中的各变量赋值
self.name = name self.device = device
vertexCount = vertices.count
}
}
|
作为要渲染的基本单位,每个节点中的顶点都需要包含有一个名字以便使用,之后设备会为其创建buffer并渲染顶点,buffer的结构大致是这样的:
接下来,你需要把当前视图控制器中的部分渲染代码移动到Node中去,这些代码会为特定的顶点渲染起作用。
这样你需要在Node.swift里面添加一个新方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
func render(commandQueue: MTLCommandQueue, pipelineState: MTLRenderPipelineState, drawable: CAMetalDrawable, clearColor: MTLClearColor?){
let renderPassDescriptor = MTLRenderPassDescriptor()
renderPassDescriptor.colorAttachments[0].texture = drawable.texture
renderPassDescriptor.colorAttachments[0].loadAction = .Clear
renderPassDescriptor.colorAttachments[0].clearColor = MTLClearColor(red: 0.0, green: 104.0/255.0, blue: 5.0/255.0, alpha: 1.0)
renderPassDescriptor.colorAttachments[0].storeAction = .Store
let commandBuffer = commandQueue.commandBuffer()
let renderEncoderOpt = commandBuffer.renderCommandEncoderWithDescriptor(renderPassDescriptor)
if
let renderEncoder = renderEncoderOpt {
renderEncoder.setRenderPipelineState(pipelineState)
renderEncoder.setVertexBuffer(vertexBuffer, offset: 0, atIndex: 0)
renderEncoder.drawPrimitives(.Triangle, vertexStart: 0, vertexCount: vertexCount, instanceCount: vertexCount/3)
renderEncoder.endEncoding()
}
commandBuffer.presentDrawable(drawable)
commandBuffer.commit()
}
|
在上一篇教程里面你也可以找到这段代码,你会发现这段代码来源于ViewController类的render()方法,不过对于Node的顶点渲染有所改动。
3.创建三角形子类(Triangle Subclass)
同上创建文件,命名为Triangle.swift.
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
import Foundationimport Metal
class
Triangle: Node {
init(device: MTLDevice){
let V0 = Vertex(x: 0.0, y: 1.0, z: 0.0, r: 1.0, g: 0.0, b: 0.0, a: 1.0)
let V1 = Vertex(x: -1.0, y: -1.0, z: 0.0, r: 0.0, g: 1.0, b: 0.0, a: 1.0)
let V2 = Vertex(x: 1.0, y: -1.0, z: 0.0, r: 0.0, g: 0.0, b: 1.0, a: 1.0)
var verticesArray = [V0,V1,V2]
super.init(name:
"Triangle"
, vertices: verticesArray, device: device)
}
}
|
这里的Triangle继承于刚刚创建的Node类,在构造函数里有三个关于三角形顶点的常量,最后打包为一个数组并传递给了父类的构造函数中。
4.重构视图控制器(View Controller)
打开ViewController.swift并删除下面这行: