【笔记】【百人计划】图形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/