1.Lambert模型
-
-
-
- 基于Lambert余弦定理构造出的模型,只计算了漫反射部分
-
-
代码部分解析
-
- 可以看到Lambert模型中只返回了漫反射(diffuse)
-
- 再看一下diffuse是什么组成的
- 第一项为主光源的入射光颜色
- 第二项为漫反射材质的颜色
- 第三项 法线方向和光照方向的点积(cosθ=n.l)
2.Phong模型
代码部分解析
- 计算反射光方向,用着色器自带的reflect函数计算。注意:这里的光照方向是指往外的方向,所以要取反。
- 高光部分需要的反射方向(r)和观察方向(v)的余弦值(这里cos等于两者点积)
- 观察/视线方向如下:通过相机位置减去顶点像素点位置获得(世界坐标下做的运算)
-
漫反射部分已经在上边Lambert模型中计算完成
-
计算高光部分=入射光的颜色 * 高光材质颜色 * (v·r)gloss
- 环境光量(Unity的一个宏)* 漫反射材质
3.Blinn-Phone模型
- 对Phong模型进行了改进,不用反射向量来计算高光
- 关键:引入了半程向量h
- 将高光部分的计算改为了法线(n)和半程向量(h)的点积
- 指数依然是gloss(GAMES101中的p指数)
代码部分解析
- 半程向量的计算:光照方向和观察方向相加,再归一化
- NdotH:就是半程向量和法线向量的夹角的余弦。得到高光方向
4.Phong模型和Blinn-Phong模型的区别
- 总体来说就是Blinn-Phong计算更简单
- 特殊情况:Phong模型中,材质反光度很低时,一部分高光的反射向量和观察方向夹角超过90°,就会产生高光缺失、断层的效果。
5.Gourand模型
- 逐顶点计算
- 镜面高光效果差
- 对应的着色频率:Gouraud shading(逐顶点)算出顶点法线,然后插值
- 是在顶点着色器中计算的。(因为是逐顶点的)
- 顶点颜色使用的是Phong模型的结果
- 高光部分用的是观察方向和反射方向的余弦值
- 计算完之后将它作为顶点所携带的颜色
- 在片元/像素着色器中,直接将顶点携带的颜色赋值给了返回的颜色
具体实现代码
Shader "Custom/BaseLighting" { Properties { _Diffuse ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Specular("Specular",Color) = (1,1,1,1) _Glossiness ("Smoothness", Range(1.0,255)) = 20 _NormalMap("NormalMap",2D) = "white"{} _LocalNormalSild("LocalNormal",Range(0,1)) = 0 _CubeMap("Env Map",CUBE)= ""{} _MipScale("MipScale",Range(0,10)) =0 _EnvScale("EnvScale",Range(0,1)) = 1 [Toggle] _Phong("Phone", Int) = 0 [Toggle] _BlinnPhong("Binn Phone", Int) = 0 [Toggle] _IBL("IBL", Int) = 0 //_Metallic ("Metallic", Range(0,1)) = 0.0 } SubShader { Tags{"RenderType" = "Opaque"} LOD 100 Pass{ Tags { "LightMode"="ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma shader_feature _PHONE_ #pragma shader_feature _BlinnPhone #include "UnityCG.cginc" #include "Lighting.cginc" float4 _Diffuse; float4 _Specular; float _Glossiness; float _LocalNormalSild; sampler2D _MainTex ; float4 _MainTex_ST; uniform sampler2D _NormalMap; uniform float4 _NormalMap_ST; samplerCUBE _CubeMap; float _MipScale; float _EnvScale; bool _Phong; bool _BlinnPhong; bool _IBL; struct appdata{ float4 vertex :POSITION; float2 uv:TEXCOORD0; float3 normal :NORMAL; float4 tangent :TANGENT; }; struct v2f { float4 Pos :SV_POSITION; float3 worldNormal: NORMAL; float2 uv :TEXCOORD0; float3 tangentDir :TEXCOORD1; float3 bitangentDir :TEXCOORD2; float3 worldPos :TEXCOORD3; float3 normalDir: TEXCOORD4; }; v2f vert(appdata v){ v2f o; o.Pos = UnityObjectToClipPos(v.vertex); o.uv = v.uv; o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz; o.worldNormal = UnityObjectToWorldNormal(v.normal); //计算切线和副切线 o.normalDir = UnityObjectToWorldNormal(v.normal); o.tangentDir = normalize(mul(unity_ObjectToWorld,float4(v.tangent.xyz,0.0)).xyz); o.bitangentDir = normalize( cross(o.normalDir,o.tangentDir).xyz*v.tangent.w);//乘tangent.w来确定切线方向 return o; } fixed4 frag(v2f i):SV_Target{ float4 MainTex = tex2D(_MainTex,TRANSFORM_TEX(i.uv,_MainTex));//TRANSFORM_TEX用于控制纹理贴图的缩放和位移 float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz *_Diffuse*MainTex.rgb; //光方向 float3 worldLight = normalize(_WorldSpaceLightPos0.xyz); float3 worldNormal = normalize(i.worldNormal); i.worldNormal = worldNormal; float3 LightColor = _LightColor0.rbg; //TBN float3x3 tangentTransform = float3x3(i.tangentDir,i.bitangentDir,i.normalDir); //获取映射过去的法线数据(法线自身数据 需要通过TNB矩阵变换到世界空间) float3 normalLocal = UnpackNormal(tex2D(_NormalMap,TRANSFORM_TEX(i.uv,_NormalMap))); float3 normalWorld = normalize(mul(normalLocal,tangentTransform)); float3 finiNormal = lerp(worldNormal,normalWorld,_LocalNormalSild); float NotL = max(0.0,dot(finiNormal,worldLight)); //lambert计算法线后背面一般为黑色 这里使用环境光和光照做插值 float3 diffuse = lerp(ambient.rgb*_Diffuse.rgb*MainTex.rgb,_LightColor0.rgb*_Diffuse.rgb*MainTex.rgb,NotL); //Lambert 结果 float3 color = LightColor*MainTex.rgb*NotL; //Phong (比Lambert多了环境光和高光反射) //-worldLight表示入射光方向 float3 reflectDir = normalize(reflect(-worldLight,finiNormal)); float3 viewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz); float VdotR = max(0.0,dot(reflectDir,viewDir)); float3 specular = _LightColor0.rgb*_Specular.rgb*pow(VdotR,_Glossiness); //phone结果 if(_Phong){ color = diffuse + ambient+ specular; } //Blinn-phong(比Phong在计算高光时使用半程向量 计算更快 光照更真实 ) float3 halfDir = normalize(worldLight+viewDir); // float NdotH = saturate(dot(halfDir,finiNormal));//将数值规范在0-1 specular = _LightColor0.rgb*_Specular.rgb*pow(NdotH,_Glossiness); //Blin-phong结果 if(_BlinnPhong){ color = diffuse + ambient+ specular; } //环境贴图 float3 worldRef = normalize(reflect(-viewDir,finiNormal)); float4 reflcol = texCUBElod(_CubeMap,float4(worldRef.rgb,(255-_Glossiness)*8/(255)))*_EnvScale; specular = _LightColor0.rgb*_Specular.rgb*pow(NdotH,_Glossiness); specular = lerp(diffuse*specular,specular,_Glossiness/255); reflcol.rgb = lerp(reflcol*diffuse.rgb,reflcol,_Glossiness/255); if(_IBL){ color = diffuse + reflcol.rgb + specular; } //float3 worldNormal = normalize(i.worldNormal); //float3 worldLight = normalize(_WorldSpaceLightPos0.xyz);//世界光方向 //float3 lightColor = _LightColor0.rgb; //float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Diffuse; //float3 directLight = max(0,dot(worldNormal,worldLight))*lightColor; //float3 MainTex = tex2D(_MainTex,i.uv).rgb; //float3 diffuseColor = MainTex *(directLight+ambient); return float4(color,1); } ENDCG } } FallBack "Diffuse" }
效果对比
Lambert
Phong
BlinnPhong