向量

  1. 点乘
    $$
    \vec{a}\vec{b}=|a||b|cos\theta
    $$
    用途:求角度或者投影,两个向量多么接近,以及两个向量的前后关系

  2. 叉乘
    $$
    |\vec{a}\times\vec{b}|=||a|||b||sin\theta
    $$
    用途:得到垂直于 a,b两向量构成的面的 向量,并且叉乘的模是两向量平行四边形的面积。可以判断两向量内外或者左右关系。

    例如game101叉乘

    如何判断B在A的左侧?

    可以A叉乘B,看得到向量是正还是负,再通过右手螺旋定则,就可以了。

    这是一个二维生三维的升变公式。

    平面坐标中,如何判断P点在ABC内部?

    AB叉AP,BC叉BP,CA叉CP 三者结果若全都一个方向则在内部,反之不在

    注意:得到的向量的方向满足于右手螺旋定则(某些的图形Api可能选左手,视情况定),因此叉乘不满足交换律,因为结果向量的方向不同。

矩阵

  1. 矩阵乘法,不存在交换律,结合律分配律存在

    image-20221024233926616

    A矩阵列数和B矩阵行数相同,再进行A行向量和B列向量的点乘。

  2. 矩阵转置
    $$
    (AB)^T =B^TA^t
    $$

  3. 旋转矩阵是正交矩阵,即逆矩阵就是矩阵的转置。

  4. 单位矩阵,矩阵的逆 忘了的话就看线代;

二维变换

线性变换(Linear Transforms)

如果可以用一个相同维度的矩阵乘以你的输入坐标得到输出坐标,那我们就认为这是线性变换。例如旋转,缩放。

求线性变换公式规则是什么?举一个求旋转矩阵公式的例子

变换旋转

注意 这里是绕原点逆时针旋转

图中物体旋转,所有点都满足相同的旋转公式,所以可以找特殊点推导其旋转公式
$$
假设四边长皆为1,逆时针旋转\theta, 则右下角坐标变为,(cos\theta,sin\theta),存在
$$

$$
\begin{bmatrix}
xˋ\\

\end{bmatrix}
=
\begin{bmatrix}
a&b\\
c&d
\end{bmatrix}
\begin{bmatrix}
x\\
y
\end{bmatrix}
带入 x=1,y=0 ,xˋ=cos\theta,xˋ=sin\theta
$$

则有
$$
a+b0=cos\theta ,c+d0=sin\theta
$$

即求出 a和c ,同样我们再带入第二个点,比如左上角(-sin theta,cos theta),即可求出 b,d
$$
\begin{bmatrix}
xˋ\\

\end{bmatrix}
=
\begin{bmatrix}
cos\theta&-sin\theta\\
sin\theta&cos\theta
\end{bmatrix}
\begin{bmatrix}
x\\
y
\end{bmatrix}
$$
Attention:旋转矩阵是正交矩阵,逆矩阵与转置矩阵相同,带入个顺时针旋转theta角就明白了

也可以这样理解 :旋转矩阵中的值即为x,y轴的单位向量旋转后的值,三维也是同样的

没必要记,毕竟容易推导;举一反三,什么对称,缩放,切变公式也就往后稍稍了;

齐次坐标(Homogeneous Coordinates)

为什么齐次坐标?

当我们想着把平移用矩阵的形式表示时,公式是
$$
\begin{bmatrix}
xˋ\\

\end{bmatrix}
=
\begin{bmatrix}
1&0\\
0&1
\end{bmatrix}
\begin{bmatrix}
x\\
y
\end{bmatrix}
+
\begin{bmatrix}
t_x\\
t_y
\end{bmatrix}
$$
所以平移并不是线性变换,这是一种仿射变换(Affine Transformations)。

我们不想要平移成为特例,而是想着把平移旋转缩放等一起概括,所有有了齐次坐标。

齐次坐标

二维坐标中,我们人为的加入了第三个坐标 w,并且定义为 w只能=1或0;

如果w=1,坐标表示为2D的一个点,若等于0,则表示无限长的2D向量。

因此如果我们用齐次坐标来表示平移时,就会有以下式子
$$
\begin{bmatrix}
xˋ\
yˋ\

\end{bmatrix}
=
\begin{bmatrix}
1&0&t_x\\
0&1&t_y\\
0&0&1
\end{bmatrix}
\begin{bmatrix}
x\\
y\\
1
\end{bmatrix}
=
\begin{bmatrix}
x+t_x\\
y+t_y\\
1
\end{bmatrix}
$$
发现仅用矩阵乘法就可以很好的表示位移,就很舒服。****

w的定义规则有一定原因

  1. 为了保证向量不变性,即不管怎么平移向量,向量大小即方向不改变,我们使其w=0

  2. 为了符合点与点之间,向量与向量之间,点与向量之间的运算关系

    vector+vector=vector 向量之和仍为向量 其中w一直是0
    vector+point=vector 点加向量表示点的平移,w=1+0=1符合
    point-point=vector 两点之差为向量,w=1-1=0符合
    那两点之和是什么?

    有一点point(x,y,w),我们将w转为1,同时除以w得point(x/w,y/w,1),加上另外一点(x`/w,y`/w,1)两点相加后得到
    $$
    \frac{x+x}{w}, \frac{y+y}{w},
    2
    $$

    w仍要为1,所以再除以2得到
    $$
    \frac{x+x}{2w}, \frac{y+y}{2w},
    1
    $$
    显然,得到了一个中点。

    所以在齐次坐标中,两点相加表示两点的中点.

    并且齐次坐标系下,点乘以任一非零实数仍为相同的点。

在齐次坐标下的缩放旋转和平移

齐次坐标表示

组合变换

组合变换

如图,显然这个物体是经过两次变换到新二张图,又由于矩阵乘法不满足交换律,所以是先旋转还是先平移就会有区别。

通过计算得知,先旋转再平移是符合结果的;
$$
T_{(1,0)}\cdot R_{45}
\begin{bmatrix}
x\\
y\\
1
\end{bmatrix}
=
\begin{bmatrix}
1&0&1\\
0&1&0\\
0&0&1
\end{bmatrix}
\begin{bmatrix}
cos45&-sin45&0\\
sin45&cos45&0\\
0&0&1
\end{bmatrix}
\begin{bmatrix}
x\\
y\\
1
\end{bmatrix}
$$
注意,组合变换公式的计算顺序是从右到左的,即先把输入坐标与右一 到右二 直到左一3x3矩阵逐个计算

当然,矩阵乘法满足结合律1,我们也可以先计算所有变换矩阵,再把其结果与输入坐标进行计算。

当我们要绕非原点进行旋转时一般是将图形平移到旋转点为原点,再绕原点旋转,然后再平移回去即可

组合变换表示为
$$
T_{(c)}\cdot R_{(\theta)}\cdot T_{(-c)}
$$

三维变换

右手坐标系的情况下。

多了一个维度Z,其他与二维变换同样,齐次坐标规则也一致
$$
3D
point =(x,y,z,1)
$$

$$
3D \ vector =(x,y,z,0)
$$

w为一表示点,为0表示向量

旋转矩阵表示

即绕三个轴旋转,这里先只考虑绕单个轴旋转,

  • 点绕x轴旋转,即点 x轴坐标不变(则第一行为1,0,0,0),且y,z旋转后坐标与x轴无关(则第一列为1,0,0,0),当然也可以选择几个特殊点计算求得,所以其旋转矩阵可写为
    $$
    R_{x}(\alpha)=
    \begin{bmatrix}
    1&0&0&0\\
    0&cos\alpha&-sin\alpha&0\\
    0&sin\alpha&cos\alpha&0\\
    0&0&0&1
    \end{bmatrix}
    $$

  • 点绕z轴旋转,同上规则
    $$
    R_{z}(\alpha)=
    \begin{bmatrix}
    cos\alpha&-sin\alpha&0&0\\
    sin\alpha&cos\alpha&0&0\\
    0&0&1&0\\
    0&0&0&1
    \end{bmatrix}
    $$

  • $$
    R_{z}(\alpha)=
    \begin{bmatrix}
    cos\alpha&0&sin\alpha&0\\
    0&1&0&0\\
    -sin\alpha&0&cos\alpha&0\\
    0&0&0&1
    \end{bmatrix}
    $$
    所以三维空间内任意一个绕任意轴的旋转都可以由绕xyz旋转组合而成。
    $$
    R_{(\vec{n},\alpha)}=R_{(\vec{x},\alpha x)}\cdot R_{(\vec{y},y\alpha)}\cdot R_{(\vec{z},\alpha z)}
    $$
    而由于其分解和运算的过程很麻烦,就有了罗德里格斯旋转方程。

罗德里格斯旋转方程(Rodrigues)

PASS~

三维到二维

从三维到二维有一个既定的过程,举一个现实中的栗子,摆拍

摆拍首先得把人和物带到拍照的位置,就是平移缩放旋转等操作,这就是模型变换(Model Transformation).

模型变换的目的在于得到模型的世界坐标(模型空间到世界空间,摄像机知道模型的相对坐标)

摆好相机的位置和相机的朝向,这就是 视图变换(View Transformation).

视图变换的目的在于把物体跟随相机移动的一个便于计算的位置,为了投影变换。

然后开始拍照,就存在一个三维到二维投影的过程,这就是投影变换(Projection Transformation).

三个合称MVP变换,得到的结果为MVP变换矩阵

视图变换

视图变换目的在于把所有物体变换到目标位置。这里得变换矩阵是根据摄像机变换得来的。

相机基础属性有三个:

  1. 位置 e(x,y,z)
  2. 朝向 g
  3. 相对相机的正上方向量 t

我们要摆放相机,这里有一个约定俗成的规定:

相机位置位于(0,0,0),朝向为-z(0,0,-1) ,正上方向量为y(0,1,0),

为什么,因为好处多多。

所以我们需要进行以下变换

  • 旋转g到**-z**
  • 旋转ty
  • 旋转g × tx(1,0,0)
  • 将相机坐标e平移到原点

首先平移矩阵最好写
$$
T_{(VIEW)}=
\begin{bmatrix}
1&0&0&-x_e\\
0&1&0&-y_e\\
0&0&1&-z_e\\
0&0&0&1
\end{bmatrix}
$$
然后就是三个旋转,

这里为了计算方便:选择逆向思维,我们先求-z到g,y到t,x到g x t的 这三个单位向量的旋转矩阵,得到的结果组合起来就是目标旋转矩阵的逆矩阵,然后又因为旋转矩阵是正交矩阵,所以将结果转置,就是我们要的目标矩阵了.

又因为,旋转矩阵中的值即为x,y,z三个轴的单位向量旋转后的值

所以计算结果逆矩阵为
$$
R_{VIEW}^{-1}=
\begin{bmatrix}
x_{\vec{g}\times\vec{t}}&x_\vec{t}&x_{-\vec{g}}&0\\
y_{\vec{g}\times\vec{t}}&y_\vec{t}&y_{-\vec{g}}&0\\
z_{\vec{g}\times\vec{t}}&z_\vec{t}&z_{-\vec{g}}&0\\
0&0&0&1
\end{bmatrix}
$$
转置一下就是目标旋转矩阵了
$$
R_{VIEW}=
\begin{bmatrix}
x_{\vec{g}\times\vec{t}}&y_{\vec{g}\times\vec{t}}&z_{\vec{g}\times\vec{t}}&0\\
x_\vec{t}&y_\vec{t}&z_\vec{t}&0\\
x_{-\vec{g}}&y_{-\vec{g}}&z_{-\vec{g}}&0\\
0&0&0&1
\end{bmatrix}
$$

所以视图变换矩阵
$$
R_{VIEW}T_{(VIEW)}=
\begin{bmatrix}
x_{\vec{g}\times\vec{t}}&y_{\vec{g}\times\vec{t}}&z_{\vec{g}\times\vec{t}}&0\\
x_\vec{t}&y_\vec{t}&z_\vec{t}&0\\
x_{-\vec{g}}&y_{-\vec{g}}&z_{-\vec{g}}&0\\
0&0&0&1
\end{bmatrix}
\begin{bmatrix}
1&0&0&-x_e\\
0&1&0&-y_e\\
0&0&1&-z_e\\
0&0&0&1
\end{bmatrix}
$$

投影变换

投影变换分为两种

  • 正交投影(Orthographic projection)
  • 透视投影(Perspective projection)

两者共有的属性

  • Near clip plane 和Far clip plane

    两者分别代表摄像机看见的最近平面和最远平面,两平面间的空间就是我们能看见的

    正交投影为这个空间近似于长方体,透视投影则时像个椎体,成为视锥体

  • 由于之前的视图变化,使得计算更加简便

  • 摄像机看向z还是-z不同图形软件可能不同,如opengl就是右手坐标系,unity是左手坐标系

    投影变换判断前后时若再右手坐标系下,越近的物体,z越大。

之前开发2d游戏时对于视角的探索已经让我对两者有了很清晰的认识,现在来学习怎么实现的

正交投影

正交投影

如图 正交投影的特点是没有透视关系,近切面与远切面大小相同。

可视空间内的物体x,y坐标不变,将z坐标舍弃,形成一个二维平面,再将平面移动拉伸到一个左下角为(-1,-1),右上角为(1,1)的矩形中。

为什么要映射在-11的矩形内,为了后续计算更方便,是一个约定俗成的东西,相当于我们拍照,规定洗出的照片都得是2×2大小的,学opengl时学到一个标准化设备坐标系,范围也是-11,两者也对应起来了。

正交投影通俗做法就是是:就是将空间中的一个立方体(即长方体,设为 S )变换成一个标准立方体canonical cube) 。

何为标准立方体?即以原点为中心,边长为2的立方体,也就是立方形在x,y,z三个轴上都是从-1到1。

整个变换过程就是我们的正交投影变换,其对应矩阵即为正交投影变换矩阵。该变换我们可以分为如下两步(这也体现了视图变换的重要性,使得我们做投影变换变得很简单):

  1. 平移变换,将长方体S平移到原点。
  2. 缩放变换,将长方体S的长宽高缩放成2

所以有正交投影变换矩阵(r,l,t,b,n,f 两两成对,分别为 切面x,y,z最小及最大坐标);
$$
M_{ortho}=
\begin{bmatrix}
\frac{2}{r-l}&0&0&-x_e\\
0&\frac{2}{t-b}&0&-y_e\\
0&0&\frac{2}{n-f}&-z_e\\
0&0&0&1
\end{bmatrix}
\begin{bmatrix}
1&0&0&-\frac{r+l}{2}\\
0&1&0&-\frac{t+b}{2}\\
0&0&1&-\frac{n+f}{2}\\
0&0&0&1
\end{bmatrix}
$$

这里遗留了一个问题:没了z坐标 ,怎么判断物体的前后?

透视投影

透视投影

与正交投影不同的是,透视投影会有近大远小的现象,这种投影更有真实感。从图中我们可以看出远切面要大于近切面,因此我们摄像机所观测的区域不再是一个长方体,而是变成了一个四棱台(即四棱锥去掉顶部),也就是我们常说的视锥体(Frustum)。

所以透视投影是个什么过程呢?

它分为两个过程

  • 第一步是将视锥体压缩成长方体的过程
  • 第二步是对长方体执行正交投影变换的过程、

那么我们只需要计算出第一步的变换矩阵,然后将它乘以正交投影变换矩阵,即可得到我们的透视投影变换矩阵了。

第一步个人理解就相当于把视锥体切分成n个截面,把每一个截面都压缩成近切面的大小。

第一步有以下规定,

  • 近切面永远是不变的,四个顶点都是设置好的常量
  • 远切面的z值不论变换前后都是不变的。
  • 透视中心点是不会变化的

所以该如何挤压呢?我们以挤压y坐标为例

y坐标投影

如图,我们从竖切面下手,取压缩面上的一点(x,y,z),再取近切面上的对应点为(x`,y`,z`),设摄像机到近切面的距离为n(已知常量),

我们要求y`的表达式,这里有很明显的一对相似三角形,存在关系
$$
y=\frac{n}zy $$ 同样的道理,对xz切面作近似三角形,得出x的表达式
$$
x`=\frac{n}zx
$$

因此在齐次坐标下就有了如下转换过程
$$
\begin{pmatrix}
x\
y\
z\
1
\end{pmatrix}
=>^{相似}
\begin{pmatrix}
\frac{n}zx\\
\frac{n}zy\\
?\\
1
\end{pmatrix}
=>^{*z}
\begin{pmatrix}
nx\\
ny\\
?\\
z
\end{pmatrix}
$$

为什么想到要同时乘z,因为z是变量

有个问题,z坐标压缩后不应该还是z吗?怎么不知道?留待后面说,实际会变大

所以也就存在一个4x4的变换矩阵使得
$$
M_{persp->ortho}^{4×4}
\begin{pmatrix}
x\\
y\\
z\\
1
\end{pmatrix}
=
\begin{pmatrix}
nx\\
ny\\
?\\
z
\end{pmatrix}
$$
计算得到该矩阵
$$
M_{persp->ortho}^{4×4}=
\begin{pmatrix}
n&0&0&0\\
0&n&0&0\\
?&?&?&?\\
0&0&1&0
\end{pmatrix}
。。。。。。
可以是这个
\begin{pmatrix}
n&0&0&0\\
0&n&0&0\\
?&?&?&?\\
0&0&0&z
\end{pmatrix}


$$
这里我们用前者表示该变换矩阵

到了这一步我们还需要搞清楚第三行是什么就完成了。

这里我们带入两点:

  1. Near clip plane上的任意点,设为(i,j,n),它的z值变换后不变依旧为 n。
  2. Far clip plane上的中心点,设为(0,0,f),它的z值变换后不变依旧为 f。

上述式子就变成了
$$
M_{persp->ortho}^{4×4}
\begin{pmatrix}
x\\
y\\
n\\
1
\end{pmatrix}
=
\begin{pmatrix}
nx\\
ny\\
n^2\\
n
\end{pmatrix}
$$

$$
M_{persp->ortho}^{4×4}
\begin{pmatrix}
0\\
0\\
f\\
1
\end{pmatrix}
=
\begin{pmatrix}
0\\
0\\
f^2\\
f
\end{pmatrix}
$$

设第三行值分别为A,B,C,D,得到方程
$$
\begin{cases}
Ax+By+Cn+D=n^2\\
Cf^2+D=f^2
\end{cases}
$$
A,B=0;
$$
Cn-n^2=Cf-f^2
$$

为什么 前两个为0,听了不同的课,感觉对这里的解释大多摸棱两可,个人理解是,为了对所有点都能应用该变换矩阵,同时减少未知因素的影响。

即可算出,C=n+f,D=-nf,因此视锥体压缩为长方体的矩阵为(n,f):

$$
M_{persp->ortho}^{4×4}=
\begin{pmatrix}
n&0&0&0\\
0&n&0&0\\
0&0&n+f&-nf\\
0&0&1&0
\end{pmatrix}
$$

这里就是抓住了之前我们规定的不变常量得到的

所以视锥体内的一点压缩后的z坐标会有变化吗? $$ z=(n+f)*z-nf=nz+fz-nf=(z-f)*n+zf(0>=n>=z>=f)
$$
z-f>0, (z-f)n <0,同时 zf<z<0

因此我们可以得出z`<z,变换后 z坐标 将会变小(右手坐标系),即变换后离摄像机更远了。

屏幕映射 视口变换

结果前面的处理,我们得到了一个三维空间下的标准立方体[-1, 1]³,我们需要将他转到屏幕坐标系下,屏幕有以下特点

  • 屏幕是光栅成像设备,其成像点通常理解为二维数组,数组元素为像素。
  • 屏幕坐标系原点此处为左下角(0,0),定义宽度width,和高度height。
  • 对于屏幕上像素,其位置为(x, y),其像素中心点的坐标是(x+0.5, y+0.5)。

先不管它的Z轴数据(由深度缓冲来处理),屏幕映射需要将X和Y轴 [-1, 1]² 映射到屏幕坐标 [0, width] x [0, height],屏幕原点一般在左下角,通过齐次坐标的矩阵,先将 [-1, 1]² 缩放至 [width, height],再平移使得原点坐标对齐,将标准立方体转换成屏幕空间,变换矩阵为
$$
M_{}^{4×4}=
\begin{pmatrix}
\frac {width}2&0&0&\frac {width}2\\
0&\frac {height}2&0&\frac {height}2\\
0&0&1&0\\
0&0&0&1
\end{pmatrix}
$$

光栅化

光栅化的目的在于将之前输出的数据包括 计算处理得到片元。就是要计算出每一个像素的RGB值。其工作主要是

  1. 图元组装,三顶点读取,两两连接,形成三角形。

  2. 采样,遍历像素点,判断哪些像素位于三角形内

  3. 循环每个三角形,直到结束

为什么是三角形?

这是由其特性决定的:

  • 是最基础的多边形,任何多边形都可由三角形组成
  • 内外定义清晰(叉积)
  • 方便插值运算,通过三个顶点得出三角形内部的属性。(重心坐标)

判断像素位于三角形内

叉积

优化

显然没必要判断所有像素

  1. AABB包围盒

    三角形三个顶点xy的最大最小值,由该四个值形成一个包围盒,只判断包围盒内的像素即可。

  2. 增量横扫

    参考知乎文章三角形光栅化

采样

采样是指:对一个函数,用一组离散值作为输入得到函数的一组离散值作为输出,将函数离散化的过程。

在判断像素是否位于三角形内时,我们利用了像素的概念,这就是采样的运用

而当我们采样频率比不上信号变化的速率时将会导致一些问题,这就是走样

包括锯齿(像素采样)摩尔纹(空间采样)车轮效应(时间采样)

走样

探讨为什么产生走样就要了了解这些关键词:

傅里叶级数展开,傅里叶变换李永乐简单讲解傅里叶和可视化展示形象展示傅里叶变换

二维图片的傅里叶级数展开和傅里叶变化,空域频域和如何品鉴频域图图像傅里叶变换)

图像卷积

简单说下结论:

傅里叶级数意思就是任何周期性函数都可以展开为多个正(余)弦函数的组合,对于周期无法分明的函数我们认为其周期时正负无穷

傅里叶变换结果就是展开的结果频域,当然可以逆变换。

空域卷积等于频域乘积。滤波通过卷积实现

走样本质就是频域发生了重叠导致

走样的解决方案

各种抗锯齿方案主要了解MSAA,有兴趣了解TAA,FXAA,SUPER RESOLUTION

着色

当我们讨论着色器的时候,着色器在想什么?

它在想这个地方我要什么颜色,这个地方明暗是怎么样的?

着色:对一个物体应用一个材质的过程,它不考虑阴影。

光照

这里讨论的是Blinn-Phong模型,简化了反射时的吸收等其他影响要素

light

如图,一个物体的受光一般分为三部分,从上倒下依次是

  • 镜面高光(Specular Highlights)
  • 漫反射(Diffuse Reflection)
  • 环境光(Ambient Light)

光照的局部模型

对于局部一点的光照,通常采用极限思想,即一束极小的光从光源发出,撞到极小的一点反射到观察者眼中。

由上可知其需要三点

  • 光源/入射光线, 定义为单位向量l
  • 反射面/法向量, 定义为单位向量n
  • 观察者/反射光线 ,定义为单位向量v

正如下图

view

漫反射

在量子光学中,光的能量是量子化的,我们称光的量子为光量子

个人对量子光学的理解也只停留在高中物理课上的光电效应等程度上,但也够了

1

如图,漫反射定义为入射光向四面八方反射强度相同的光。反射光互不平行。

虽然这是张平面图,但是反射是立体的,正如尾兽玉爆炸时的扩散。

反射光的强度和入射光相比明显是更小的,这里影响系数是入射光和法向量的夹角θ的余弦(兰伯特定律Lambert’s Law),就像使夏天太阳光直射北回归线,冬天直射南回归线时的强度变化。

我们设入射光强度为I,则反射光强为
$$
Icosθ
$$
此外还有第二个影响要素,在真空环境下,在漫反射光的传播过程中,能量分布相当于是从小球壳到大球壳的传播的过程,根据能量守恒定律,小球壳上能量和大球壳上的能量是相等的,因此,大球壳单位面积上的能量就会更小,这里就存在一个单位面积光强和表面积的反比关系,设观察者离漫反射点的距离为R,则有
$$
Icosθ/r^2
$$

综合以上有漫反射公式
$$
L_{diffuse}=\frac{I}{R^2}max(0,cosθ)
$$
加上物体表面漫反射系数k,并带入n,l,得到最终公式
$$
L_{diffuse}=k_d\frac{I}{R^2}max(0,\vec n\cdot \vec l)
$$

漫反射光强取决于反射面法向量和入射光强的余弦。

高光

着色点材质较光滑时,反射接近镜面反射(但也仅是接近),与漫反射强度分析一致,分两步,反射后的强度衰减,反射光传播中的强度衰减,其中后者是相同的道理,这里需要明白前者。

通俗理解,什么情况下,其强度会更强?那就是反射光越接近理论上的镜面反射光的时候,也就是说,观察者的观测向量越接近镜面反射光时,观测到的高光越强,计算接近程度时用两向量点乘。

高光

如图,定义镜面反射光为m,入射光强为I所以高光反射强度为
$$
I \vec v\cdot \vec m
$$
加上第二步中的公式,以及考虑物体表面高光反射系数k得到Phong模型下的高光公式
$$
L_s=k_s(\frac{I}{R^2})max(0,\vec m\cdot \vec v)^p
$$
这里加个指数P是为了控制高光反射面积的大小和能够看到高光的范围。当p的值越大,反射面积(能看到高光的范围)越小。

Blinn-Phong模型简化了镜面反射光的计算,经验化的转成了通过计算半程向量h和法向量n的接近程度。半程向量就是角平分线向量,四边形法则相加后单位化再即可
$$
\vec h=\frac {\vec v +\vec l} {|\vec v +\vec l|}
$$

所以有高光反射公式
$$
L_s=k_s(\frac{I}{R^2})max(0,\vec n\cdot \vec h)^p
$$

高光反射强度取决于镜面反射光和出射光的余弦,近似的是法向量和半程向量的余弦。

环境光

环境光就是从四面八方反射到来的光,这些光都有各自丰富的故事,很是复杂,这里大胆的假设光强全都一致(后续有全局光照,光追等)
$$
L_a=k_aI_a
$$
通常就是材质的RGB数值。

就这样把三个公式加起来就得到了Blinn-Phong模型的光照公式
$$
L=k_d\frac{I}{R^2}max(0,\vec n\cdot \vec l)+k_s(\frac{I}{R^2})max(0,\vec m\cdot \vec v)^p+k_aI_a
$$

着色频率

分为三种,也就是三种不同的着色算法

  • Flat Shading 逐片元着色

    根据每个三角形的法向量着色。

  • Gouraud Shading 逐顶点着色

    通过计算顶点所关联的面的法向量的平均来求得顶点法向量,再通过对三个顶点着色结果的线性插值计算得到片元的着色.

  • Phong Shading 逐像素着色

着色频率的旋转取决于模型的复杂的(面,顶点的复杂度)

法线计算

面:叉乘

顶点:周围面法向量的平均

三角形内的点:利用三角形重心坐标进行插值,即三角形面上的一点可以由三角形三个顶点来表示,那么三角形内的任意点的属性就可以由三个顶点的属性以重心坐标的形式表现出来。

材质/shader/贴图

材质是一类物体属性的集合用面向对象理解就是一个类,我们可以为其定义一些属性和方法,属性可以在定义类时决定,也可以后面实例化对象时再改,比如反射系数,顶点数据,shader就是定义的函数,用来处理顶点,曲面细分,几何,片元数据且输出数据的函数。

贴图Texture个人理解认为包含两个部分,纹理和映射(UV坐标)

纹理就是图片上的某点的数值(颜色贴图就是RGBA的值,法线贴图就是法线值),映射就决定了贴图的纹理和模型是如何对应的。

一个3D物体的表面有各式各样的着色点,如果我们把它的表面展开,就如下图

纹理

我们把右一图上某一点的纹理应用到了左一渲染图上的对应点,而这个过程,我们就称为纹理映射。右一图上保存着所有着色点的纹理和其他数据,如uv坐标等,这个图我们就称为贴图

纹理映射

4k渲染图对应4k纹理没问题,像素点一一对应,

但如果是1080p的纹理呢?那么像素点映射的纹理坐标会是一个小数,那么到底映射什么纹理就成了问题

比如就近原则,或者双线性插值(周围四个采样点,xy坐标插值)

如果8k纹理呢?也不行,一个像素点就取了多个纹理值,采样频率跟不上信号变化频率,会出现走样问题。

超采样可以解决这个问题,但消耗很多,纹理过滤也还行,但当一个像素对应100x100的纹理呢?周围几个或上十个纹理插值解决不了问题。

所以用什么?MipMap!

MipMap(多级渐远纹理)

mipmap

如上图所示,启用MipMap会生成多级纹理,一张128x128的纹理会渐进生成多级纹理,从第0级128x128开始,每一级是上一级的一半分辨率,直到1x1,最终纹理空间只多了三分之一。

层级 大小
0 128x128
1 64x64
2 32x32
3 16x16
4 8x8
5 4x4
6 2x2
7 1x1

如何应用?

比如:2*2的像素需要映射128*128的纹理像素时,计算一颗像素需要映射到纹理像素为64*64,在Mipmap纹理中里寻找最接近64*64纹理像素的多级渐远纹理(也可以在mipmap层级之间进行插值),并使用此多级渐远纹理进行采样。
$$
log_264=6
$$

所以使用 第五级 2x2 纹理。

如果映射纹理像素为90x90呢?

进行三线性插值

在第0级和第1级纹理分别进行双线性插值得到两个结果

再对这两个结果进行插值运算即可。

优点:

    1. 质量高:避免了像素映射过大纹理时造成的采样频率低和数据频率高造成的失真和摩尔纹,效果比无Mipmap好得多。

​ 2. 性能好:避免了不使用Mipmap下距离远时采样频率低和数据频率高而照成texture cache命中率不高(相邻Pixel采样Texel时uv相差比较大)使性能下降。

缺点:

     1. 占用显存。
        2. 只能进行纹理的正方形范围查询(特向异性过滤)