本系列文章是對 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上.
下次見!