[MetalKit]Using MetalKit part 5使用MetalKit5

本系列文章是对 http://metalkit.org 上面MetalKit内容的全面翻译和学习.

MetalKit系统文章目录


上一次我们了解了graphics pipeline图形管线Metal pipelineMetal管线.这次我们更深入地学习管线,并理解顶点是如何在底层被处理.为此,我们需要学习一点儿3D math 3D数学概念比如transformations变换.

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屏幕空间. 本节我们只关注第一个阶段.

我们三角形的顶点是以object space物体空间(本地坐标系)表示的.它们指定的三角形原点在屏幕的中心.为了在更大的空间(世界坐标系)放置和移动三角形,我们需要transformations变换这些顶点.我们关注的transformations变换是: scaling缩放, translation平移rotation旋转.

translation matrix平移矩阵类似于identity matrix单位矩阵(主对角线上是1的矩阵),但在[12], [13][14]位置(在列主序矩阵中它们相当于[3],[7][11])上存放着向量D,这个向量代表着顶点相对于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]位置上存放着向量S,这个向量代表着顶点被缩放到的比例.x,y,z向量值通常能是一样的浮点数,因为这样各个轴上都按比例缩放.

| 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变换:

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上.
下次见!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容