着色频率(Shading Frequencies)
所谓的着色频率,其实就是着色的精细程度。
我们介绍三种着色频率:
- 逐三角形着色。即每个三角形指定一个颜色值。Flat Shading
- 逐顶点着色。即每个顶点指定一个颜色值。Gouraud Shading(需要插值)
- 逐像素着色。即每个像素指定一个颜色值。Phong Shading(需要插值)
后两者需要插值。
以下三个模型,三角形的个数是完全一样的。唯一不一样的就是着色频率。可以很明显的看出,从左到右,随着着色频率变得精细,光暗效果更佳。
既然需要插值,我们就需要知道怎么插值。显然,Blinn-Phong模型中需要用到法线。所以我们先说说法线是怎么插值的。
法线插值
顶点法线
面的法线是好说的。我们总是为每个面定义一个法线。问题是,在顶点处如何定义法线呢?
例如下面这个二维的几何体。
在边(三维中就是面)和边的交界处,顶点的法线如何定义?
再例如三维的情况
面片衔接的地方,如何定义法线?
其实很简单,就是把邻接的面片的法线加起来求和(再归一化)就行了。
其实也还可以更精细一些,就是按照面积加权。
像素法线
这里要用到重心坐标。这个我们后面再讲。
图形(实时渲染)管线(Graphics (Real-time Rendering) Pipline)
渲染管线是对之前所有内容的一个总结。也就是从点坐标到在屏幕上绘制图形的全流程。
如下图所示:
- 顶点处理
- 三角形处理
- 光栅化
- 片段(像素)处理
- 帧缓冲区操作
1 顶点处理(MVP变换)
顶点处理,其实就是把一些坐标点,通过模型(模型本身的旋转拉伸剪切)、视图(相机的摆放)、投影(透视投影到正交投影再到正则立方体)。以及视口变换(正则立方体到屏幕)
2 三角形处理
这一步其实不需要我们来处理。我们得到模型的时候,一般就得到了顶点的连接关系。也就是指定哪三个顶点连成一个三角形(想一想OBJ文件格式就知道了)
3 光栅化
光栅化就是
找哪些像素是在三角形内部的。如果在内部,就按照该三角形的颜色值为像素指定颜色。
4 片段处理
片段(fragment)其实就是像素(pixel),只是叫法不同罢了。
首先,做一个深度检测算法,一般是Z-Buffer。按照物体的遮挡关系绘制图形。
其次,根据光源、相机、物体三者的相对位置,绘制物体光暗的变化,也就是着色(shading)。可以使用Blinn-Phong Shading
最后,我们还可以在物体上绘制不同的纹理。例如木头就是木头的纹理,金属就是金属的纹理。不同的物体材质有不同的纹理。(我们后续将会着重讲纹理)
Shader程序
对我们程序员来说,GPU厂商和操作系统厂商已经为我们写好了大部分的固定流程。我们无需关心许多千篇一律的流程(因为代码都是一样的,已经被写好了)。而GPU厂商和操作系统为我们留出了一些接口,即API。这就相当于我们可以写许多用户自定义的程序片段。这些程序片段就是shader 程序。这些API就是Shading Language。
有两种shader程序:
- vertex shader
- fragment shader
顾名思义,vertex shader就是允许你在顶点处理的步骤上填写自定义的程序。fragment shader就是允许你在像素处理的时候写自定义的程序。
由此,我们就可以实现千变万化的效果。
流行的API有:
GLSL(OpenGL Shading Language)(跨平台)
HLSL(High Level Shader Language)(Windows专用)(DirectX)
SPIR(Standard, Portable Intermediate Representation)(跨平台)(Vulkan)
Metal Shading Language(苹果专用)
我们只要记得:shader程序就是一段运行在GPU的代码即可。它是硬件和操作系统厂商为你写好了框架,只要等你填上去的那个“填空题”。因为它运行在GPU上,所以天然就是并行的,无需指定。
如图就是一段GLSL的 fragment shader程序
其中uniform是外部程序传入shader的变量
varying是vertex和fragment shader之间数据传递的
他们都可以暂且认为是个全局变量。
shader程序可以在shadertoy网站练习
下面这个是Inigo Quilez的招牌shader程序
纹理映射
对于不同的物体来说,显然其上的图案是不同的。
比如气球和木地板,他们的图案就完全不同。我们就称,他们的纹理不同。
但是相同材质的物体,显然纹理是相同的。因此我们要能够在相同 材质的不同物体上能够复用相同的纹理。
通常,我们先绘制出2D纹理来,然后把它映射到3D物体上。这个过程就是纹理映射。
例如我们把地球表面可以展开成一个二维的地图,也可以把二维的地图给贴到三维的地球表面上去。我们可以很形象地将纹理映射称之为贴图。
纹理映射并非一件简单的事情。
我们要实现的实际上是2维平面到3维曲面的映射关系。他们之间一一对应。
在此处,我们以三角形为纹理的最小单元。我们要做的,其实就是找到二维平面上的三角形的坐标值与三维曲面上的坐标值的对应关系。
如图所示。我们定义二维平面的坐标用(u, v)表示。这是因为三维曲面坐标已经用(x, y, z)表示了。所以我们用uv这两个字母。
我们只要记得: (u,v)不过是三维曲面展开到2维平面上的时候,我们自己给定义的坐标值而已。
上图中,红色越多代表u坐标越大,绿色越多代表v坐标值越大。
为了统一标准。我们总是让uv值在[0,1]内。
只要知道每个三角形对应的uv值,我们就做好了纹理映射。
另外
贴图是可以不断重复的,例如下图。
就像贴瓷砖一样。美术师只要巧妙地绘制纹理,让重复的地方看不出来接缝。纹理贴图就可以无限地循环复制。
这种就被称之为tiled texture