找回密码
 立即注册
查看: 170|回复: 0

光照模型的道理和实现思路——Phong

[复制链接]
发表于 2024-7-15 18:23 | 显示全部楼层 |阅读模式
Phong光照模型

之前说过,游戏场景中计算光照时,需要考虑光源、模型材质和不雅察看标的目的三方面的因素。很多物理学家、数学家按照这些因素,再结合真实物理世界光照效果的经验,总结出几种非常经典的光照模型,比如说兰伯特(Lambert)、环境光(Ambient)、Phong、Blinn-Phong、PBR等等,此中最经典的要数Phong光照模型。
Phong光照模型是由著名学者裴祥风在1975年提出的第一个有巨大影响力的光照计算模型。这个模型只考虑物体对直接光照的反射感化,认为环境光是常量,没有考虑物体之间彼此的反射光,物体间的反射光只用环境光暗示。用一个简单的公式来暗示,就是:
光照 =  漫反射(Diffuse) + 高光反射(Specular) + 环境光(Ambient)
漫反射

漫反射用来描述,当光线从光源照射到模型概况时,该概况会向每个标的目的散射多少辐射量。**漫反射光线的强度与物体概况的法线和光源标的目的之间夹角的余弦值成正比。**这部门可以通过光源照射标的目的和法线标的目的的点积来实现。漫反射光的计算公式可以写成:



漫反射计算

此中,normalDir暗示法线标的目的,lightDir暗示光源标的目的,为了防止点积为负数,因此使用max函数将其截取到0,这样可以防止物体被后面来的光源照亮。C-light暗示光源颜色,m-light暗示材质的漫反射颜色。
法线标的目的可以通过下面的公式计算得出:
  1. // v暗示顶点
  2. normalDir = normalize(mul(float4(v.normal,0.0),unity_WorldToObject).xyz);
复制代码
光源标的目的通过使用Unity内置的变量_WorldSpaceLightPos0计算获取,不外这里涉及到光源类型问题,如果是平行光源,直接将_WorldSpaceLightPos0变量归一化,就能得到光源标的目的;但如果是点光源,还需要再减去顶点的世界空间坐标位置,然后再归一化获取光源标的目的。
  1. //i.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;
  2. // v暗示顶点
  3. // 获取平行光、点光源标的目的
  4. #if defined (DIRECTIONAL)
  5. half3 light_dir = normalize(_WorldSpaceLightPos0.xyz);
  6. #elif defined (POINT)
  7. half3 light_dir = normalize(_WorldSpaceLightPos0.xyz - i.pos_world);
  8. #endif
复制代码
除了使用max()函数截取点积成果,使用saturate()函数也可以达到同样的效果。
接下来是C-light光源颜色,光源颜色使用内置变量_LightColor0获取,不外在此之前,还需要引入内置文件“AutoLight.cginc”
最后是M-light材质颜色,直接可以通过材质纹理获取。
知道了上述参数的计算或获取方式,就能计算出漫反射颜色了,不外还有一个问题没有考虑,那就是光的衰减。
平行光凡是是用来模拟自然界的太阳光,因为太阳光有很高的强度,光的衰减几乎可以忽略不计。而对于点光来说,就必需通过计算物体到光源之间的距离来获得。具体计算方式如下:
  1. // 计算衰减
  2. #if defined (DIRECTIONAL)
  3. half attuenation = 1.0; // 衰减系数
  4. #elif defined (POINT)
  5. // 计算点光源到物体之间的具体
  6. // i.pos_world = mul(unity_ObjectToWorld,v.vertex).xyz;
  7. // v暗示顶点
  8. half distance = length(_WorldSpaceLightPos0.xyz - i.pos_world);
  9. // 衰减范围值
  10. half range = 1.0 / unity_WorldToLight[0][0];
  11. float attuenation = normalize((range - distance)/range);  // 点光源的衰减值
  12. #endif
复制代码
漫反射最终的计算公式为:
  1. half4 base_color = tex2D(_MainTex, i.uv);
  2. half3 NdotL = dot(normal_dir,light_dir);
  3. //  计算漫反射颜色
  4. half3 diffuse_color = max(0.0, NdotL) * _LightColor0.xyz * base_color.xyz * attuenation;
复制代码
高光反射

这里的高光反射是一种经验模型,并不完全符合真实物理世界中的高光反射现象。当一束光线照射到物体上时,光线会沿着物体概况法线防线朝着和入射标的目的相反的标的目的反射,这种反射的光线,可以让物体看起来更加有光泽。
此外,高光反射还和摄像机的不雅察看标的目的有关,从分歧的角度不雅察看物体,物体的高光反射也是纷歧样的,高光反射的计算公式是:



高光反射的计算

reflectDir暗示反射标的目的,viewDir暗示摄像机的不雅察看标的目的,C-light暗示光源颜色。
反射标的目的使用reflect()函数,传入光源负标的目的和法线标的目的,具体代码如下:
  1. half3 reflect_dir = reflect(-light_dir, normal_dir);
复制代码
不雅察看标的目的通过摄像机世界空间坐标位置减去顶点世界空间位置计算得出,具体代码如下:
  1. half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
复制代码
高光反射的计算代码如下:
  1. half3 view_dir = normalize(_WorldSpaceCameraPos.xyz - i.pos_world);
  2. half3 reflect_dir = reflect(-light_dir, normal_dir);
  3. half RdotV = dot(reflect_dir,view_dir);
  4. half3 spec_color = max(0.0,RdotV) * _LightColor0.xyz;
复制代码
这个高光计算的代码还是属于简单版的,大师可以引入其他变量,比如反光度(shininess)、高光强度(specIntensity)、高光遮罩(specMask)等等,灵活控制高光反射的效果。
  1. half3 spec_color = pow(max(0.0,RdotV),_Shininess) * _LightColor0.xyz * _SpecIntensity * spec_mask.rgb;
复制代码


高光反射

环境光

最后一步的添加环境光,环境光相对来说就非常简单了,可以使用纯色,或者使用一张纹理贴图来制作。
Phong光照模型最后的计算成果是
  1. half3 phong_color = diffuse_color + spec_color + _AmbientColor.xyz;
复制代码
总结

上述漫反射、高光反射和环境光的计算属于简易版本,实际在编写shader中,还可以插手更多参数实现更复杂精确的控制效果,比如说混入颜色贴图、粗拙图贴图、法线贴图。
此外,需要提醒的是:任何光照模型的计算,都需要按照衬着路径的思路来进行计算,具体思路请参考Unity中的衬着路径RenderingPath,下面给出一个光照模型shader Pass的模板:
  1. Shader ”lit/Phong”
  2. {
  3.     SubShader
  4.     {   
  5.        Tags {”RenderType”=”Opaque”}
  6.        Pass
  7.         {   
  8.             Tags { ”LightMode”=”ForwardBase” }
  9.             CGPROGRAM
  10.             #pragma multi_compile_fwdbase
  11.             #include ”AutoLight.cginc”
  12.         }
  13.         Pass
  14.         {   
  15.             Tags { ”LightMode”=”ForwardAdd” }
  16.             Blend One One
  17.             CGPROGRAM
  18.             #pragma multi_compile_fwdadd
  19.             #include ”AutoLight.cginc”
  20.             ENDCG
  21.         }
  22.     }
  23. }
复制代码
以前向衬着路径为例,它使用到了两个Pass,一个是ForwardBase,此外一个是ForwardAdd,因此在Tags 中要将这两个Pass名称传入到LightMode。
初度之外,还需要分袂添加multi_compile_fwdbase 和 multi_compile_fwdadd。
此外,还需要再引入AutoLight.cginc内置文件,这个文件包含了许多光源计算的内置变量。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-23 17:49 , Processed in 0.101043 second(s), 27 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表