现实中光照是一件非常复杂的事情,人眼能看到的某个物体是因为该物体反射了光进入了人的眼晴,而物体反射光的来源并不单单只来源于光源,也包含其他物体反射出来的光,也就是说光线是在物体之间反复弹射(举个例子:光滑的瓷砖表面会折射出其他物体),除此之外,光滑的物体还会使人眼接收到更亮高光效果,所以说想要达到更加逼真的光照效果单靠Lambert光照模型模拟的漫反射效果是远远不够的。
原理参考资料: 现代计算机图形学入门-闫令琪 第8讲 (说明图片来源该课件截图)
Phong光照模型
\(L=L_{a}+L_{d}+L_{s}\)
即: 光照最终结果=环境光照+漫反射光照+镜面反射光照
1.环境光
由于\(L_{a}\)过于复杂(涉及到光照追踪),在该模型中被直接简化成\(L_{a}=k_{a}I_{a}\),即一个光照强度乘以一个常量,目的只有一个,让一个没有受到光源照射的物体不会那么黑(有个默认颜色),这里可以参考之前Lambert光照模型里面的图片效果
这里解释一下,但凡我们能看到的物体都一定反射了光,哪怕它没有直接接受光源的光,它也会反射来自其他地方的光,就是上面所说的接收了其他物体反射出来的光
2.漫反射
直接参考前文所讲的Lambert光照模型
3.镜面反射(核心点)
如图紫色方块代表单位受光面积(受光点),向量L为光入射方向,向量R为反射方向,向量V为观察方向,观察上面可以发现,当向量R与向量V接近时(夹角趋向0),人眼会受到高光效果。所以可以得出以下公式
\(L_{s}=k_{s}(I/r^{2})\max(0,\vec{R}\cdot\vec{V})^{p}\)
(衰减与系数参考Lamert光照模型)
指数系数p的作用:
在现实生活中,高光一般只存在于光的反射方向与观察方向夹角趋于0时才会有,稍微偏一点角度都不会看到高光效果,上图展示了不同指数的变化曲线,可以发现当指数越大,曲线变化越剧烈,所以公式中的指数系数p,就是用来处理这个现象的
BlinnPhong光照模型
BlinnPhong光照模型是Phong的改进型,Phong模型中向量R的计算非常的耗时(光照是逐片元计算的)
通过上图观察,向量V与量向R之间的夹角可以转换成向量H与法线向量N的夹角
而\(\vec{H}=\vec{L}+\vec{V}\) (计算量直接变成两个相量相加)
所以Phong模型的公式可以优化成:
\(L_{s}=k_{s}(I/r^{2})\max(0,\vec{N}\cdot (\vec{L}+\vec{V}))^{p}\)
最后完整公式:
\(L=L_{a}+L_{d}+L_{s}\)
\(=k_{a}I_{a}+k_{d}(I/r^{2})\max(0,\vec{N}\cdot\vec{L})+k_{s}(I/r^{2})\max(0,\vec{N}\cdot (\vec{L}+\vec{V}))^{p}\)
UnityShader 实现代码如下:
Shader "Custom/BlinnPhongLight" { Properties { _MainTex("MainTex",2D) = "white"{} _Gloss("Gloss",Range(8.0,256)) = 20 } SubShader { LOD 100 Pass { Name "LambertLight" Tags{"LightMode" = "ForwardAdd"} CGPROGRAM #pragma vertex vert #pragma fragment frag uniform float4 _LightColor0; struct a2v { float4 model_vertex : POSITION; float3 model_normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 clip_pos : SV_POSITION; float2 uv : TEXCOORD0; float3 world_pos : TEXCOORD1; float3 world_normal : TEXCOORD2; float4 objPos : TEXCOORD3; }; sampler2D _MainTex; fixed _Gloss; v2f vert(a2v v) { v2f o; o.uv = v.uv; o.clip_pos = UnityObjectToClipPos(v.model_vertex); o.world_normal = mul((float3x3)unity_ObjectToWorld, v.model_normal); //UnityObjectToWorldNormal(v.model_normal); o.world_pos = mul(unity_ObjectToWorld, v.model_vertex).xyz; o.objPos = v.model_vertex; return o; } fixed4 frag(v2f i) : SV_Target { //顶点法线(世界空间) fixed3 world_normal = normalize(i.world_normal); //光入射方向(世界空间) fixed3 world_light = _WorldSpaceLightPos0.xyz - i.world_pos.xyz; fixed3 world_light_dir = normalize(world_light); //观察方向 fixed3 world_view_dir = normalize(_WorldSpaceCameraPos.xyz - i.world_pos.xyz); //距离光源的位置 float world_light_distance = length(world_light); //衰減系数 float atten = 1 / pow(world_light_distance, 2); //环境光(固定系数) fixed3 ambientColor = UNITY_LIGHTMODEL_AMBIENT.rgb; //漫反射 fixed3 diffuseColor = atten * _LightColor0.rgb * max(0, dot(world_normal, world_light_dir)); //镜面反射 fixed3 specularColor = atten * _LightColor0.rgb * pow(max(0, dot(world_normal, normalize(world_view_dir + world_light_dir)) ), _Gloss); //最终光颜色 fixed3 lightColor = ambientColor + diffuseColor +specularColor; //贴图颜色 fixed4 texture_color = tex2D(_MainTex, i.uv); //叠加光颜色 fixed3 finalColor = texture_color.rgb * lightColor; return fixed4(finalColor, 1.0); } ENDCG } } }
最终效果如下:
文章评论