本系列文章是对 metalkit.org 上面MetalKit内容的全面翻译和学习.c++
上一次咱们了解了graphics pipeline图形管线
和Metal pipelineMetal管线
.此次咱们更深刻地学习管线,并理解顶点是如何在底层被处理.为此,咱们须要学习一点儿3D math 3D数学
概念好比transformations变换.github
在3D graphics 3D图形
世界中,咱们常常以3维或4维来考虑咱们的数据.上一节中,location
和color
都是vector_float4(4维)类型.为了在屏幕上绘制3D几何体,顶点经历了一系列变换-从object space物体空间
到world space世界空间
,接着到camera/eye space摄像机/视点空间
, 再到clipping space裁剪空间
, 到 normalized device coordinates space归一化设备坐标空间
, 最终到了screen space屏幕空间
. 本节咱们只关注第一个阶段.swift
咱们三角形的顶点是以object space物体空间
(本地坐标系)表示的.它们指定的三角形原点在屏幕的中心.为了在更大的空间(世界坐标系)放置和移动三角形,咱们须要transformations变换
这些顶点.咱们关注的transformations变换
是: scaling缩放, translation平移和rotation旋转.数组
translation matrix平移矩阵相似于identity matrix单位矩阵(主对角线上是1的矩阵),但在 [12], [13] 和 [14] 位置(在列主序
矩阵中它们至关于[3]
,[7]
和[11]
)上存放着向量D,这个向量表明着顶点相对于x,y,z轴被移动的距离.bash
| 1 0 0 Dx |
| 0 1 0 Dy |
| 0 0 1 Dz |
| 0 0 0 1 |
复制代码
scaling matrix缩放矩阵也相似于identity matrix单位矩阵但在 [0], [5] 和 [10] 位置上存放着向量S,这个向量表明着顶点被缩放到的比例.x,y,z向量值一般能是同样的浮点数,由于这样各个轴上都按比例缩放.ide
| Sx 0 0 0 |
| 0 Sy 0 0 |
| 0 0 Sz 0 |
| 0 0 0 1 |
复制代码
rotation matrix旋转矩阵也相似于identity matrix单位矩阵但根据旋转轴不一样,旋转角度的正弦或余弦存放的位置也会不一样.若是是绕x轴旋转,则存放在 [5],[6], [9] 和 [10] 位置上.若是是绕y轴旋转,则存放在 [0] , [2] , [8] 和 [10] 位置上.是绕z轴旋转,则存放在 [0] , [1] , [4] 和 [5] 位置上.请牢记,这些位置须要被转置为column-major order列主序
.函数
| 1 0 0 0 |
| 0 cos -sin 0 |
| 0 sin cos 0 |
| 0 0 0 1 |
| cos 0 sin 0 |
| 0 1 0 0 |
| -sin 0 cos 0 |
| 0 0 0 1 |
| cos -sin 0 0 |
| sin cos 0 0 |
| 0 0 1 0 |
| 0 0 0 1 |
复制代码
好了,我已经有了足够的数学知识来应付本周的内容了,让咱们把这些数学公式用到代码里面吧.咱们从第三部分part 3的代码继续下去.方便起见,咱们建立一个名为Matrix的结构体
,其中包含这些transformations变换
:post
struct Matrix {
var m: [Float]
init() {
m = [1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]
}
func translationMatrix(var matrix: Matrix, _ position: float3) -> Matrix {
matrix.m[12] = position.x
matrix.m[13] = position.y
matrix.m[14] = position.z
return matrix
}
func scalingMatrix(var matrix: Matrix, _ scale: Float) -> Matrix {
matrix.m[0] = scale
matrix.m[5] = scale
matrix.m[10] = scale
matrix.m[15] = 1.0
return matrix
}
func rotationMatrix(var matrix: Matrix, _ rot: float3) -> Matrix {
matrix.m[0] = cos(rot.y) * cos(rot.z)
matrix.m[4] = cos(rot.z) * sin(rot.x) * sin(rot.y) - cos(rot.x) * sin(rot.z)
matrix.m[8] = cos(rot.x) * cos(rot.z) * sin(rot.y) + sin(rot.x) * sin(rot.z)
matrix.m[1] = cos(rot.y) * sin(rot.z)
matrix.m[5] = cos(rot.x) * cos(rot.z) + sin(rot.x) * sin(rot.y) * sin(rot.z)
matrix.m[9] = -cos(rot.z) * sin(rot.x) + cos(rot.x) * sin(rot.y) * sin(rot.z)
matrix.m[2] = -sin(rot.y)
matrix.m[6] = cos(rot.y) * sin(rot.x)
matrix.m[10] = cos(rot.x) * cos(rot.y)
matrix.m[15] = 1.0
return matrix
}
func modelMatrix(var matrix: Matrix) -> Matrix {
return matrix
}
}
复制代码
让咱们讲解一下这个代码.咱们首先建立一个结构体
并声明一个数组
存放浮点数
.而后提供一个初始化方法,返回单位矩阵(对角线上都是1).接下来,咱们建立变换矩阵.最后,咱们建立一个modelMatrix模型矩阵,它组合了全部的变换并输出到一个矩阵中.学习
为了让这些变换起做用,咱们须要经过shader
把它们送到GPU
.为此,咱们首先应该建立一个新的缓冲器.让咱们命名为uniform_buffer. Uniforms
是一个结构体,咱们能够用它来发送数据到整个模型,而不是各个顶点.用uniforms
替换及只发送一个包含全部变换的最终model matrix模型矩阵
,只是为了节约空间.因此在MetalView
类开头处建立新的缓冲器:
var uniform_buffer: MTLBuffer!
复制代码
在createBuffers() 函数里,给缓冲器分配内存,足够容纳4x4的矩阵:
uniform_buffer = device!.newBufferWithLength(sizeof(Float) * 16, options: [])
let bufferPointer = uniform_buffer.contents()
memcpy(bufferPointer, Matrix().modelMatrix(Matrix()).m, sizeof(Float) * 16)
复制代码
在sendToGPU() 函数里,在设置vertex_buffer
到command encoder命令编码器
后,也设置一下uniform_buffer:
command_encoder.setVertexBuffer(uniform_buffer, offset: 0, atIndex: 1)
复制代码
最后,让咱们转到Shaders.metal中进行最后一部分的配置.在Vertex
结构体下面,建立一个新的结构体命名为Uniforms,用来保存咱们的模型矩阵:
struct Uniforms {
float4x4 modelMatrix;
};
复制代码
修改vertex shader顶点着色器
,来接收咱们从CPU
传过来的变换:
vertex Vertex vertex_func(constant Vertex *vertices [[buffer(0)]],
constant Uniforms &uniforms [[buffer(1)]],
uint vid [[vertex_id]])
{
float4x4 matrix = uniforms.modelMatrix;
Vertex in = vertices[vid];
Vertex out;
out.position = matrix * float4(in.position);
out.color = in.color;
return out;
}
复制代码
这里咱们作的就是传递uniforms做为第2个参数(缓冲器),并将模型矩阵乘以顶点.若是如今你运行应用,你将看到咱们的三角形仍是老样子,占据了视图的整个空间.
让咱们缩放它到原始尺寸的四分之一.在modelMatrix函数添加一行:
matrix = scalingMatrix(matrix, 0.25)
复制代码
再运行应用,注意到三角形如今变小了:
接下来,让咱们沿y轴正方向平移三角形,向上移动半个屏幕的高度:
matrix = translationMatrix(matrix, float3(0.0, 0.5, 0.0))
复制代码
再运行应用,注意到三角形如今比之前高了:
最后,让咱们绕z轴旋转三角形:
matrix = rotationMatrix(matrix, float3(0.0, 0.0, 0.1))
复制代码
再运行应用,能够看到三角形也旋转了:
下周咱们终于能够开始绘制3D物体(例如立方体或球体)了,源代码source code 已发布在Github上.
下次见!