Unity3D ShaderLab 漫反射卷积光照模型
漫反射卷积【Diffuse convolution】是一个模糊立方体的过程,它保留了立方图的整体光照强度,只模糊了细节。
这种效果在我们要活得一个更具全局光照表面效果的时候非常有用。
为了实现这种效果,我们需要创建一个卷积运算的立方图。比如ATI的工具制作CubeMapGen。
下载地址:
http://developer.amd.com/tools-and-sdks/archive/legacy-cpu-gpu-tools/cubemapgen/
安装完毕后,我们通过右上角的LoadBasemap,LoadCubeMap配置贴图,在设置一下FilterType,Filter Angle等参数后点击右下角的FilterCubeMap按钮。
等待一会就可以Filter完成,点击Save CubeMap to Images按钮,就可以保存我们的立方体图了。
然后就是新建Shader,Material。双击脚本,计入编辑器模式。
1>修改Properties:
Properties { _MainTint("Global Tint",Color)=(1,1,1,1) _BumpMap ("Normal Map", 2D) = "bump" {} _AOMap("Ambient Occlusion Map",2D)="white"{} _CubeMap("Diffuse Convolution CubeMap",Cube)=""{} _SpecIntensity("Specular Intensity",Range(0,1))=0.4 _SpecWitdth("Specular Width",Range(0,1))=0.2 }
2>修改SubShader变量
CGPROGRAM #pragma surface surf DiffuseConvolution #pragma target 3.0 samplerCUBE _CubeMap; sampler2D _BumpMap; sampler2D _AOMap; float4 _MainTint; float _SpecIntensity; float _SpecWitdth; struct Input { float2 uv_MainTex; float2 uv_AOMap; float3 worldNormal; INTERNAL_DATA };
我们需要模型的世界法线,所以加入INTERNAL_DATA。因为着色器包含一张法线贴图,我们使用它获得修改后的法线。
3>完成光照函数
inline fixed4 LightingDiffuseConvolution(SurfaceOutput s,fixed3 lightDir,fixed3 viewDir,fixed atten){ //计算光照向量; viewDir = normalize(viewDir); lightDir = normalize(lightDir); s.Normal = normalize(s.Normal); float NdotL = dot(s.Normal,lightDir); float3 halfVec = normalize(lightDir+viewDir); //计算高光; float spec = pow(dot(s.Normal,halfVec),s.Specular*128)*s.Gloss; //光照模型赋值; fixed4 c; c.rgb = (s.Albedo*atten)+spec; c.a = 1; return c; }
4>surf处理贴图
void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_AOMap, IN.uv_AOMap); float3 normals = UnpackNormal(tex2D(_BumpMap,IN.uv_AOMap)).rgb; o.Normal = normals; float3 diffuseVal = texCUBE(_CubeMap,WorldNormalVector(IN,o.Normal)).rgb; o.Albedo = (c.rgb*diffuseVal)*_MainTint; o.Specular = _SpecWitdth; o.Gloss = _SpecIntensity*c.rgb; o.Alpha = c.a; }
完成了光照函数后,使用模型的世界法线来对卷积后的立方图进行纹理映射,然后把结果传入output结构体。
返回Unity编辑器,最终效果如下:
通过上面的函数,我们首先得到被法线贴图修改后的模型的世界法线,并利用法线数据来查找立方图上的位置以得到他的像素和颜色,
这就是我们要在Input结构体重申明float3 worldNormal,以及INTERNAL_DATA原因。
然后我们使用WorldNormalVector函数来得到最终的法线向量,并用于texCUBE的检索。
Shader "91YGame/CubeMapLight" { Properties { _MainTint("Global Tint",Color)=(1,1,1,1) _BumpMap ("Normal Map", 2D) = "bump" {} _AOMap("Ambient Occlusion Map",2D)="white"{} _CubeMap("Diffuse Convolution CubeMap",Cube)=""{} _SpecIntensity("Specular Intensity",Range(0,1))=0.4 _SpecWitdth("Specular Width",Range(0,1))=0.2 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf DiffuseConvolution #pragma target 3.0 samplerCUBE _CubeMap; sampler2D _BumpMap; sampler2D _AOMap; float4 _MainTint; float _SpecIntensity; float _SpecWitdth; struct Input { float2 uv_MainTex; float2 uv_AOMap; float3 worldNormal; INTERNAL_DATA }; inline fixed4 LightingDiffuseConvolution(SurfaceOutput s,fixed3 lightDir,fixed3 viewDir,fixed atten){ //计算光照向量; viewDir = normalize(viewDir); lightDir = normalize(lightDir); s.Normal = normalize(s.Normal); float NdotL = dot(s.Normal,lightDir); float3 halfVec = normalize(lightDir+viewDir); //计算高光; float spec = pow(dot(s.Normal,halfVec),s.Specular*128)*s.Gloss; //光照模型赋值; fixed4 c; c.rgb = (s.Albedo*atten)+spec; c.a = 1; return c; } void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_AOMap, IN.uv_AOMap); float3 normals = UnpackNormal(tex2D(_BumpMap,IN.uv_AOMap)).rgb; o.Normal = normals; float3 diffuseVal = texCUBE(_CubeMap,WorldNormalVector(IN,o.Normal)).rgb; o.Albedo = (c.rgb*diffuseVal)*_MainTint; o.Specular = _SpecWitdth; o.Gloss = _SpecIntensity*c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }