参考源Illumination Models and Shaing,翻译声明仅供学习交流:https://www.cnblogs.com/baolong-chen/category/1559372.html
为了尽可能得到真实感图像画面,必须要模拟物体表面在多种光照条件影响下的图像呈现
一、Lighting Model Method
Lighting Model params
light source参数
- 位置-Position
- 光谱光波-Electromagnetic Spectrum
- 光源形状-Shape
object sucface参数
- 位置-Position
- 反射率属性-Reflectance properties
- 邻近面-Position of nearby surcface
camera(eye)参数
- 镜头位置-Position Sensor
- spectrum sensitivities
Ambient Illumination-环境光
假定场景没有方向光只有环境光,且环境光入射到每个物体表面的量和方向是恒定的,这个理想的光照模型在现实世界基本不存在的。
公式:Iamb = Ka·Ia
Ia: 环境光强度,
Ka∈[0,1]: 物体表面反射率
最后把IRamb,IGamb,IBamb传递给模型顶点或像素颜色RGB
Unity中环境光宏定义:#define UNITY_LIGHTMODEL_AMBIENT (glstate_lightmodel_ambient * 2)
Diffuse Reflection-漫反射
漫反射公式:Idiff = Kd·Ip·Cos(θ) = Kd·Ip·(N·L)
Lambert余弦定律:反射强度与表面的平滑度相关,与入射角度Sin(α)成反比或者Cos(θ)成正比
Ip:光源强度,也可作为衰减光源fatt(r)Ip---光照颜色
Kd∈[0,1]:物体表面反射率
N:平面法线, (使用前要归一化运算)
L:光源方向,(使用前要归一化运算)
漫反射和环境光合并公式:I = Idiff + Iamb = Kd·Ip·(N·L) + Ka·Ia
最后把IR IG IB 传递给模型顶点或像素颜色RGB即可
//漫反射和环境光合并
//可以转换到模型空间、切线空间、世界空间
v2f vert(a2v a) { v2f v; v.pos = UnityObjectToClipPos(a.vertex); //模型空间 /*fixed3 mn = normalize(a.normal); fixed3 lightdir = normalize(ObjSpaceLightDir(a.vertex)); fixed3 val = max(0, dot(mn, lightdir)); fixed3 diff = _Smoothness * _LightColor0.rgb * val * _Diffuse.rgb; v.color = UNITY_LIGHTMODEL_AMBIENT.xyz + diff;*/ //世界空间---由于世界空间有许多信息例如环境映射、光照、视角,一般推荐转换到世界空间 fixed3 wm = normalize(mul(a.normal, (float3x3)unity_WorldToObject)); fixed3 lightdir = normalize(_WorldSpaceLightPos0.xyz); fixed3 val = max(0, dot(wm, lightdir)); fixed3 diff = _Smoothness * _LightColor0.rgb * val * _Diffuse.rgb; v.color = UNITY_LIGHTMODEL_AMBIENT.xyz + diff; return v; }
Specular Reflection-高光反射
镜面(高光)反射,模拟陶瓷、金属等光滑表面材质,反射角影响反射强度。
公式(Phong Model):Ispec = Ks·Ip·cosn(Φ) = Ks·Ip·(R·V)n
Ks:物体表面高光反射率
Ip:光源强度---光照颜色
L:光源方向
N:平面法线
R:反射方向
V:视线方向
n:高光反射系数,决定着与理想高光反射模型的偏差
R推导过程
R推导过程:理想的反射模型是等腰三角形(见图2)。L入射起点,R反射终点。
矢量R = OR = LR–LO = 2LP-LO = 2(LO+OP)-LO = LO+2OP
LO已知,那么求得LO在法向量上的投影OP即可,
取得N的单位向量n:n=OP/|OP|=N/|N|
R = 2·dot(n, L)·n – L == reflect(-L,n)
v2f vert(a2v a) { v2f v; v.position = UnityObjectToClipPos(a.vertex); fixed3 world_pos = mul(a.vertex, unity_WorldToObject); fixed3 world_n = normalize(mul(a.normal, (float3x3)unity_WorldToObject)); fixed3 world_light_dir = normalize(UnityWorldSpaceLightDir(world_pos));//顶点指向光源 fixed3 world_view_dir = normalize(_WorldSpaceCameraPos.xyz - world_pos);//顶点指向摄像机 //reflect函数参数需要从入射光到顶点方向 fixed3 world_ref_dir = normalize(2 * dot(world_n,world_light_dir)*world_n - world_light_dir);//reflect(-world_light_dir,world_n); fixed3 spec_color = _Ks * _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(world_ref_dir, world_view_dir)), _N); fixed3 diff_color = _Kd * _LightColor0.rgb * saturate(dot(world_n, world_light_dir)); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 color = ambient + diff_color + spec_color; v.color = color; return v; }
组合环境光、漫反射、高光
公式:I = Iamb + Idiff + Ispec = Ka·Ia + Ip (Kd· N ⋅ L + Ks (R⋅V)n)
v2f vert(a2v a) { /*同上...*/ fixed3 diff_color = _Kd * _LightColor0.rgb * saturate(dot(world_n, world_light_dir)); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 color = ambient + diff_color + spec_color; v.color = color; return v; }
翻译声明仅供学习交流:https://www.cnblogs.com/baolong-chen/category/1559372.html
二、Polygon Rendering Methods
Flat Shading-略
UnityStore有一款插件:Poly World
Gouraud Shading
- 获取每个多边形的顶点法线
- 带入光照模型计算每个顶点的光照强度
- 在曲面多边形线性插值顶点强度
根据上述三点,基本确定是在顶点着色内进行着色运算
高洛德着色中,公共顶点法线(红点法线)来源于所有共点面法线的平均值,如下图。主要目的是对三个顶点组成的三角面内部进行着色,而其他三角面没有被照射到,就不会有光照效果。
实现代码,参考上面的vert(a2f a)函数即可
Phong Shading
- 获取每个多边形法线
- 在曲面多边形线性插值顶点法线
- 带入光照模型计算每个片元内像素点的光照强度
上述三点,前面两点在顶点着色器计算,最后一步的着色计算在片元着色器
v2f vert(a2v a) { v2f v; v.position = UnityObjectToClipPos(a.vertex); v.world_normal = UnityObjectToWorldNormal(a.normal); v.world_position = mul(UNITY_MATRIX_M, a.vertex); v.world_light_dir = UnityWorldSpaceLightDir(v.world_position);//顶点指向光源 v.world_view_dir = UnityWorldSpaceViewDir(v.world_position);//顶点指向摄像机 return v; } fixed4 frag(v2f v) : SV_Target{ fixed3 world_pos = normalize(v.world_position); fixed3 world_n = normalize(v.world_normal); fixed3 world_light_dir = normalize(v.world_light_dir); fixed3 world_view_dir = normalize(v.world_view_dir); fixed3 world_ref_dir = normalize(2 * dot(world_n, world_light_dir) * world_n - world_light_dir); fixed3 spec_color = _Ks * _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(world_ref_dir, world_view_dir)), _N); fixed3 diff_color = _Kd * _LightColor0.rgb * saturate(dot(world_n, world_light_dir)); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; fixed3 color = ambient + diff_color + spec_color; return fixed4(color, 1.0f); }
最后效果图:高洛德着色与冯着色效果如下图: