渲染的流程
我们可以利用空间变换将三维世界中的物体变换到屏幕空间中,但是我们将如何给每个像素点着色呢?举个例子,我们有一个三角形,我们给定了三个点的坐标信息,我们怎样能将其绘制到屏幕上呢?这个过程就是下面的渲染流程
上图显示了给定了顶点信息,GPU是如何一步一步的将其渲染到图像中的。
这里我们只看Rasterization这一步,看这一步是如何做的。假设我们需要渲染一个三角形,如下所示:
我们很自然的只需要判断三角形覆盖了哪些像素点,然后给这些像素点着色就可以了啊。
利用我们之前的公式很容易的就可以解决。
1 | color = texture(image, uv) |
但是说起来很容易,做起来却不是那么简单的,这里有两个问题:
- 如何判断像素点在三角形内部
- 三角形内部的点应该渲染成什么颜色
下面我们来一个一个的解决。
光栅化的输入输出
首先我们来分析一个这个着色函数
1 | color = texture(image, uv) |
这个方程需要三个输入条件:
- 三角形覆盖了哪些pixel
- 每个pixel的UV坐标
- 纹理贴图
纹理贴图给定了,那么需要光栅化解决的就是前两个问题。
因此如果把光栅化当做一个函数来看的化,那么他的输入就应该是:
- 每个顶点的位置坐标
- 每个顶点的uv坐标
而他的输出则是:
- 哪些pixel需要绘制
- 每个pixel的顶点坐标
在vs中已经给出了每个顶点的位置坐标和uv坐标,这就是光栅化的输入。下面我们以三角形为例子来看下如何得到它的输出
一个几何问题
给定三个点,构成了三角形,如何判断其他的点在三角形内还是三角形外?
实际上这就是需要解下面的一个方程:
(边的下方)
(边的下方)
(边的上方)
(实际上这只是一个方法,还可以用向量叉乘,重心计算等方法)
从framebuffer左上角起,对于每个pixel都计算一遍,然后将其送入到fs中。如果pixel在三角形内,则需要计算他的uv坐标,然后再纹理采样,输出起颜色值,如果不在三角形内,则不进行处理。
怎样能计算的更快一点?
正常来讲,我们对每个pixel都需要计算上面的三个方程式,也就是每个pixel都需要做6个乘法,6个加法。那么能不能简化一下计算呢?
我们看对于pixel而言:
这样我们在计算了第一个点后,其他的所有的点就只需要3次加法就可以了。点则是一个种子点。
思考一下问什么这样做可以?
- 离散
- 相邻
- 三角形的几何特性
还能更快一点么?
更快一点,那么我们可以以block为单位,按照区域进行筛选,如果区域不与三角形相交,那么区域内的所有点都不与三角形相交,这样就可以排除掉这些点了。
还能不能更快一点呢?
实际上可以按照层级进行block,这样可以进一步的优化。
如何计算UV坐标
我们有了三角形的三个顶点的UV坐标,那么三角形内部的其他的pixel点的UV坐标该怎样计算呢?这里我们使用插值的方法。
公式里面的表示区域的面积。面积的计算比较复杂,实际上我们上面的函数可以推到为用三角形边的线性方程来计算的公式,如下所示:
这样就简化了很多。
插值计算可以固化到硬件中,因此这一步骤可以非常快,一般来说它不会影响性能。
插值后的结果作为fs的输入。
相应的硬件模块
- TS :由顶点坐标和UV坐标计算得到线性方程和插值公式
- RAST :由线性方程进行光栅化,向后面的模块输出block mast
- VARY :根据插值公式在三角形的覆盖点上(RAST结果)进行插值,插值结果送给后面的运算shader的模块作为输入。
后记
实际上插值还可以利用重心坐标如下计算: