[MetalKit]6-Using-MetalKit-part-5使用MetalKit5

本系列文章是對 metalkit.org 上面MetalKit內容的全面翻譯和學習.c++

MetalKit系統文章目錄git


上一次咱們瞭解了graphics pipeline圖形管線Metal pipelineMetal管線.此次咱們更深刻地學習管線,並理解頂點是如何在底層被處理.爲此,咱們須要學習一點兒3D math 3D數學概念好比transformations變換.github

3D graphics 3D圖形世界中,咱們常常以3維或4維來考慮咱們的數據.上一節中,locationcolor都是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_buffercommand 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個參數(緩衝器),並將模型矩陣乘以頂點.若是如今你運行應用,你將看到咱們的三角形仍是老樣子,佔據了視圖的整個空間.

chapter05_1.png

讓咱們縮放它到原始尺寸的四分之一.在modelMatrix函數添加一行:

matrix = scalingMatrix(matrix, 0.25)
複製代碼

再運行應用,注意到三角形如今變小了:

chapter05_2.png

接下來,讓咱們沿y軸正方向平移三角形,向上移動半個屏幕的高度:

matrix = translationMatrix(matrix, float3(0.0, 0.5, 0.0))
複製代碼

再運行應用,注意到三角形如今比之前高了:

chapter05_3.png

最後,讓咱們繞z旋轉三角形:

matrix = rotationMatrix(matrix, float3(0.0, 0.0, 0.1))
複製代碼

再運行應用,能夠看到三角形也旋轉了:

chapter05_4.png

下週咱們終於能夠開始繪製3D物體(例如立方體或球體)了,源代碼source code 已發佈在Github上.

下次見!

相關文章
相關標籤/搜索