![57336b60169897ec8015908a5a9168e3.png](https://i-blog.csdnimg.cn/blog_migrate/f7ce645dd34962da2f7b0ab065234ab5.png)
讲一讲3D图形渲染管线(3D graphic rendering pipeline)。
为了简单起见,假设就渲染一个三角形模型,来看看GPU的任务是什么。
输入: GPU拿到的是一组顶点数组,每个顶点三个坐标分量,而且是这个模型的局部坐标, 一共个3顶点: x0, y0, z0, x1, y1, z0, x2, y2, z2
还有一些定量:三角形在全局里的位置,转向信息model matrix
;相机的位置,转向信息 view matrix
;相机的焦距信息 projection matrix
。
输出: GPU要给出一个二维位图各个像素的颜色。 如果画面大小是30x30,每个像素有R,G,B分量加一个深度分量w. 那一共30x30x4个浮点数。
这是GPU要完成的任务。
![9f5705cadc9881d9088c93899c70d105.png](https://i-blog.csdnimg.cn/blog_migrate/9b86ffe2709c4c3694f41b1faf668a2d.jpeg)
接下来我们来分解这个任务:
首先顶点处理器(vertex processor)开始处理顶点。 顶点处理器有很多个, 每个一次处理一个顶点,每个顶点特有的信息是attribute, 包括比如位置position (x,y,z)
, 颜色color (R,G,B,A)
, 顶点法线 normal (x,y,z)和贴图坐标UV (u,v)
; 它还知道一些uniform量,也就是Model, View, Projection Matrix这些定量。 除此之外它不知道任何其他顶点的信息。
对于顶点的坐标,输入时是模型局部空间的坐标,我们要知道它们在屏幕空间里的坐标。
对于一个走进顶点处理器的顶点,它自己来自哪儿,要解决去哪儿的问题。
顶点处理器拿到这个顶点后,对这个顶点做矩阵运算, 大致是这样:
position * ModelMatrix * ViewMatrix * ProjectionMatrix
物体空间 * ModelMatrix = 世界空间
世界空间 * ViewMatrix = 观察空间
观察空间 * ProjectionMatrix = 屏幕空间
得到的结果: 这个顶点在屏幕空间里的二维坐标,(x, y) 加一个深度的量z
这里的二维坐标x,y就是最终要写入的位图的像素坐标。
现在这个顶点走出了顶点处理器,它知道了自己要去哪儿。
![dc4b82bf30296e6a98004107c1b335ac.png](https://i-blog.csdnimg.cn/blog_migrate/1b60345207bda8285d5f839b4d42346b.jpeg)
接下来是栅格化处理。 因为之前有三个顶点分别进入了三个顶点处理器处理,它们分别得到了一个二维坐标。 于是栅格化处理器拿到三个二维坐标。 它的工作是把这三个二维坐标连成三角形区域,给出这个区域里的像素(fragment)。
它'喷'出一个个fragment, 各带着自己的屏幕坐标信息, 这个坐标是栅格化处理器根据前一步输出的三个顶点的屏幕空间坐标做线性补间(interpolation)得出的。
它还带着其他信息比如uv贴图坐标或颜色, 这也是根据三个顶点的uv坐标或颜色做线性补间得出的。
所以记住栅格化做了很多补间工作,它把三个顶点的那些不连续的量(坐标,uv, 颜色)线性得分摊到区域内的像素里。
如果顶点带有用于自定义的量(varying)比如weight,那这些量也会被补间,变成fragment的输入varying量。
很多人会好奇片元(fragment)和像素(pixel)有什么区别,好像都是像素的意思。 在这个例子里,只有一个三角形要渲染,这个三角形所产生的每个fragment都会留在留在最终的渲染画面里,那这fragment和pixel是等价的。 但想象一下如果有多个不透明的三角形,且在视线方向相互叠加,那靠后面的三角形所产生的fragment被前面的三角形挡住,也就深度测试判定为丢弃的话,那这个fragment就不会留在最终的渲染画面里。 所以fragment可以理解为临时的pixel,或渲染过程中打得草稿。
![49f7f10438ab839a1c9eb182b99b0705.png](https://i-blog.csdnimg.cn/blog_migrate/e6041a05df16789a85cd864f78eb757f.png)
现在有一大堆fragment从栅格化处理器里喷出来了, 你能猜到接下来轮到像素处理器了(fragment processor)
类似顶点处理器,像素处理器有很多个,每个每次处理一个fragment, 每个fragment特有的信息是attribute,比如gl_FragCoord, 就是该片元的屏幕坐标; 或gl_FragColor,这是被补间的顶点颜色。 还有用户定义的varying输入量,比如前面说的weight。
它还知道一些uniform量,通常是灯光的位置角度强度颜色信息, 还有用户定义的如时间信息。
像素处理器最关键的任务是给它拿到的fragment上色。 比如通过灯光的信息与fragment的法线信息得出光照效果, 最终把颜色写入gl_FragColor。 所以在fragment shader的最后都会看到 gl_FragColor = ...
可以看出一个个无色的fragment进入片元处理器,出来的是一个个上了颜色的fragment。
它的功能就是给没有颜色信息的片元着色(shading),所以叫shader。
每个fragment知道了自己的二维像素坐标,也知道了自己的颜色。 那就可以各就各位放进最终的渲染画面了。
推荐阅读:
3D Graphics with OpenGLwww.ntu.edu.sg![f37d494c45746c77f5044bdf1dadd804.png](https://i-blog.csdnimg.cn/blog_migrate/bb71d02f521b76a6d981f663b5b74036.jpeg)