文章目录
前言
本文为GAMES101现代计算机图形学入门 的学习笔记系列。
我们的系列笔记将分为两部分:
- 课堂笔记
- 作业
原课程为2020年2月闫令琪所教授的 GAMES101 现代计算机图形学入门。
课程主页:https://sites.cs.ucsb.edu/~lingqi/teaching/games101.html
(幻灯片和课程录像均在此处)
课程共计22节。作业共计8次。
针对人群:计算机图形学入门新手
教材:
Steve Marschner and Peter Shirley的"Fundamentals of Computer Graphics"
第三版或更新版本。目前无官方中文版。
民间翻译:https://www.stubbornhuang.com/1812/
笔记目录
笔记目录
2022-6-11
阅读材料:第 10 章(Surface Shading), 第 10.2 节;第 17 章(Using Graphics Hardware), 第 17.1 节
本节课内容:
- Blinn-Phong(继续上节)
– 高光、环境光 - 着色频率
- 渲染管线
- 纹理映射(部分)
Blinn-Phong Shading
上节课讲了Blinn-Phong 的漫反射项(Lambertian Diffuse)
还剩下高光和环境光没讲。
高光(Specular Light)
漫反射是与视角无关的,它只和光源与物体的相对位置有关。而高光不仅和光源和物体的相对位置有关,还和观察的角度有关。
具体来说,高光就是镜面反射, 即当观察者的视线与反射光线恰好处在同一角度(或者相距很小角度)的时候,会看到很亮的光点。
如图所示。
那么,做法就很简单了:我们找到反射光线的方向向量(R),再给出一个相机视线的方向向量(v)。我们只要看这两者是不是夹角很小即可。
如何定义夹角呢?显然用cos比较方便。而在第二节课中我们讲过,单位向量的夹角余弦恰好就是单位向量点乘。
所以,我们只要点乘R与v就好了。
R ⃗ ⋅ v ⃗ \vec R\cdot \vec v R⋅v
这样,就能衡量出两个方向夹角的大小。
- 但是要求出反射光线R向量,虽然不是不可以,但是比较麻烦,计算量大。所以Blinn提出来了一个天才般的想法:
不去计算R与v的夹角余弦,而是去计算v和入射方向I的角平分线 与 表面法线的夹角余弦
于是就定义了这个角平分线为h
h = b i s e c t o r ( v , I ) = v + I ∣ ∣ v + I ∣ ∣ \mathbf{h} = bisector(\mathbf{v},\mathbf{I}) =\frac{\mathbf{v}+\mathbf{I}}{||\mathbf{v}+\mathbf{I}||} h=bisector(v,I)=∣∣v+I∣∣v+I
这个h被称为:半程向量。
顺带一提,如果不用这个技巧的模型,被称为Phong模型。利用这个技巧的,才叫Blinn-Phong模型。
Blinn-Phong模型大大简化了计算。因为计算反射方向是一件很繁琐的事情(想想看找到某个向量以另一个向量为轴的对称向量,并不是一件很容易的事情)。
- 另外,还需要注意的是。由于我们不允许出现负值(夹角余弦出现负值代表着角度大于90度了,而我们的范围也就是0-90度),所以要加上一个max(0, xxx)。当小于0的时候,取0。
3. 我们观察这个公式,还会发现一点:即max之后还有个alpha次方。这是为什么呢?
这是因为cosin函数太平缓了,不够陡峭,所以高光的范围太大了,为了让高光范围小一点,我们可以取alpha次方。(alpha一般可以取100~200之间)
变动p和ks,我们得到的效果如图所示。
(注:Ls的s代表的是specular)
环境光(ambient)
环境光就用很简单的一个系数代替就好了。这是非常简化的模型。
实际上,若是用更复杂的模型,可以看看全局光照技术。
Blinn-Phong整体效果
将三项加和(漫反射+高光+环境光)
得到效果如下
其中La的a代表ambiant
Ld的d代表diffuse
Ls的s代表specular
着色频率(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