第一次课(2024.9.3)费广正老师为大家重新概括了一下光栅化渲染光线的流程。
其中涉及到了一些小要点和比较有趣的问题,这里记录一下。

光栅化渲染管线中几个主要阶段的重要任务

VertexShader

顶点着色器主要负责**坐标系的转换**。将模型顶点数据从本地坐标经过MVP矩阵变换到世界空间World Space(裁剪空间Clip Space、屏幕空间 Screen Spcae等)

Rasterization

光栅化是对三角面片的离散化处理。光栅化主要有两个任务,分别是
  • 填充 填充屏幕中的像素颜色
  • 插值
    两个层面:
    1)对像素点间的颜色进行线性插值(Breshanm画线算法,双线性插值)
    2)对顶点数据进行插值(颜色、法线、深度等,但纹理坐标除外,因为近大远小,不同深度的模型纹理坐标有差距)

FragmentShader

计算光照(直接光照+间接光照 = 全局光照),根据光照模型进行着色。

图像混合阶段

可能会有同一像素有不同颜色缓冲的结果,此时需要根据先前设置的方法进行颜色融合(例如Blend One One等)

Unity Shader 结构

SubShader不是Shader的子。
SubShader的执行过程:不是选择,而是自上至下按顺序检查当前的硬件条件是否支持当前的SubShader,若支持则执行,否则检查下一SubShader直到进入FallBack调用Unity默认的Shader。

有向无环图 Directed Acyclic Graph, DGA

https://blog.csdn.net/wireless_com/article/details/119306172

法线的坐标系转换

参考:
https://blog.csdn.net/zhetianyun/article/details/103054640

以Unity Shader为例,在将顶点法线转换到世界空间坐标系时需要乘以float(3x3)unity_WorldToObject矩阵,而不是M变换矩阵。

1
2
3
4
5
6
7
8
9
10
// Transforms normal from object to world space
inline float3 UnityObjectToWorldNormal( in float3 norm )
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
return UnityObjectToWorldDir(norm);
#else
// mul(IT_M, norm) => mul(norm, I_M) => {dot(norm, I_M.col0), dot(norm, I_M.col1), dot(norm, I_M.col2)}
return normalize(mul(norm, (float3x3)unity_WorldToObject));
#endif
}

下面来解释一下为什么。

为什么非等比变换会使法线不再垂直于模型表面

结论:如果使用M变换矩阵变换法向量,则非等比缩放变换会使变换后的法向量不再垂直于模型表面。
下面给出一个小证明:
以一个三角面片为例,假设两条边的向量为$ \vec a , \vec b$,则三角面片的法向量为$ \vec n = normalize(\vec a \times \vec b) $。
我们只对三角面片做缩放变换。假设该矩阵M为
$$ \begin{bmatrix}
{s_{1}}&{0}&{0}&{1}\
{0}&{s_{2}}&{0}&{1}\
{0}&{0}&{s_{3}}&{1}\
{0}&{0}&{0}&{1}\
\end{bmatrix}$$
对于
等比变换
,$ s_{1} = s_{2} = s_{3} $使用经过缩放的两条边做叉乘得到缩放后的法向量$ \vec n^{'} = normalize(s\vec a \times s\vec b) $,可以发现缩放后的法向量和原法向量做缩放变换后相等。
对于非等比变换,使用经过缩放的两条边做叉乘得到缩放后的法向量,可以发现缩放后的法向量和原法向量做相同缩放变换后的变换不相等。

对法线做变换的正确方法

https://blog.csdn.net/zhetianyun/article/details/103054640
对于切线可以使用