Metal入門(mén)資料006-圖像處理基礎(chǔ)知識(shí)講解

寫(xiě)在前面:

對(duì)Metal技術(shù)感興趣的同學(xué),可以關(guān)注我的專題:Metal專輯
也可以關(guān)注我個(gè)人的簡(jiǎn)書(shū)賬號(hào):張芳濤
所有的代碼存儲(chǔ)的Github地址是:Metal

正文

上次我們描述了graphics pipeline(圖形管道)和Metal pipeline(Metal管道)。 現(xiàn)在是時(shí)候我們更深入地觀察管道,并了解頂點(diǎn)如何在較低的層次上真正處理。 為此,我們需要學(xué)習(xí)一些比如transformations3D math(3D)數(shù)學(xué)概念。

3D graphics(3D圖形)的世界中,我們通常根據(jù)34維來(lái)考慮我們的數(shù)據(jù)。 正如你從上一集中記得的,location(位置)和color(顏色)都是vector_float4(4維)類型。 為了在屏幕上繪制3D幾何圖形,頂點(diǎn)會(huì)遭受一系列變換 - 從object space(物體空間)到world space(世界空間),然后到camera/eye space(相機(jī)/眼睛空間),然后到clipping space(裁剪空間),然后到normalized device coordinates(標(biāo)準(zhǔn)化的設(shè)備坐標(biāo))空間,最后到screen space(屏幕空間)。 我們只看這個(gè)劇集的第一階段。

我們triangle(三角形)的頂點(diǎn)用object space(物體空間)(局部坐標(biāo))表示。 他們目前指定了位于屏幕中心的三角形原點(diǎn)。 為了在更大的場(chǎng)景(世界空間)中定位和移動(dòng)三角形,我們需要對(duì)這些頂點(diǎn)應(yīng)用transformations(變換)。 我們將看到的transformations(轉(zhuǎn)換)是:scaling(縮放),translation(平移)和rotation(旋轉(zhuǎn))。

translation matrix(平移矩陣)類似于identity matrix(單位矩陣)(其主對(duì)角線上的值為1)以及位置[12],[13]和[14](column-major order(按照列排序),它們等同于[3],[ 7]和[11]位置)填充了一個(gè)D向量的值,該D向量表示頂點(diǎn)將移動(dòng)到各個(gè)x,y,z軸上的距離。

| 1     0     0    Dx |
| 0     1     0    Dy |
| 0     0     1    Dz |
| 0     0     0     1 |

scaling matrix(縮放矩陣)也類似于identity matrix(單位矩陣),其中位置[0],[5][10]用表示頂點(diǎn)將被放大/縮小的比例的S向量的值填充。x,y,z向量值通常是相同的浮點(diǎn)值,因?yàn)樵谒休S上按比例進(jìn)行縮放。

| Sx    0     0     0 |
| 0     Sy    0     0 |
| 0     0     Sz    0 |
| 0     0     0     1 |

rotation matrix(旋轉(zhuǎn)矩陣)也類似于identity matrix(單位矩陣),其中取決于我們正在旋轉(zhuǎn)哪個(gè)軸,不同的位置正在用我們旋轉(zhuǎn)的角度的sinus(正弦)或cosinus(余弦)填充。 如果我們圍繞x軸旋轉(zhuǎn),則會(huì)填充位置[5][6],[9][10]。 如果我們圍繞y軸旋轉(zhuǎn),則填充位置[0][2],[8][10]。 最后,如果我們圍繞z軸旋轉(zhuǎn),則填充位置[0],[1][4][5]。 請(qǐng)記住,這些職位需要轉(zhuǎn)換為按列排序。

| 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 |

我們有足夠的數(shù)學(xué)知識(shí)----得需要一周的時(shí)間來(lái)消化掉,所以讓我們把這些矩陣加入到代碼中。 我們將繼續(xù)第3部分之后的代碼。它適用于我們創(chuàng)建一個(gè)名為Matrix的結(jié)構(gòu)體,它將包含這些transformations(轉(zhuǎn)換):

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
  }
}

我們來(lái)看看這段代碼。 我們首先創(chuàng)建一個(gè)struct(結(jié)構(gòu)體)并聲明一個(gè)浮點(diǎn)類型的數(shù)組。 然后我們?yōu)樗峁┮粋€(gè)初始化器,它是identity matrix(單位矩陣)(對(duì)角線上的所有的值都是1)。 接下來(lái),我們創(chuàng)建變換矩陣。 最后,我們創(chuàng)建一個(gè)modelMatrix,它將所有轉(zhuǎn)換組合到一個(gè)輸出矩陣中。

為了進(jìn)行這些轉(zhuǎn)換工作,我們需要通過(guò)shader(著色器)將它們發(fā)送到GPU。 為了做到這一點(diǎn),我們首先需要?jiǎng)?chuàng)建一個(gè)新的緩沖區(qū)。 我們把它命名為uniform_buffer。 當(dāng)我們想要將數(shù)據(jù)發(fā)送到整個(gè)模型而不是每個(gè)頂點(diǎn)時(shí),Uniforms就是我們可以使用的構(gòu)造。 只有通過(guò)使用Uniforms來(lái)節(jié)省空間并發(fā)送包含所有轉(zhuǎn)換的最終model matrix才有意義。 所以在我們的MetalView類的一開(kāi)始,創(chuàng)建新的緩沖區(qū):

var uniform_buffer: MTLBuffer!

createBuffers()函數(shù)內(nèi)部,為緩沖區(qū)分配內(nèi)存,足以容納4x4矩陣:

uniform_buffer = device!.newBufferWithLength(sizeof(Float) * 16, options: [])
let bufferPointer = uniform_buffer.contents()
memcpy(bufferPointer, Matrix().modelMatrix(Matrix()).m, sizeof(Float) * 16)

endToGPU()函數(shù)內(nèi)部,在命令編碼器中設(shè)置vertex_buffer之后,還要設(shè)置uniform_buffer

command_encoder.setVertexBuffer(uniform_buffer, offset: 0, atIndex: 1)

最后,讓我們轉(zhuǎn)到Shaders.metal進(jìn)行配置的最后部分。 在Vertex結(jié)構(gòu)體下面,創(chuàng)建一個(gè)名為Uniforms的結(jié)構(gòu),它將保存我們的模型矩陣:

struct Uniforms {
float4x4 modelMatrix;
};

修改vertex shader(頂點(diǎn)著色器)以包含我們從CPU傳來(lái)的轉(zhuǎn)換:

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;
}

我們?cè)谶@里所做的只是將uniforms作為第二個(gè)參數(shù)(緩沖區(qū)),然后將模型矩陣與頂點(diǎn)相乘。 如果您現(xiàn)在運(yùn)行該應(yīng)用程序,您將看到一個(gè)三角形,占據(jù)整個(gè)視圖的空間。

我們將其縮小至原始尺寸的四分之一。 將此行添加到modelMatrix函數(shù)中:

matrix = scalingMatrix(matrix, 0.25)

再次運(yùn)行應(yīng)用程序,注意現(xiàn)在三角形變小了:

接下來(lái),讓我們通過(guò)將它向上移動(dòng)一半的屏幕大小來(lái)在y軸上平移三角形:

matrix = translationMatrix(matrix, float3(0.0, 0.5, 0.0))

再次運(yùn)行應(yīng)用程序,注意三角現(xiàn)在比以前更高:

最后,讓我們圍繞z軸旋轉(zhuǎn)三角形:

matrix = rotationMatrix(matrix, float3(0.0, 0.0, 0.1))

再次運(yùn)行應(yīng)用程序,注意三角形現(xiàn)在也旋轉(zhuǎn)了:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容