Decorative image frame

【笔记】【百人计划】图形2.4 传统经验光照模型

图形2.4 传统经验光照模型

  • 经验模型

对真实光照的模拟,简化了真实光照的计算,并且有不错的效果

  • 基于物理理论的光照模型

使用物理的度量和统计方法,效果非常真实,但是计算困难,实现也较困难

image-20220727120611504

一、局部光照模型

只关心直接光照部分

1.1 Lambert余弦定律

光源每秒发出的能量为辐射通量$P$

单位面积接受的光照为辐射通量密度(辐照度)$P/A$

当光源垂直照射的单位面积的辐照度为$E_1 = P/A_1$

当光束以某个角度照射到面积更大的平面上的辐射度为$E_2=P/A_2=P\cos\theta/A_1=E_1\cos\theta$

image-20220727120541155

1.2 漫反射

光线照射到模型表面时被均匀反射到各个方向
$$
C_{diffuse} = C_{light}albedondotL
$$

1.3 镜面反射(高光反射)

光线到达物体表面发生镜面反射,观察视线在反射光线的附近便能够观察到镜面反射。

镜面反射的反射率是根据菲涅尔效应决定的。

通常使用对应的反射贴图描述物体表面的反射率,并使用光泽度(粗糙度,反光度)描述高光范围的大小
$$
C_{specular}=C_{lgiht}*m_{specular}dot(v,r)^m
\r=1-2ndotL
n
$$

image-20220727120611504

1.4 环境光

在局部光照模型中,没有考虑间接光照的影响,为了处理间接光照,引入Ambient环境光
$$
C_{ambient}=Albedo*Ambient
$$
通常使用漫反射的反照率来指示环境光照的反射光量。(只计算了环境光照的漫反射)

1.5 自发光

通常作为单独的一项加入光照模型,一般使用一张发光贴图描述物体自发光

二、经典光照模型

2.1 Lambert光照模型

理想漫反射
$$
L_o = C_{diffuse}= C_{light}albedondotL
$$

2.2 Phong光照模型

ambient环境光+lambert漫反射+高光
$$
L_o=C_{ambient}+C_{diffuse}+C_{specular}\=albedoAmbient + C_{light}(albedo*ndotL+m_{specular}*dot(v,r)^m )
$$

2.3 Blinn-Phong光照模型

在phong光照模型的基础上,使用ndotH代替vdotR
$$
h=normalize(l+r)\
L_o=C_{ambient}+C_{diffuse}+C_{specular}\=albedoAmbient + C_{light}(albedo*ndotL+m_{specular}*dot(n,h)^m )
$$

三、着色方法

着色方法,或者说着色模型,跟光照模型没有任何关系

3.1 Flat着色方法

面着色,顾名思义以每一个面作为一个着色单位。模型数据大多以很多个三角面进行存储,因此也就记录了每个面的法线向量,利用每个面的法线向量进行一次Blinn-Phong反射光照模型的计算,将该颜色赋予整个面,效果如下:

Flat Shading

改进方法:改进方法就是对三角形面的每个顶点进行着色,再对三角形面内的颜色插值,即Gouraud Shading。

3.2 Gouraud着色方法(几乎不用 )

顶点着色,片元着色通过顶点颜色插值

3.3 Phong着色方法

求出三角形顶点法向,法向插值得到三角形内部每个片元的法向,精确计算着色。

作业

1.学习先行版 基础渲染光照介绍(一),并说出能量守恒的理念在基础光照模型中的作用。

在光照模型当中,我们考虑不同的光源类型:环境光照(间接光照)与直接光照,将它们分开看待。实际上它们都是全局光照的一部分,我愿称之为“全局能量守恒 ”。

而对于某种光源,也有对于其本身的能量守恒,我愿称之为“局部能量守恒”。

那么局部能量守恒在光照模型中的表现其实就是
$$
入射光能量=反射光能量 + 表面吸收能量
$$

2.基于能量守恒的理念,自己写一套完整的光照模型,需要包含环境光照。

对于这个作业我的理解是,使用能量守恒的思想去改进传统光照模型 ,而不是直接实现一个cook-torrance模型。

环境光照

那么首先来做一下环境光照,不妨来试一下cubeMap做的环境光。

1
texCUBElod(_CubeMap, float4(normal,0.0));

严格上的IBL,也是要分成漫反射和镜面反射两部分

但是为了简化,就只取漫反射好了,

因为是第一次使用这个功能,在cubemap这里卡了一会儿。

image-20220729233520909

这里对于卷积方式很自然地选择漫反射,并且按理来说,漫反射也不需要mip,于是我就关掉了生成Mip贴图选项,但是这样得到的贴图是错误的。只能把这个选项开启才是正常的。下面同样是第一级的mipmap,可以观察到漫反射和镜面反射的卷积结果还是有所区别的。

image-20220729233752792 image-20220729233806108

但是漫反射的卷积为什么要做mipmap呢?(不做的话结果是错的,不管什么卷积类型,除非是None)

image-20220729234005976

以及这个mipmap的生成方式是什么呢?

查了下文档

image-20220729235546954

也就是说,Convolution的结果是 储存在mipmap里的,所以不开mipmap就会出错。

但是依然没有解决的问题是,==对于diffuse来说,mipmap是怎么生成的?==

这个问题,只能暂时先放一放了,当前的猜想是,这个diffuse并非理想漫反射,而是按照采样的区域去控制mipmap,0层级当然就只有法线方向,最高层级就是理想漫反射。

对于作业来说,就取一个层级的漫反射卷积环境光照好了

但是值得注意的是,在漫反射部分有一个小细节,无论是环境光还是直接光照。
$$
L_o(v) = f_d\int_{l\in\Omega}L_i(l)\cos\theta dl
\ = f_dL_i\int_{i\in\Omega}\cos\theta dl
\ = \pi f_dL_i ,\pi f_d\in[0,1]
\f_d = \frac{albedo}{\pi},albedo \in[0,1]
$$
这需要区别开albedo和漫反射的brdf,有这个pi的区别,因此我们才能直接用albedo*L_i

1
2
float4 indirect_irradiance = texCUBElod(_CubeMap, float4(normal,1.0));
float3 indirect_diffuse = indirect_irradiance * albedo;
直接光照

这部分就要考虑blinn-phong模型的改进了。

我们还是直接看渲染方程
$$
\int_{\Omega^+}f_rL_i\cos\theta d\theta\
=\int_{\Omega^+}(f_d+f_s)L_i\cos\theta d\theta\
=\int_{\Omega^+}f_dL_i\cos\theta d\theta+\int_{\Omega^+}f_sL_i\cos\theta d\theta
$$
这样将brdf拆开了两部分。

在这个过程中,看起来好像没有什么问题,但是这需要关系到如何定义这个f_d,f_s。问题的本质还是漫反射光和镜面反射光的关系,更深入的数学推导也许应该回归到BRDF的函数本质,或者说散射函数,暂时就不去研究了。总之,在这里的表达中,就是反射的光一部分进行了漫反射,一部分进行了高光反射。但是它们的总能量,等于反射光的能量。

在learnopengl中的表达,就是

1
2
float kS = calculateSpecularComponent(...); // 反射/镜面 部分
float kD = 1.0 - ks; // 折射/漫反射 部分

我们可以理解为把这两个比例分配给了光的能量,也可以直接认为 这是属于BRDF的一部分。

就不说cook-torrance了,总之我们先人为提供一个因子。glossy好像是个不错的选择。

1
2
float3 kd = (255.0 - _Gloss)/255.0;
fixed3 color = indirect_diffuse * _EnvScale + diffuse * kd + specular * (1-kd);

漫反射的部分依然和上面一样

1
fixed3 diffuse = _LightColor0.rgb * albedo * max((dot(normal, LightDir)),0);

而对于高光部分。要说它能量守不守恒,也不太好衡量,毕竟能量小于入射能量就行了。无论小多少,它都是一种材质。所以就放在这里不管了。
$$
C_{light}*m_{specular}*ndotH^m
$$

image-20220730013111824 image-20220730013248302

但是这个模型的缺陷在于可以调整的参数太少了,不能表达足够多的材质,最直接的改进部分是对于kd的选择,

1
2
float kd = (255.0 - _Gloss)/255.0;
kd = pow(kd,3);

我们对kd做一些映射,就能够使得在同样的光滑度下,高光的能量占比更大(降低增加gloss减少高光范围的趋势),或者其实就干脆换一个参数来控制kd

image-20220730014709778 image-20220730014811460
总结

调参差不多就玩到这里了,整个光照模型就大概如此。

至于能量守恒的部分,首先是环境光照和直接光照的区分。

剩下的最主要的就是漫反射和镜面反射的能量分配,最后就是考虑光照计算的正确性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fixed4 frag(v2f i) : SV_Target {

fixed3 normal = normalize(i.normal);
fixed3 LightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed3 ViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
float3 h = normalize(LightDir + ViewDir);
float ndotL = dot(normal, LightDir);
float ndotH = dot(normal,h);
fixed3 albedo = tex2D(_Albedo, i.uv).rgb * _Diffuse.rgb;
//ambient
//fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;

float4 indirect_irradiance = texCUBElod(_CubeMap, float4(normal,_CubeMapLod));
float3 indirect_diffuse = indirect_irradiance * albedo;

//diffuse
fixed3 diffuse = _LightColor0.rgb * albedo * max(ndotL,0);

//specular
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(ndotH,0), _Gloss) ;

float kd = (255.0 - _Spec)/255.0;//float kd = (255.0 - _Gloss)/255.0;
fixed3 color = indirect_diffuse * _EnvScale + diffuse * kd + specular * (1.0-kd);//ambient + diffuse + specular;
return fixed4(color,1.0);
}

参考资料

[1] https://www.bilibili.com/video/BV1B54y1j7zE 【技术美术百人计划】图形 2.4 传统经验光照模型详解

[2] Unity Shader入门精要

[3]https://docs.unity3d.com/cn/2021.3/Manual/class-TextureImporter.html#GenerateMipMaps

[4] https://learnopengl-cn.github.io/

【笔记】【百人计划】图形2.3 常用函数介绍(HLSL)

微软文档HLSL内部函数:

https://docs.microsoft.com/zh-cn/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions

一、基本数学运算

1
2
3
4
5
6
7
8
9
10
max(a,b)//最大值
min(a,b)//最小值
mul(a,b)//变量相乘,矩阵、向量运算(a是向量则看作行向量,b是向量则看作列向量)
abs(a)//绝对值
round(x)//最接近的整数
sqrt(x)//平方根
rsqrt(x)//平方根倒数
degrees(x)//弧度转角度
redians(x)//角度转弧度
noise(x)//噪声函数
img img

二、幂指对函数

1
2
3
4
5
6
7
8
pow(x,y);//x^y
exp(x);//e^x
exp2(x);//2^x
ldexp(x, exp);//x*2^exp
log(x);//lnx
log10(x);//log_10 x
log2(x);//log_2 x
frexp(x, out exp);//把浮点数x分解为尾数mantissa和指数 x = ret * 2^exp,返回值是位数,exp参数返回的值是指数(如果x参数为0,则尾数和指数均返回0)
img

三、三角函数与双曲函数

1
2
3
4
5
6
7
8
9
10
sin(x);cos(x);tan(x);//x均为弧度
sincos(x, out s, out c);//返回x的正弦值和余弦值
tan(y,x);//返回y/x的正切值
asin(x);//反正弦
acos(x);//反余弦
atan(x);//反正切
atan2(y,x);//输出y/x的反正切
sinh(x);//双曲正弦,(e^x-e^(-x))/2
cosh(x);//双曲余弦,(e^x+e^(-x))/2
tanh(x);//双曲正切,(e^x-e^(-x))/(e^x+e^(-x))
img img

四、数据范围类

1
2
3
4
5
6
7
8
9
10
ceil(x);//向上取整
floor(x);//向下取整
step(x,y);//x<=y为1,否则为0
saturate(x);//返回将x钳制到0和1之间的值
clamp(x,min,max);//把x限制在[min,max]范围内,
fmord(x,y);//返回x对y取余的余数
frac(x);//返回x的小数部分
modf(x, out ip);//将x分为小数和整数部分(符号与x相同),ip返回整数部分,整体返回小数部分
lerp(x,y,s);//按照s在x到y之间插值(x*(1-s)+y*s)
smoothstep(min,max,x);//如果x在min和max范围内,则返回01之间的平滑Hermite插值,使用smoothstep在两个值之间创建平滑过渡。例如平滑混合两种颜色
img img

五、类型判断类

1
2
3
4
5
6
all(x);//确定x所有分量是否均为非零,均非零则返回true,否则false(处理浮点型、整形、布尔型数据定义的标量、向量或矩阵)
clip(x);//如果x小于零,则丢弃当前像素,常用于判定范围(不仅针对0,返回值为void)。常用于测试alpha,如果每个分量代表到平面距离,还可用来模拟剪切平面
sign(x);//返回x正负性,小于0为-1,大于0为1,0则返回0
isinf(x);//x为+INF或-INF则为true,否则false
isfinite(x);//判断x是有限的,与isinf相反
isnan(x);//如果x为NAN(非数字),返回true,否则false

六、向量与矩阵类

1
2
3
4
5
6
7
length(v);//向量模长
normalize(v);//x/length(x)归一化
distance(a,b);//向量之间的距离(表示的点的距离)
dot(a,b);//点乘
cross(a,b);//叉乘
determinant(m);//矩阵m的行列式
transpose(m);//m的转置矩阵

七、光线运算

1
2
3
4
5
6
7
8
9
//注意所有的入射方向都表示入射方向的相反方向
reflect(i,n);//计算入射方向i对于法线n的反射方向
fefract(i,n,ri);//入射方向i,法线n,ri折射率,计算折射方向
lit(n_dot_l,n_dot_h,m);//返回照明系数向量(ambient,diffuse,specular,1)
//ambient=1
//diffuse = ndotl<0 ? 0 : ndotl
//specular = ndotl<0 || ndoth < 0 ? 0: ndoth^m
faceforward(n,i,ng);//输出法线n,输入视线方向i,表面法线方向ng,反转表面法线(如有必要)以面向和i相反的方向,在n中返回结果
//返回-n*sign(dot(i,ng))

八、1D纹理查找(几乎不用)

函数ddx,ddy用于求取相邻像素间某属性的差值;输入参数通常是纹理坐标,返回相邻像素间的属性差值;

1
2
3
4
5
6
tex1D(s,t);//一维纹理查找,返回纹理采样器s在标量t位置的color4
tex1D(s,t,ddx,ddy);//微分查询一维纹理ddx,ddy均为向量
tex1Dlod(s,t);//使用LOD查找纹理s在t.w位置的color4
tex1Dbias(s,t);//t.w决定的某个mip层偏置后的一维纹理查找
tex1Dgrad(s,t,ddx,ddy);//使用微分并指定mip层的一维纹理查找
tex1Dproj(s,t);//把纹理当作一张幻灯片投影到场景中,计算出投影纹理坐标t(t.w除以透视值),然后使用投影纹理坐标查询

九、2D纹理查找

1
2
3
4
5
6
tex2D(s,t);//二维纹理查找,返回纹理采样器s在t(x,y)位置的color4
tex2D(s,t,ddx,ddy);//微分查询二维纹理t,ddx,ddy均为向量
tex2Dlod(s,t);//使用LOD查找纹理s在t.w位置的color4
tex2Dbias(s,t);//t.w决定的某个mip层偏置后的二维纹理查找
tex2Dgrad(s,t,ddx,ddy);//使用微分并指定mip层的二维纹理查找
tex2Dproj(s,t);//把纹理当作一张幻灯片投影到场景中,计算出投影纹理坐标t(t.w除以透视值),然后使用投影纹理坐标查询

十、3D纹理查找

3D纹理资源(体积纹理),包含3D体积的纹理像素

1
2
3
4
5
6
tex3D(s,t);//三维纹理查找,返回纹理采样器s在vec t位置的color4
tex3D(s,t,ddx,ddy);//微分查询三维纹理t,ddx,ddy均为向量
tex3Dlod(s,t);//使用LOD查找纹理s在t.w位置的color4
tex3Dbias(s,t);//t.w决定的某个mip层偏置后的立方体纹理查找
tex3Dgrad(s,t,ddx,ddy);//使用微分并指定mip层的立方体纹理查找
tex3Dproj(s,t);//使用投影方式的立方体文里查找

十一、立体纹理查找

指CubeMap

1
2
3
4
5
6
texCUBE(s,t);//返回纹理采样器s在vec t位置的color4
texCUBE(s,t,ddx,ddy);//微分查询立方体纹理t,ddx,ddy均为向量
texCUBElod(s,t);//使用LOD查找纹理s在t.w位置的color4
texCUBEbias(s,t);//t.w决定的某个mip层偏置后的三维纹理查找
texCUBEgrad(s,t,ddx,ddy);//使用微分并指定mip层的三维纹理查找
texCUBEproj(s,t);//把纹理当作一张幻灯片投影到场景中,计算出投影纹理坐标t(t.w除以透视值),然后使用投影纹理坐标查询

作业

  • 写出最常用的5个函数
    • dot,mul,normalize,tex2d,pow
  • 这是我当前感觉最常用的,dot和mul不必说,向量矩阵运算必备;而由于光照计算中使用方向,常常需要对向量归一化;有贴图就有纹理采样;最后这个pow方法,看看各种光照模型的公式里,就有很多地方出现幂运算了。
  • ddx ddy的实际使用测试

ddx/ddy与法线贴图

ddx/ddy计算法线

首先ddx与ddy是两个函数,完整的表达是ddx(m),ddy(m)。

在glsl中,它相当于dFdx(m),dFdy(m)。

这两个函数只能作用于片元着色器中,根据他们的作用也很好理解这一点。

我们需要提供一个属性,然后这个函数会计算当前片元在屏幕空间中,沿x或y方向关于这个属性的偏导,因为片元是离散的,当然是以差分形式。

img

如果是考虑世界空间位置这一属性,我的理解是,对于当前着色点,计算的是这两个方向的切线方向。

于是有一个通常的法线计算方法

1
normal = normalize(cross(ddx(pos),ddy(pos)));

image-20220728230748527

左边是ddx,ddy计算出的法线,右边是顶点法线在fs中插值的法线。

很明显,这两者就好像flat shading和phong shading的关系。仔细想想确实如此。不考虑着色,只考虑几何信息,这个球体本来就是三角网格

image-20220728230953252

对于三角形上的一个点的法线,使用切线叉乘计算的法线当然是原原本本的网格法线。而右边的法线是顶点法线插值。

在unity中还原flat shading或是low poly风格的渲染,确实会采用ddx和ddy的方法。

ddx/ddy与法线贴图

learnopengl中这一段代码给我留下了深刻印象。当时用的时候完全不知道dFdx是做什么的,只能硬着头皮用了。现在我们已经了解了ddx函数,再回过头来看一下它对发现贴图的处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Easy trick to get tangent-normals to world-space to keep PBR code simplified.
// Don't worry if you don't get what's going on; you generally want to do normal
// mapping the usual way for performance anways; I do plan make a note of this
// technique somewhere later in the normal mapping tutorial.
vec3 getNormalFromMap()
{
vec3 tangentNormal = texture(normalMap, TexCoords).xyz * 2.0 - 1.0;

vec3 Q1 = dFdx(WorldPos);
vec3 Q2 = dFdy(WorldPos);
vec2 st1 = dFdx(TexCoords);
vec2 st2 = dFdy(TexCoords);

vec3 N = normalize(Normal);
vec3 T = normalize(Q1*st2.t - Q2*st1.t);
vec3 B = -normalize(cross(N, T));
mat3 TBN = mat3(T, B, N);

return normalize(TBN * tangentNormal);
}

核心就是求TBN矩阵嘛。N向量和B向量都很好理解,一个直接就是模型法线,一个就是由叉乘计算的副切线。

问题就在于切线的计算。

1
vec3 T  = normalize(Q1*st2.t - Q2*st1.t);

img

(这里把E1看作Q1,E2看作Q2)

$E_1=ΔU_1T+ΔV_1B$

$E_2=ΔU_2T+ΔV_2B$

这样再结合cpu计算TBN的方法,就很好理解了。

似乎后面的课程马上就会到法线贴图的部分,到时候再详述好了。

参考资料

[1]https://www.bilibili.com/video/BV1q64y1m7Ev 【技术美术百人计划】图形 2.3 常用函数介绍
[2]https://www.jianshu.com/p/7fc6a2fef29d
[3]https://learnopengl.com/code_viewer_gh.php?code=src/6.pbr/1.2.lighting_textured/1.2.pbr.fs
[4]https://learnopengl-cn.github.io/05%20Advanced%20Lighting/04%20Normal%20Mapping/
[5]https://zhuanlan.zhihu.com/p/484182779

【笔记】【百人计划】图形2.2 模型与材质基础

一、渲染管线与模型基础

image-20220723154809728

uv

在建模软件中完成UV展开,UV放置在一个横向u,纵向v,(0-1)的二维坐标系中

展开后的uv在sp等软件中绘制贴图

模型信息

模型的表达(以obj为例)

  • v顶点坐标
  • vt对应纹理坐标 texcoord
  • vn顶点法线
  • f面
  • 顶点色(fbx)
# obj格式 fbx格式 备注
多个object 支持 支持 在文件中存放多个三维模型
单object拥有多个材质 支持 支持
Polygons面 支持 支持 易于被3ds max等软件编辑加工 注:导入到ue4时,必须为三角形面
Normal 支持 支持
Material 支持 支持
光滑组(Smoothing Group 支持 支持 光滑组中的点或面使用平均法线来计算光照,使得整个mesh区域看起来更平滑
uv套数 1 多套
顶点Color 不支持 支持 导入ue4时,Vertex Color Import Option设置为Replace
ue4 lod 不支持 支持 导入ue4时,勾选Import Mesh LODs
ue4 simple collision 支持 支持 任意凸体碰撞命名规则:UCX_[RenderMeshName]、UCX_[RenderMeshName]_00
socket插槽 不支持 支持 命名规则:SOCKET_[RenderMeshName]、SOCKET_[RenderMeshName]_01
skeleton骨骼 不支持 支持
skeletal mesh网格 不支持 支持
animation sequence 不支持 支持 fbx可在文件中存放多个动作序列

二、材质基础

  • 材质

    • 描述物体对光的接收与散射

    • 漫反射

    • 镜面反射

    • 折射

      • 菲涅尔定律可以描述反射与折射的比例
    • 粗糙镜面反射glossy(磨砂金属)

    • 粗糙镜面折射(毛玻璃)

    • 多层材质

      image-20220723160309345
    • 次表面散射(Subsurface scattering)

      • 光线传入物体内部后,经过多次弹射再离开物体表面

      • image-20220723160453573
      • image-20220723160606384
  • 改变材质表面

    • 使用法线贴图对法线做出调整,影响光照计算的结果

三、模型数据解析

  • 顶点动画

    • 在vs中改变模型的顶点位置,达到模型运动的效果
    • (需要一定数量的顶点,效果才比较明显)
  • 纹理动画

    • 在fs中修改模型uv信息,使采样贴图时发生位移而产生运动效果
  • 顶点色

    • 在渲染时影响输出结果,控制颜色范围
    • 例:当前顶点色达到一定值,就认定为阴影中
    • 片元当中使用重心坐标插值
  • 顶点法线与面法线

image-20220723162706622

四、扩展

NPR中,通常在vs中将顶点往法线方向偏移(模型背面),然后在fs中直接输出一个颜色,达到描边效果。

BackFacing描边时,线条断开就是因为没有平滑顶点之间的法线(面法线外扩),法线不连续导致的。

image-20220723162821829

作业

顶点色还可以做什么

  • 在渲染上
    • 直接作为物体颜色
    • 与贴图颜色混合。
    • 取代一些光照:如教程中罪恶装备案例对阴影的处理
  • 其他应用
    • 不直接用于渲染,正如贴图,顶点色还可以储存其他信息、属性

模型光滑组对法线有什么影响

首先要知道什么是光滑组,这其实也是我第一次见这个名词。

光滑组:(smoothing group)是用来设置边缘线的光滑显示的。

光滑组定义了模型边缘渲染的方式(flat shading和Gouraud/Phong shading),也就是101中所说的着色频率(Shading frequencies)

这也就控制了所说的边缘“软硬”

在这里插入图片描述

那这样回答这个问题就很简单了

Flat shading使用的是面法线;gouraud shading使用的是顶点法线;在vs中计算顶点着色,片元对颜色插值;phong shading使用顶点法线,片元上对法线插值后,在fs中计算光照。

光滑组所做的也就是决定模型哪些部分采用法线插值

因此光滑组的作用也很有趣,让一个模型的着色方式不同,来表现同一模型的不同细节。

过渡部分

尝试了Maya中对应的“软硬边”功能,因为非常值得思考的是,在光滑组与非光滑组的过渡部分如何处理?

image-20220723175542376 image-20220723175557394

我们来研究这里的细节。首先对于完整的硬边三角形和软边三角形不必说。

对于硬边三角形的顶点法线,就是面法线,因此,4个硬边相接的顶点上,出现了4个顶点法线,分别是用1256这四个面法线代替的。关注2354这里的顶点法线,只有完整的硬边面5,还保持着面法线,

image-20220723180155081

我尝试对这个顶点剩余的硬边进行软化,观察这个顶点法线是否变化

image-20220723180950650 image-20220723181006044

出现明显的偏移,因此可以得出结论

软边会使相邻两个面的面法线进行插值得到一个顶点法线。

像这里4条边,只需要3条软边,就完成了周围四个面法线(其实是面法线所代替的4个顶点法线)的插值得到的顶点法线,这和4条软边是一样的。

image-20220723182303248 image-20220723182545394

研究了半天,放了个最简单的例子,其实就理解为软边会使相邻两个面法线插值好了

image-20220723185549252

参考资料

[1] https://www.bilibili.com/video/BV1kh411U7F7 【技术美术百人计划】图形 2.2 模型与材质基础

[2]https://www.bilibili.com/video/BV1X7411F744 GAMES101-现代计算机图形学入门-闫令琪

【笔记】【百人计划】图形2.1 色彩空间

一、发送器

光源是出生点,光源发射出光线,光线通过直射反射折射等路径最终进入人眼。

人眼接收到光线后,人眼细胞产生一系列化学反应,把产生的信号传入大脑,大脑对颜色产生认知。

  • 光源

    • 产生光的物体
  • 波长

    • 光的波长分布理论上是无限大的,人眼可见光是局限的
  • 能量分布

    • 分光光度计
      • 描述光线的具体能量强度
      • 拆分光线,形成单一波长光
      • 测量单一波长光的实际所含能量
  • 光的传播

    • 光每次经过反射、投射都会对光的能量分布产生影响

二、接收者

人眼接受色彩的特性

  • 相对亮度感知
    • 在暗的环境下看亮的东西会更亮
  • 人眼HDR
    • 人眼可以自动调整曝光
    • 能够分辨高亮度当中的层次区别、阴影中物体的异同
  • 人眼感光细胞分布
    • 杆状细胞
      • 感知亮度,对亮度敏感
    • 锥状细胞
      • 感知色彩
      • L细胞(红色)、M细胞(绿色)、S细胞(蓝色)
      • 根据这三种细胞分布可以得出,人眼对红色感知度最高,对蓝色感知度最低
image-20220723141425478
  • 接收公式
    • $C=\int S(\lambda)\cdot I(\lambda) \cdot R(\lambda)d\lambda$
    • C是人眼输出到大脑的神经电信号
    • S表示LMS三个感光细胞的感知分布
    • I表示光源的功率谱分布
    • R表示反射物体的吸收功率分布

三、色彩空间

历史上人们对人眼感知色彩的猜测

  • 人们有100多种感受色彩的细胞
  • 人眼有RGB三种感知细胞
  • 人眼有黑白、红绿、黄蓝三种感知细胞

1905 Munsell色彩系统(美术角度)

(HSL色相饱和亮度)

image-20220723142131075

1931 CIE色彩系统(科学角度)

image-20220723143907732 image-20220723144232530

曲线覆盖的面积相等
$$
\int_0^\infin \bar r(\lambda)d\lambda=\int_0^\infin \bar g(\lambda)d\lambda = \int_0^\infin \bar b(\lambda)d\lambda
$$

红色基色强度有负数部分,虽然物理正确,但很不直观,不便使用,因此进行归一化,保证色彩在-1~1之间

归一化后,r’g’b’都在-1~1之间,r’+g’+b’=1,可以通过其中两个计算另一个

image-20220723144809678

1931 XYZ 色彩系统

  • Color matching function
  • image-20220723144427061

XYZ与CIE 1931RGB的转换

image-20220723145521683 image-20220723145726749

在该图像上无法体现出亮度,于是在归一化的基础上把XYZ的Y拿出来与xy组成Yxy色彩空可能感觉,Y表示亮度,xy表示色彩

  • 问题
    • 色彩分布不均匀

定义色彩空间

色彩空间满足的指标

  • 色域(三个基色的坐标,由此形成三角形)
  • Gamma(如何对三角形内进行切分)
  • 白点(色域三角形中心)
    • 又称为E点或者D55点。白点用于定义色域的白色。相关关键词,白平衡。组成白点的各色能量频谱一致,因此白点的频谱是最平坦的。
image-20220723150148332

sRGB的构成:在sRGB的色域与色域中心下,gamma=2.2

四、常用的色彩模型与色彩空间

  • 色彩模型
    • 使用一定规则描述、排列颜色的方法
    • RGB、CMYK、LAB
  • 色彩空间
    • 需要至少满足三个指标
      • 色域、白点、Gamma
    • CIE XYZ,Adobe RGB,sRGB,Japan Color 2001 Uncoated,US web Coated(后两个基于CMYK)

image-20220723150723307

五、色彩空间转换

这里直接复制一段几年前写的色彩模型转换的笔记

HSV-RGB

在实际使用中,以$photoshop$为例,其色彩模型数值取值为:

​ $H取0-360,S,V取0-100%(实际用户输入调整数值范围为0-100),此处计算取0-1较方便$

​ $RGB均取0-255$

$$
C=V\times S
\X=C\times(1-|(H/60^\circ)mod\ 2-1|)
\m=V-C\
(R’,G’,B’)=\left{\begin{array}{}
(C,X,0)&,0^\circ\leq H<60^\circ\
(X,C,0)&,60^\circ\leq H<120^\circ\
(0,C,x)&,120^\circ\leq H<180^\circ\
(0,X,C)&,180^\circ\leq H<240^\circ\
(X,0,C)&,240^\circ\leq H<300^\circ\
(C,0,X)&,300^\circ\leq H<360^\circ\
\end{array}{}\right.
\(R,G,B)=\frac{(R’+m,G’+m,B’+m)}{255}
$$
该式子理解如下:

将红(R),黄(Y),绿(G),青(M),蓝(B),品红(C),红(R)

均匀放在一条轴上,采用线性插值的方式画出色相,如图

色彩线性插值2

如图,横坐标分布$H$,纵坐标的最低值$V(1-S)$即是$m$,因为是线性插值计算,所以RGB呈折线式分布,根据图理解,可以如下改进原公式
$$
C=V(最高阈值)\
m=V(1-S)(最低阈值)\
在图像处理应用中提取图像亮部或暗部,若想要改变容差,则只需要改变阈值
\
X=V\times S\times (1-|H/60| mod 2-1)+m\(该式可以直接表达线性插值混合部分的折线(锯齿状线条))
\(R,G,B)=255\times \left{\begin{array}{}
(C,X,m)&,0^\circ\leq H<60^\circ\
(X,C,m)&,60^\circ\leq H<120^\circ\
(m,C,x)&,120^\circ\leq H<180^\circ\
(m,X,C)&,180^\circ\leq H<240^\circ\
(X,m,C)&,240^\circ\leq H<300^\circ\
(C,m,X)&,300^\circ\leq H<360^\circ\
\end{array}{}\right.
$$

RGB-HSV

$$
R’,G’,B’=\frac{R,G,B}{255}\
Cmax=max(R,G,B)
\Cmin=min(R,G,B)
\\Delta=Cmax-Cmin
$$

$H(hue)$
$$
H=\left{\begin{array}{}0^\circ& \Delta =0\60^\circ \times(\frac{G’-B’}{\Delta}mod\ 6)&,Cmax=R’\60^\circ \times(\frac{B’-R’}{\Delta}+2)&,Cmax=G’\60^\circ \times(\frac{R’-G’}{\Delta}+4)&,Cmax=B’\end{array}{}\right.
$$
$S(saturation)$
$$
S=\left{\begin{array}{}0&,Cmax=0\\frac{\Delta}{Cmax}&,Cmax\neq0\end{array}{}\right.
$$
$V(value)$
$$
V=Cmax
$$

作业

  • 色彩空间的定义

    • 基于CIE XYZ色彩空间,确定一定的色域、白点,以及特定的gamma采样方式,所决定出的色彩区域。
  • 人眼可见光范围

    • 390nm~760nm

参考资料

[1] https://www.bilibili.com/video/BV1Qb4y1S7CC 【技术美术百人计划】图形 2.1 色彩空间介绍

[2] https://blog.csdn.net/weixin_43280713/article/details/84133949

[3] https://www.cnblogs.com/freshair_cnblog/p/11493706.html

【笔记】【百人计划】图形1.4 PC手机图形API介绍

图形1.4PC手机图形API介绍

基础概念

  • 图形API

    • 是一个图形库,用于渲染2D、3D矢量图形的跨语言、跨平台的应用程序接口(API),针对GPU。
  • 应用端

    • 我们自己的程序端
  • 图元

    • 要渲染的几何物体、形状
  • 纹理

    • 可以理解为贴图,opengl es中纹理就是图片的另一种叫法
  • 纹素

    • 纹理的基础单元(纹理的像素)
  • 顶点数组

    • 顶点是组成图元的各个顶点的坐标数据(vertex),这些坐标数据可以一起存到要给内存数组中,这就是顶点数组
  • 顶点缓冲区

    • 显存中专门分配一块显存来存储这个顶点数组,这个显存就是顶点缓冲区
  • 顶点着色器

  • 片元着色器

主流图形API

image-20220722211701281

  • DirectX(windows)

  • Opengl(windows,linux,mac)

  • Opengl ES(移动设备——手机、oculus)

    • Opengl ES2.0/3.0是可编程图形管线(vs、fs),1.x是固定管线

Opengl ES3.0新功能

image-20220722211827363

  • 兼容性

    • gles 3.0是向后兼容的(2.0的程序可以在3.0中使用)
  • 新特性

    • 阴影贴图、体渲染、gpu粒子动画、几何形状实例化、纹理压缩、gamma矫正
    • 适应嵌入系统的局限性
  • 渲染管线

    • 移除alpha测试
      • 片段着色器可抛弃片段,因此alpha测试可以在fs中进行
    • 移除逻辑操作(LogicOp)
      • 很少使用
  • Shader编写

    • 版本声明

    • #version 300 es//声明为指定使用Opengl ES3.0版本
      //不添加版本声明或使用#version 100 es 指定使用ES2.0
      //(2.0是es中最先出的可编程管线,所以是100,为了匹配版本号,3.0跳到了300 es)
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18

      - 输入输出

      - in,out,inout关键字取代attribute,varing
      - gl_FragColor和gl_FragData删除了
      - 片段着色器可以使用out声明字段输出

      - 变量赋值

      - 可以直接使用layout对指定位置变量赋值

      - ```glsl
      //shader中
      layout (location = 1) uniform float a;
      //应用端代码中
      GLES30.glUniform1f(1,1f);
      //2.0中必须使用如下形式
      GLES20.glUniform1f(GLES20.glGetAttribLocation(program,"a"),1f)

骁龙Adreno对应ES版本型号

https://zh.wikipedia.org/wiki/Adreno

高通骁龙组件列表 - 维基百科,自由的百科全书 (wikipedia.org)

Adreno320开始支持OpenglES3.0

作业

  • 支持Opengl es3.2的骁龙GPU型号有哪些
    • Adreno420/430/5xx/6xx/7xx
  • 如何看待Vulkan、
    • 对Vulkan了解不多,最大的特点就是:更复杂,也更高性能,并且支持跨平台。这增加了它的学习成本和上手难度的同时,能更深入地控制硬件,达到对性能更极致的追求。

【笔记】【百人计划】图形1.3 纹理

图形1.3纹理

纹理

纹理–贴图

一种可供着色器读写的结构化存储形式

  • 纹理的出现
    • 牺牲几何细节
      • 降低建模工作量
      • 减少储存空间
      • 增加读取速度
    • (使用图像数据源来修正或模拟物体表面)

纹理管线

image-20220722170359786

模型空间位置-(投影函数-纹理映射)-纹理坐标(UV)-通讯函数-新纹理坐标-纹理采样(避免依赖纹理读取)-纹理值

  • 投影函数

    • uv值的计算,就是texture mapping的投影方式(展UV)
    • 特殊情况
      • 环境贴图的采样
  • 通讯函数

    • 对纹理坐标进行扩展。例如平移、缩放、旋转或控制图像的应用方式
  • 依赖纹理读取

有两个定义。其一是移动设备上,当PS中手动计算了纹理坐标而不是用VS中传入的值时会变成依赖纹理读取,在老的移动GPU(不支持GLES3.0)上,没有依赖纹理读取的shader会跑的更高效,因为纹素数据可以被提前获取。另一个定义则更早,在早期的桌面GPU上,当一个纹理的坐标基于另一个纹理的结果时。例如用法线贴图中获取的法线来访问一张立方体贴图,这样的功能是受限制的,如今虽可以使用但是也是有一定的性能影响的。

纹理采样设置

Warp Mode
  • 决定UV在[0,1]之外的表现形式
    • Repeat,Mirror,Clamp,Border
  • GL-Warpping Model包装模式
  • DX-Texture Addressing Mode纹理寻址模式

image-20220722170720962

Filter Mode
  • 最近邻Nearest

image-20220722172111472

顾名思义,就是找到最接近的像素颜色

  • 双线性插值Bilinear

image-20220722172531595

  • 双三次插值Bicubic(立方卷积)

取周围临近16个像素——除了周围四个直接相邻像素点的影响,同时考虑临近像素的变化率的影响

  • 兰索斯插值

取64个像素

  • Quilez光滑曲线插值

2x2纹理组,使用smoothstep和quintic曲线

用这两种曲线对纹理坐标进行处理后进行双线性插值

但是我怀疑是否是用曲线插值直接代替线性插值

image-20220722173955476

Texture Magnification/Minification

其实对于纹理放大对应的还有纹理缩小,但是本质是一样的,都是纹理像素和屏幕对纹理采样的频率差别太大导致的。

image-20220722174926271
  • 纹理太大,会出现颜色丢失与闪烁、摩尔纹

  • 纹理太小,则出现走样

  • 解决方法

    • Supersampling(点查询)
      • 消耗大
    • Mipmap(范围查询:快速、==近似、方形范围==)
      • 消耗的内存比原先多1/3
image-20220722180359176

假如L是4个像素,那么D=2,也就是说,在第2层的mipmap上,这个区域会对应一个像素

image-20220722190424580

  • mip之间的过渡——场景中的mip层级是不连续的
    • Trilinear interpolation
image-20220722181227045
  • Mipmap的限制
    • Overblur
image-20220722181812718

屏幕映射到纹理上的形状并不是标准的正方形

image-20220722182042045
  • 解决方法:各向异性过滤Anisotropic Filtering
  • Ripmaps
    • 对于矩形的查询有更好的结果
    • 但是对于倾斜的形状仍然无法解决
image-20220722181954884
  • EWA filtering
    • 多次查询会更消耗

image-20220722182224011

image-20220722192532542
  • Unity/Ue4中的各向异性过滤
    • 并不总是使用Ripmap
    • 重用已有的Mipmap,屏幕像素反向投影到纹理空间,通过覆盖方块==最短边==来确定level
    • 较长的边创建一条各向异性线,按照过滤等级沿着这条线多次采样并合成。(过滤等级越高,采样次数越多,消耗越大)

优化与纹理应用

优化

  • CPU优化——降低DrawCall——减少命令缓冲区的命令
image-20220722193601854

纹理图集、纹理数组、无约束纹理——避免渲染时频繁改变纹理带来的消耗

  • 纹理图集
    • 把若干小纹理合并成大纹理
image-20220722193710685
  • GPU渲染优化——纹理压缩
    • 减少资源在CPU中解压缩的过程
    • 减小包体,减轻数量级、带宽计算压力
    • 提高内存使用效率

其他应用

  • CubeMap立方体贴图
    • 使用从原点出发的方向向量进行采样
image-20220722194124743
  • Bump Mapping凹凸贴图
    • 不增加顶点的情况下改变几何表面法线
  • Displacement Mapping位移/置换贴图
    • 曲面细分、对顶点进行位置移动
  • Environment Lighting
  • Store microgeometry
  • Procedural textures
  • Solid modeling
  • Volume rendering
  • 。。。

参考资料

[1]https://www.bilibili.com/video/BV1sA411N7z3 【技术美术百人计划】图形 1.3 纹理的秘密

[2]https://www.bilibili.com/video/BV1X7411F744 GAMES101-现代计算机图形学入门-闫令琪

[2]https://zhuanlan.zhihu.com/p/365518995

【笔记】【百人计划】图形1.2.3 MVP矩阵运算

图形1.2MVP矩阵运算

  • MVP矩阵

    • Model模型,View观察,Projection投影矩阵
  • 空间

    • Local Space(Local Coordinate)
    • World Space(World Coordinate)
    • View/Camera Space(View/Camera Coordinate)
    • Clip Space(Clip Coordinate)
    • Screen Space(Screen Coordinate)

image-20220721144144034

M:模型空间->世界空间

对于模型本身来说,有自身的Local Space,当把模型放入世界空间场景去描述时,就需要把模型的局部坐标变换到世界坐标,这个变换用矩阵描述,就是Model矩阵

$M_{Model} = M_TM_RM_S$

平移Translation

Translation变换不是线性变换
$$
\begin{bmatrix}x’\y’ \end{bmatrix} =\begin{bmatrix}a&b\c&d \end{bmatrix} \begin{bmatrix}x\y \end{bmatrix}+\begin{bmatrix}t_x\t_y \end{bmatrix}
$$

齐次坐标

$2D point = (x,y,1)^T$

$2Dvector = (x,y,0)^T$ (向量的平移不变性)
$$
\begin{bmatrix}{}x’\y’\1’ \end{bmatrix}{} = \begin{bmatrix}{}1&0&t_x\0&1&t_y\0&0&1 \end{bmatrix}{}\cdot\begin{bmatrix}{}x\y\1 \end{bmatrix}{} = \begin{bmatrix}{}x+t_x\y+t_y\1 \end{bmatrix}{}
$$
对于点 $(x,y,w)^T$ 的表达,与 $(x/w,y/w,1)^T,w\neq 0$ 等价

仿射变换

对于任何一种变换能够写成

Affine map = linear map + translation
$$
\begin{bmatrix}x’\y’ \end{bmatrix} =\begin{bmatrix}a&b\c&d \end{bmatrix} \begin{bmatrix}x\y \end{bmatrix}+\begin{bmatrix}t_x\t_y \end{bmatrix}
$$
表示仿射变换,使用齐次坐标可以表示成矩阵乘法
$$
\begin{bmatrix}{}x’\y’\1’ \end{bmatrix}{} = \begin{bmatrix}{}a&b&t_x\c&d&t_y\0&0&1 \end{bmatrix}{}\cdot\begin{bmatrix}{}x\y\1 \end{bmatrix}{}
$$

3维的平移矩阵

$$
T(t_x,t_y,t_z) = \begin{bmatrix}1&0&0&t_x\0&1&0&t_y\0&0&1&t_z\0&0&0&1\end{bmatrix}
$$

旋转Rotation

$$
R_x(\theta) =\begin{bmatrix}1&0&0&0
\0&\cos\theta&-\sin\theta&0
\0&\sin\theta&\cos\theta&0
\0&0&0&1
\end{bmatrix},\
R_y(\theta) =\begin{bmatrix}\cos\theta&0&\sin\theta&0
\0&1&0&0
\-\sin\theta&0&\cos\theta&0
\0&0&0&1
\end{bmatrix},\
R_z(\theta) =\begin{bmatrix}\cos\theta&0&-\sin\theta&0
\\sin\theta&0&\cos\theta&0
\0&0&1&0
\0&0&0&1
\end{bmatrix}
$$

旋转矩阵是正交的

接下来我们来说一说为什么绕y轴的旋转矩阵长得有点不一样。

这里是同一个右手坐标系,分别从x,z,y的方向来观察。

我们绕哪个轴旋转,哪个轴上的坐标就不变,这很明显,因此只需要观察剩下的两个轴所构成的平面。

image-20220721180506988

如ZOY,YOX平面,在右手坐标系下,和二维的YOX旋转是一样的。从Y到Z,从X到Y的顺序是一致的。

但是在以Y轴作为旋转轴的XOZ平面上,是相反的,因此旋转的角度也变成了相反数。

任意旋转

对于任意一种旋转状态
$$
R_{xyz}(\alpha,\beta,\gamma)=R_x(\alpha)R_y(\beta)R_z(\gamma)
$$
这就也就是欧拉角

Rodrigues’ Rotation Formula罗德里格斯旋转公式

给定任意旋转轴和旋转角度转化为对应的旋转矩阵。
$$
R(n,\alpha) = \cos(\alpha)I+(1-\cos(\alpha))nn^T+\sin(\alpha)\begin{bmatrix}0&-n_z&n_y\n_z&0&-n_x\-n_y&n_x&0\end{bmatrix}
$$
如果想绕任意起点的轴,则与2D一样,先平移至原点,绕原点轴旋转,再平移回去。

四元数

放一放吧

但是可以先放个参考资料

quaternion.pdf (krasjet.github.io)

缩放Scale

$$
S =\begin{bmatrix}s_x&0&0&0
\0&s_y&0&0
\0&0&s_z&0
\0&0&0&1
\end{bmatrix}
$$

Model变换顺序

image-20220721163348382 image-20220721163401296

3维的仿射变换齐次坐标表示
$$
\begin{bmatrix}x’\y’\z’\1 \end{bmatrix} =
\begin{bmatrix}a&b&c&t_x
\d&e&f&t_y
\g&h&i&t_z
\0&0&0&1 \end{bmatrix} \cdot\begin{bmatrix}x\y\z\1\end{bmatrix}
$$
对应的Affine map = linear map + translation,
$$
\begin{bmatrix}x’\y’ \end{bmatrix} =\begin{bmatrix}a&b\c&d \end{bmatrix} \begin{bmatrix}x\y \end{bmatrix}+\begin{bmatrix}t_x\t_y \end{bmatrix}
$$
translation的顺序是在最后的,因此一定要先进行线性变换,再进行平移

因此
$$
M_{model} =M_{T}M_{R}M_{S}=
\begin{bmatrix}1&0&0&t_x\0&1&0&t_y\0&0&1&t_z\0&0&0&1\end{bmatrix}
\begin{bmatrix}a&b&c&0
\d&e&f&0
\g&h&i&0
\0&0&0&1
\end{bmatrix}
\begin{bmatrix}s_x&0&0&0
\0&s_y&0&0
\0&0&s_z&0
\0&0&0&1
\end{bmatrix}
$$
其中旋转矩阵是任意旋转矩阵的组合

V:世界空间->相机空间

经过Model矩阵的变换后,物体从模型空间变换到了世界空间的某个状态。在世界空间里的物体被人眼/相机看到,相机要渲染出这张图像,就要以相机为中心来描述,那么就需要把世界空间的模型再变换到相机空间/观察空间。这就是View矩阵。

变换过程:将摄像机和世界坐标重合(相机在世界空间的变换是先旋转,再平移)

  1. 平移
  2. 旋转
  3. (反转左右手坐标系:z分量取反)

首先定义一个相机

  • 位置
  • 观察方向Look-at/gaze direction
  • 向上方向Up direction

让相机和物体保持相对位置,一起移动——与世界坐标重合,再旋转

这里的重合,我们需要让g(gaze)和Y(深度)轴重合,t(up)和Y重合,另一个方向和x轴重合。

image-20220721190933162

对于相机位置$(x_e,y_e,z_e)$
$$
M_{view}=R_{view}T_{view}\
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}
$$
而该如何旋转?直接求是不好求的,但是根据相机的坐标系我们可以知道
$$
R^{-1}{view}=\begin{bmatrix}
x
{g\times t} &x_t&x_{-g}&0
\y_{g\times t}&y_t&y_{-g}&0
\z_{g\times t}&z_t&z_{-g}&0
\0&0&0&1
\end{bmatrix}
\
R_{view}=(R^{-1}{view})^T=\begin{bmatrix}
x
{g\times t}&y_{g\times t} &z_{g\times t}&0
\x_t&y_t&z_t&0
\x_{-g}&y_{-g}&z_{-g}&0
\0&0&0&1
\end{bmatrix}
\
$$
而这里对于g轴和z轴的方向统一问题,也就是左右手坐标系统一的问题,从上图可以看到,世界坐标仍然是右手系,但是相机空间定义的是左手系,指向世界空间z轴负方向。

Unity Shader入门精要中,世界空间是左手系,观察空间是右手系(Opengl)给出的解决方法是,最后再乘上一个negate z矩阵
$$
M_{view}=M_{negate\ z}M_{view}\
M_{negate\ z}=\begin{bmatrix}1&0&0&0
\0&1&0&0
\0&0&-1&0
\0&0&0&1
\end{bmatrix}
$$
就是其实就是把z坐标取反,这也是101作业当中的处理方法,其实也就是上面为什么写-g。这种细节问题,使用引擎什么的一般不会用到,但是在自己写引擎的时候,就是必须需要注意的细节了。

P:相机空间->齐次裁剪空间

  1. 不是真正的投影,为投影做准备
  2. 目的:判断定点是否在可见范围内
  3. P矩阵:对xyz分量进行缩放,用w分量做范围值,如果x,y,z都在w范围内,那么该点在裁剪空间内
  • 投影类型
    • 透视投影
    • 正交投影

其实在这次回顾101的时候发现,闫老师早就把View Transformation和Projection Transformation放到一起,统称为Viewing(观测)Transformation(这是很有道理的,因为这个阶段并没有进行透视除法,还在三维空间当中,透视除法才是把三维“投影“到二维当中的过程)。

投影矩阵只是把View frustum(视锥体)中的六面体空间变换为一个单位立方体空间(Canonical Cube) $[-1,1]^3$。

  • Viewing(观测) transformation
    • View/Camera(视图) transformation
    • Projection(投影) transformation
      • Orthographic projection
      • Perspective projection

正交投影Orthographic Projection

正交投影中的视锥变换定义$[l,r]\times[b,t]\times[f,n]->[-1,1]^3$

正交投影的变换形式可想而知,在x和y轴上需要进行缩放,在z轴上除了缩放还要进行一定的平移,使立方体中心移动到坐标原点。

101在这里的讲述更复杂也更通用的一步,是定义了视锥体和观察坐标系之间的关系

image-20220721200902778

视锥近平面中心不在观察中心。目前我还没接触到过使用这种特性。但其实都是一样的

这样的话,我们需要先平移再缩放,很明显
$$
M_{ortho}=\begin{bmatrix}
\frac{2}{r-l}&0&0&0
\0&\frac{2}{t-b}&0&0
\0&0&\frac{2}{n-f}&0
\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}=\begin{bmatrix}
\frac{2}{r-l}&0&0&\frac{r+l}{l-r}
\0&\frac{2}{t-b}&0&\frac{t+b}{b-t}
\0&0&\frac{2}{n-f}&\frac{n+f}{f-n}
\0&0&0&1
\end{bmatrix}
$$
那其实我们令相机在视锥体近平面中心轴线上,t=-b=size,r=-l =Aspect*size
$$
M_{ortho}=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{n-f}&0
\0&0&0&1
\end{bmatrix}\begin{bmatrix}
1&0&0&0
\0&1&0&0
\0&0&1&-\frac{n+f}{2}
\0&0&0&1
\end{bmatrix}\=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{n-f}&-\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}
$$
也就是入门精要里的结果

透视投影Perspective Projection

思路是先将透视投影的Frustum挤压成长方体,然后进行正交投影

image-20220721232039529

$M_{persp} = M_{ortho}M_{persp\to ortho}$

对于Frustum中间一点(x,y,z)

image-20220721232239730

$$
M_{p\to o}\begin{bmatrix}x\y\z\1\end{bmatrix}=\begin{bmatrix}nx/z\ny/z\unknown\1\end{bmatrix}=>\begin{bmatrix}nx\ny\unknown\z\end{bmatrix}\
M_{p\to o}=\begin{bmatrix}n&0&0&0\0&n&0&0\?&?&?&?\0&0&1&0\end{bmatrix}
$$
考虑

  • 近平面的点不会改变

$$
M_{p\to o}\begin{bmatrix}x\y\n\1\end{bmatrix}=
\begin{bmatrix}nx/n\ny/n\n\1\end{bmatrix}=>\begin{bmatrix}nx\ny\n^2\n\end{bmatrix}\
$$

对于第三行的计算
$$
[a,b,c,d][x,y,n,1]^T = n^2\a=b=0
\cn+d=n^2
$$

  • 远平面点的z值不变

$$
[0,0,c,d][0,0,f,1]^T=f^2
\cf+d = f^2
$$

可以解得
$$
c=n+f\d=-nf
$$

$$
M_{p\to o}=\begin{bmatrix}n&0&0&0\0&n&0&0\0&0&n+f&-nf\0&0&1&0\end{bmatrix}
\
M_p=\begin{bmatrix}
\frac{2}{r-l}&0&0&\frac{r+l}{l-r}
\0&\frac{2}{t-b}&0&\frac{t+b}{b-t}
\0&0&\frac{2}{n-f}&\frac{n+f}{f-n}
\0&0&0&1
\end{bmatrix}\begin{bmatrix}n&0&0&0\0&n&0&0\0&0&n+f&-nf\0&0&1&0\end{bmatrix}
=\begin{bmatrix}
\frac{2n}{r-l}&0&\frac{r+l}{l-r}&0
\0&\frac{2n}{t-b}&\frac{t+b}{b-t}&0
\0&0&\frac{n+f}{n-f}&\frac{2nf}{f-n}
\0&0&1&0
\end{bmatrix}
$$
这个结果也正如虎书上的结果。

对比与思考

但是这个时候问题来了,这个结果和Unity Shader入门精要上不一样……和一些其他地方的结果也不太一样……

最大的问题,都是出在四行三列和三行四列的符号上。

我们先继续考虑 $t=-b=size,r=-l =Aspect*size,\tan\frac{\theta}{2}=\frac{t}{n}$,

image-20220722005643105

$$
M_p=\begin{bmatrix}
\frac{2n}{r-l}&0&\frac{r+l}{l-r}&0
\0&\frac{2n}{t-b}&\frac{t+b}{b-t}&0
\0&0&\frac{n+f}{n-f}&\frac{2nf}{f-n}
\0&0&1&0
\end{bmatrix}=\begin{bmatrix}
\frac{n}{r}&0&0&0
\0&\frac{n}{t}&0&0
\0&0&\frac{n+f}{n-f}&\frac{2nf}{f-n}
\0&0&1&0
\end{bmatrix}\=\begin{bmatrix}
-\frac{\cot\frac{\theta}{2}}{Aspect}&0&0&0
\0&-\cot\frac{\theta}{2}&0&0
\0&0&\frac{n+f}{n-f}&\frac{2nf}{f-n}
\0&0&1&0
\end{bmatrix}
$$
入门精要里和其它一些地方的结果是:
$$
\begin{bmatrix}
\frac{\cot\frac{\theta}{2}}{Aspect}&0&0&0
\0&\cot\frac{\theta}{2}&0&0
\0&0&\frac{near+far}{near-far}&\frac{2nearfar}{near-far}
\0&0&-1&0
\end{bmatrix}
$$
事实上,这和虎书后面给出的Opengl的投影矩阵结果是一样的。

那么造成差别的是在哪一步呢?

首先来看正交投影部分

  • Result1

$$
M_{ortho}=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{n-f}&-\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}
$$

  • Result2

$$
M_{ortho}=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{near-far}&-\frac{near+far}{far-near}
\0&0&0&1
\end{bmatrix}
$$

一个比较坑的点是near/far表示正值,n/f表示坐标(负值),但是这不影响符号的相对差别。

即便把near/far反过来,第三行整体就是反的了。

后来发现,unity/opengl中希望这个frustum的近平面能够对应到立方体的-1,远平面对应到立方体的1。

但是虎书推到和闫老师的平移做法当中,是直接平移中点然后缩放,这就导致即便都是立方体,但是这里近平面对应的是1,远平面对应-1

所以这里就出现了矛盾。

给第一个结果做一个z值矫正
$$
M_{ortho}=\begin{bmatrix}
1&0&0&0
\0&1&0&0
\0&0&-1&0
\0&0&0&1
\end{bmatrix}\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{n-f}&-\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}\=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{f-n}&\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}
$$
再考虑n和f的绝对值,就能得到unity/opengl的正交投影结果了

我们带着这个结果往下看
$$
M_p=\begin{bmatrix}
\frac{2}{r-l}&0&0&\frac{r+l}{l-r}
\0&\frac{2}{t-b}&0&\frac{t+b}{b-t}
\0&0&\frac{2}{f-n}&\frac{n+f}{n-f}
\0&0&0&1
\end{bmatrix}\begin{bmatrix}n&0&0&0\0&n&0&0\0&0&n+f&-nf\0&0&1&0\end{bmatrix}
=\begin{bmatrix}
\frac{2n}{r-l}&0&\frac{r+l}{l-r}&0
\0&\frac{2n}{t-b}&\frac{t+b}{b-t}&0
\0&0&\frac{n+f}{f-n}&\frac{2nf}{n-f}
\0&0&1&0
\end{bmatrix}
$$
和这篇文章的结果是一样的https://zhuanlan.zhihu.com/p/463027517

$M_{p\to o}$计算中考虑n和f的绝对值也就是Unity/opengl的结果。
$$
M_{p\to o}\begin{bmatrix}x\y\z\1\end{bmatrix}=\begin{bmatrix}-|n|x/z\-|n|y/z\unknown\1\end{bmatrix}=>\begin{bmatrix}|n|x\|n|y\unknown\-z\end{bmatrix}\
M_{p\to o}=\begin{bmatrix}|n|&0&0&0\0&|n|&0&0\?&?&?&?\0&0&-1&0\end{bmatrix}
$$
这样就构造出了-1

然后后面的计算就自然而然了。
$$
\footnotesize{M_{p\to o}\begin{bmatrix}x\y\z\1\end{bmatrix}=\begin{bmatrix}-|n|x/z\-|n|y/z\unknown\1\end{bmatrix}=>\begin{bmatrix}|n|x\|n|y\unknown\-z\end{bmatrix}}\
M_{p\to o}=\begin{bmatrix}|n|&0&0&0\0&|n|&0&0\0&0&|n|+|f|&|n||f|\0&0&-1&0\end{bmatrix}\
\footnotesize{M_p=\begin{bmatrix}
\frac{1}{Aspect\cdot Size}&0&0&0
\0&\frac{1}{size}&0&0
\0&0&\frac{2}{near-far}&\frac{near+far}{near-far}
\0&0&0&1
\end{bmatrix}\begin{bmatrix}near&0&0&0\0&near&0&0\0&0&near+far&nearfar\0&0&-1&0\end{bmatrix}}
\\small{=\begin{bmatrix}
\frac{\cot\frac{\theta}{2}}{Aspect}&0&0&0
\0&\cot\frac{\theta}{2}&0&0
\0&0&\frac{near+far}{near-far}&\frac{2nearfar}{near-far}
\0&0&-1&0
\end{bmatrix}}
$$
终于和入门精要的结果一致了。。。

总结一下,影响这些透视投影矩阵符号的两个因素

  • near/far是正值,n/f是坐标
  • 一部分将frustum近平面映射到-1,远平面映射到1;一部分则相反

参考资料

[1]https://www.bilibili.com/video/BV1M5411P7d3 【技术美术百人计划】图形 1.2.3 MVP矩阵运算

[2]https://www.bilibili.com/video/BV1X7411F744 GAMES101-现代计算机图形学入门-闫令琪

[3] Unity Shader 入门精要

[4] Fundamentals of Computer Graphics,4th

[5] https://zhuanlan.zhihu.com/p/463027517

【笔记】【百人计划】图形1.1 渲染流水线

图形1.1 渲染流水线

一、概念流程

  • 应用程序阶段

    • 粗粒度剔除
    • 渲染设置
    • 准备基本数据
    • 输出到几何阶段(渲染图元
  • 几何阶段

    • 顶点着色器
    • 曲面细分
    • 几何着色器
    • 顶点裁剪(屏幕空间裁剪)
    • 屏幕映射(屏幕空间顶点信息
  • 光栅化阶段

    • 三角形(图元:点/线)设置,三角形(图元)遍历,片元着色器
  • 逐片元操作(合并)

    • 裁剪测试
    • 透明度测试
    • 模板测试
    • 混合
  • 后处理(图像空间的处理)

二、细节

1.应用阶段(CPU)

  • 准备基本场景数据(硬盘->内存)
    • 场景物体数据
      • 物体Transform:位置、旋转、缩放等
      • 物体网格数据:顶点位置,UV贴图
      • 这里的分类是不是有点像Maya的两种Dag类型Transform和Shape
    • 摄像机数据
      • 位置、方向、远近裁剪平面
      • 正交/透视(fov)
      • 视口比例/尺寸等
    • 光源及阴影
      • 光源类型:方向光、点光、聚光
      • 颜色、位置、方向、范围、角度等
      • 阴影设置
        • 是否需要阴影,判断该光源可见范围内是否有可投射阴影的物体
        • 阴影参数:对应光源序号、阴影强度、级联参数、深度偏移、近平面偏移
      • 逐光源绘制阴影贴图
        • 近平面偏移
        • 逐级联
          • 计算当前光源+级联对应的观察矩阵、投影矩阵、对应到阴影贴图里的视口区域
        • 绘制到阴影贴图
    • 其他全局数据
  • 加速算法/粗粒度剔除
    • 碰撞检测
    • 加速算法
    • 剔除
      • 可见光裁剪
      • 视锥剔除
      • 如CPU阶段的可见性(遮挡)剔除
        • Octree
        • BSP tree
        • K-D tree
        • BVH tree
    • 其他
  • 设置渲染状态,准备渲染参数(以unity为例)
    • 渲染设置
      • 使用着色器
      • 合批方式
    • 渲染顺序
      • 相对摄像机的距离
      • 材质Render Queue
      • UI Canvas
      • 其他
    • 渲染目标
      • Render Texture
      • Frame Buffer
      • 多个目标
    • 渲染模式
      • Forward
      • Deferred
  • 调用Draw Call,输出渲染图元到显存
    • 顶点数据
      • 位置
      • 颜色
      • 法线
      • UV texcoord
      • 其他
    • 其他数据
      • MVP
      • 纹理贴图
      • 其他

2.几何阶段

2.1顶点着色器

视图变换、顶点着色等
输出到Clip Space

2.2曲面细分着色器(可选)

网格、图元细分

2.3几何着色器(可选)

逐图元着色或者产生更多图元

2.4裁剪(不可编程)

正面或背面剔除(可配置)

裁剪的顺序

关于裁剪和透视除法的顺序,看到很多争议,包括参考图也有画在2D空间和3D空间的

在虎书4th的描述里,裁剪可能完成的地方有2种

  1. 使用6个平面围成的观察金字塔(view pyramid)的世界坐标系中
  2. 齐次坐标除法之前的4D变换空间

无论哪种都可以实现

1
2
3
4
5
6
7
for each of six planes do
if (triangle entirely outside of plane) then
break (triangle is not visible)
else if triangle spans plane then
clip triangle
if (quadrilateral is left) then
break into two triangles

而如果先做透视除法再做裁剪,透视变换保持了深度顺序,但是在0处不连续

image-20220720181336331

这样会把裁剪变得复杂,可能出现错误结果。

这部分的数学推导在后面整理到模型空间变换再研究好了。

总得来说,现代图形渲染管线,就是在齐次裁剪坐标下进行裁剪,然后由硬件完成透视除法。

至于书中提到的第一种裁剪位置在什么地方会用到就不知道了。实际上很多管线中的顺序也取决于硬件设计。

2.5透视除法

齐次裁剪坐标Clip Space下,硬件完成透视除法xyz/w,得到归一化的设备坐标NDC

Opengl和Unity的NDC的z分量范围在[-1,1]

DirectX中NDC的z分量范围是[0,1]

2.6屏幕映射(不可编程)

从连续到离散

坐标系差异(Opengl/D3D)

Opengl左下角为最小窗口坐标

Dx定义左上角为最小窗口坐标

3.光栅化阶段

3.1三角形设置Triangle Setup/图元装配Primitive Assembly

根据输入的网格顶点来计算三角形边界

3.2三角形遍历

(直线绘制算法与扫描线填充算法)扫描变换,检查像素是否被三角形覆盖,覆盖则生成一个片元

每个片元计算根据三角形顶点插值后的数据

因此片元和像素区别的意义不仅在于SSAA,还有合并操作当中,对同一像素对应片元进行合并才输出像素。

MSAA

对每个像素设置多个采样点,对每个子采样点进行覆盖测试和遮挡测试,每个子采样点都需要维护深度

3.3片元着色器

对于插值得到的片元执行片元着色器程序,输出一个或多个颜色值

4.逐片元操作(输出合并阶段)

决定每个片元的可见性

  • Alpha Test
    • 透明度小于阈值的片元就被舍弃
  • Stencil Test
  • Depth Buffer Test

如果通过测试,则把该片元颜色值和颜色缓冲区中的颜色进行合并/混合

  • Color Buffer Blending
    • Alpha Blend

输出到目标缓冲区(RT/FB)

5.后处理

  • HDR

  • Bloom

  • FXAA

  • Depth of View景深

  • 边缘检测

  • 径向模糊

  • 。。。

参考资料

[1] https://www.bilibili.com/video/BV1L54y1s7xw 【技术美术百人计划】图形 1.1 渲染流水线

[2] https://jishuin.proginn.com/p/763bfbd6e54f

[3] Unity Shader入门精要

[4] Fundamentals of Computer Graphics,4th

TA学习体系框架

TA学习体系框架

  • TA的职责

    • 为项目解决问题
    • (设计不就是解决问题吗,把设计思维运用在TA当中,我又感觉自己更适合TA了)
    • 美术的问题,程序关于渲染、流程的问题
      • 节省项目成本
      • 缩短项目周期
      • 提升项目效果
  • TA的方向

    • 程序
    • 美术
    • 工具
    • 渲染
    • 流程
    • 动作
    • 。。。

学习目标

  • 核心是学习如何解决问题
  • 区分解决问题的方法和手段
  • 多精一专

学习框架

image-20220719231108190

image-20220719231205177

image-20220719231518499

大厂的要求

入门:会使用工具、可以完成指定功能

中级:可以对流程、工具,项目上的东西可以自己修改

高级:可以把做的东西推广到流程当中

入门阶段

image-20220719232105088

image-20220719232214079

image-20220719232319370

image-20220719232355422

进阶阶段

image-20220719232721031

image-20220719232836013

image-20220719232937688

image-20220719233011772

image-20220719233059747

高级阶段

image-20220719233201038

image-20220719233208430

image-20220719233313853

2022年7月19日

独居生活的第三天也快结束了。
除了昨天准备了一天的简历和作品,其余时间基本都什么都没做。
作息和吃饭也一团糟。
从明天开始一定要进入正常的生活状态。
不然为什么要留在学校?