图形2.4 传统经验光照模型
- 经验模型
对真实光照的模拟,简化了真实光照的计算,并且有不错的效果
- 基于物理理论的光照模型
使用物理的度量和统计方法,效果非常真实,但是计算困难,实现也较困难
一、局部光照模型
只关心直接光照部分
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$
1.2 漫反射
光线照射到模型表面时被均匀反射到各个方向
$$
C_{diffuse} = C_{light}albedondotL
$$
1.3 镜面反射(高光反射)
光线到达物体表面发生镜面反射,观察视线在反射光线的附近便能够观察到镜面反射。
镜面反射的反射率是根据菲涅尔效应决定的。
通常使用对应的反射贴图描述物体表面的反射率,并使用光泽度(粗糙度,反光度)描述高光范围的大小
$$
C_{specular}=C_{lgiht}*m_{specular}dot(v,r)^m
\r=1-2ndotLn
$$
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反射光照模型的计算,将该颜色赋予整个面,效果如下:
改进方法:改进方法就是对三角形面的每个顶点进行着色,再对三角形面内的颜色插值,即Gouraud Shading。
3.2 Gouraud着色方法(几乎不用 )
顶点着色,片元着色通过顶点颜色插值
3.3 Phong着色方法
求出三角形顶点法向,法向插值得到三角形内部每个片元的法向,精确计算着色。
作业
1.学习先行版 基础渲染光照介绍(一),并说出能量守恒的理念在基础光照模型中的作用。
在光照模型当中,我们考虑不同的光源类型:环境光照(间接光照)与直接光照,将它们分开看待。实际上它们都是全局光照的一部分,我愿称之为“全局能量守恒 ”。
而对于某种光源,也有对于其本身的能量守恒,我愿称之为“局部能量守恒”。
那么局部能量守恒在光照模型中的表现其实就是
$$
入射光能量=反射光能量 + 表面吸收能量
$$
2.基于能量守恒的理念,自己写一套完整的光照模型,需要包含环境光照。
对于这个作业我的理解是,使用能量守恒的思想去改进传统光照模型 ,而不是直接实现一个cook-torrance模型。
环境光照
那么首先来做一下环境光照,不妨来试一下cubeMap做的环境光。
1 | texCUBElod(_CubeMap, float4(normal,0.0)); |
严格上的IBL,也是要分成漫反射和镜面反射两部分
但是为了简化,就只取漫反射好了,
因为是第一次使用这个功能,在cubemap这里卡了一会儿。

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


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

以及这个mipmap的生成方式是什么呢?
查了下文档
也就是说,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 | float4 indirect_irradiance = texCUBElod(_CubeMap, float4(normal,1.0)); |
直接光照
这部分就要考虑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 | float kS = calculateSpecularComponent(...); // 反射/镜面 部分 |
我们可以理解为把这两个比例分配给了光的能量,也可以直接认为 这是属于BRDF的一部分。
就不说cook-torrance了,总之我们先人为提供一个因子。glossy好像是个不错的选择。
1 | float3 kd = (255.0 - _Gloss)/255.0; |
漫反射的部分依然和上面一样
1 | fixed3 diffuse = _LightColor0.rgb * albedo * max((dot(normal, LightDir)),0); |
而对于高光部分。要说它能量守不守恒,也不太好衡量,毕竟能量小于入射能量就行了。无论小多少,它都是一种材质。所以就放在这里不管了。
$$
C_{light}*m_{specular}*ndotH^m
$$


但是这个模型的缺陷在于可以调整的参数太少了,不能表达足够多的材质,最直接的改进部分是对于kd的选择,
1 | float kd = (255.0 - _Gloss)/255.0; |
我们对kd做一些映射,就能够使得在同样的光滑度下,高光的能量占比更大(降低增加gloss减少高光范围的趋势),或者其实就干脆换一个参数来控制kd


总结
调参差不多就玩到这里了,整个光照模型就大概如此。
至于能量守恒的部分,首先是环境光照和直接光照的区分。
剩下的最主要的就是漫反射和镜面反射的能量分配,最后就是考虑光照计算的正确性。
1 | fixed4 frag(v2f i) : SV_Target { |
参考资料
[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