**Unity环境光遮蔽(Ambient Occlusion)Shader实现逻辑**

Unity环境光遮蔽(Ambient Occlusion)Shader实现逻辑
一、说明
hello大家好,我是小明。Unity Shader大龄萌新。我的shader中应该还有很多需要完善的地方,希望大佬们多多包涵,感谢指正。

二、应用情境
我们知道,很多时候,环境光遮蔽能让模型显示更加真实,且减少了实时阴影计算,更节省性能。
并且在很多运行环境中(如web端、部分移动端等),由于无法进行实时光照或出于其他原因考虑,物体与物体之间的环境光遮蔽都是需要进行AO烘焙才能实现的。
此外在很多建筑设计等场景中,“极地效果”这种非常特殊的光影需求,(可以看到物体的接近底部位置有一圈柔和而均匀的阴影,但周围并无遮挡物,这种阴影如果想在Unity实现,需要先在3D软件中烘焙,或去subPainter中绘制)也常常需要预先对物体环境光烘焙出AO贴图后,再进行制作。
在这里插入图片描述在这里插入图片描述
环境光遮蔽的其他应用需求应该不用我过多阐释,不管是在游戏的制作,还是在其他应用案例中,都屡见不鲜。
常见的AO方案也是,直接在3D软件(如3Dmax/Maya/Blender等)中拆UV,然后烘焙出AO贴图,在unity 标准着色器中赋值给occlusion即可。
unity 标准着色器中赋值给occlusion
但这样的缺点是,在标准着色器中,不管是albedo贴图,Occlusion贴图,都是使用同一套UV,以及一样的贴图采样,Texure_ST是一样的,因此当我们需要把调整Albedo贴图的重复或者缩放、偏移时,AO阴影烘焙效果也会跟着变化。
而在很多场景中:如建筑模型、大片的场地模型等,通常都是给出一小片纹理(如一片瓷砖、一个砖块、一块草皮),通过控制贴图的重复实现铺满整个面。这个时候,显然常规的AO方法就不能实现了。因此,我们需要一个新的方法来解决以上问题。

三、采用两套UV的AO实现
1、思路:我们知道,2D 贴图在 3D 模型上的显示是靠 uv mapping 来实现的,不论是 albedo 贴图还是 AO 贴图,都需要有 UV 坐标来映射到 3D 模型上。因此,解决 的思路就是,让 albedo 贴图使用第一套 UV 坐标,让 ao 贴图使用第二套 UV 坐 标,最后的像素点的 diffuse Color = albedo.rgb * ao.rgb * AOstrength,AOstrength 是自定义的控制 AO 纹理对模型影响强度的系数。

2、具体步骤
2.1
在建模软件中,先对模型的默认 UV 检视。 以 Blender 为例,进入剪辑模式后,展开 UV,即可看到模型的默认 UV 映射 信息,我们通常的 albedo 贴图都是以此映射关系对模型进行贴图上色的。在物 体数据面板,也可以看到你的 UV 贴图信息。

2.2
在此 UV 的基础上,对模型进行上色(如设置漫反射贴图等)。 这里要注意的是,这里对贴图的操作,是为了让自己知道你贴图的重复比例, 如一款瓷砖在一面墙上重复的 x/y 次数。进入到 Unity 时你依然需要重新创建材 质、调整重复次数。 到这里,基础的模型显示操作已经结束。接下来要处理 AO 环境光遮蔽效果。

2.3
在 3D 模型处理软件中,对模型拆出两套 UV。注意,拆 UV 时,要勾选创 建新的 UV 贴图。 在数据面板,我们可以看到多了新的 UV 映射关系。打开 UV 视图窗口,也 可以看到/调整新的 UV 布局。
2.4
新建一张图片并命名(如 AOBake),它会被用来记录环境光遮蔽的信息。在这里插入图片描述
2.5
设置好环境和阴影关系。如:灯光、附近的遮挡物等
*Tips:建议先把模型变成白模,一方面节省烘焙时间,另一方面降低杂色对 AO 的干扰。在这里插入图片描述
2.6
对目标物体烘焙AO至刚才新建的AOBake上
在这里插入图片描述
2.7
等待烘焙完成后,得到 AO 贴图。再次确认下 AO 贴图和 UV 是 map 的
在这里插入图片描述
2.8
将带有两套 UV 的 fbx 模型、albedo 贴图、法线贴图和 AO 贴图打包汇总。
在这里插入图片描述
——————————以上是3D建模部分的工作,接下来是Unity中的工作——————————
2.9
新建一个Material,创建一个shader,把代码都删除

2.10
编写shader
大体思路:
现在pass块中使用结构体获得模型的UV mapping信息,此时需要获取TEXCOORD0/TEXCOORD1(分别代表模型的第一套UV和第二套UV)在这里插入图片描述
在顶点函数中将UV分别设置给albedo贴图、法线贴图和ao贴图的映射关系
在这里插入图片描述
在片元函数中,对AO贴图进行采样,并使用_AOStrength系数控制阴影的程度
然后对albedo、diffuse等其他参数进行赋值。
在这里插入图片描述
至此,基本的结构已经完成,把其他的效果实现方法再补全,即可实现啦!
我们把贴图赋值,看下效果吧!
(为了让 AO 贴图的效果更佳明显,我这里对 AO 贴图又进行了点标记加工)
可以看到,调整Albedo的重复、偏移,不会影响整个模型的阴影效果哦!
在这里插入图片描述

下面是我们此次完整的代码块~

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "My2AOShader" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_BumpMap ("Normal Map", 2D) = "bump" {}
		_Specular ("Specular Color", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
		_AOTex("AOTex",2D) = "white"{}
		_AOStrength("AOStrength",Float) = 1

	}
	SubShader {
		Tags { "RenderType"="Opaque" "Queue"="Geometry"}
		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma multi_compile_fwdbase	
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _BumpMap;
			float4 _BumpMap_ST;
			fixed4 _Specular;
			float _Gloss;
			sampler2D _AOTex;
			float4 _AOTex_ST;
			float _AOStrength;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 tangent : TANGENT;
				float4 texcoord : TEXCOORD1;
				float4 texcoord1 :TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float4 uv : TEXCOORD0;
				float2 aouv :COLOR0;
				float4 TtoW0 : TEXCOORD1;  
                float4 TtoW1 : TEXCOORD2;  
                float4 TtoW2 : TEXCOORD3; 
				SHADOW_COORDS(4)
			};
			
			v2f vert(a2v v) {
			 	v2f o;
			 	o.pos = UnityObjectToClipPos(v.vertex);
			 
			 	o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
			 	o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;
				o.aouv = v.texcoord1.xy * _AOTex_ST.xy + _AOTex_ST.zw;

				TANGENT_SPACE_ROTATION;
				
				float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;  
                fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);  
                fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  
                fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;              
                o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);  
                o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);  
                o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);  				
  				TRANSFER_SHADOW(o);		 	
			 	return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
				fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				
				fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
				bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));

				fixed3 ao = pow(tex2D(_AOTex, i.aouv).r, _AOStrength);

				fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
			 	fixed3 diffuse = _LightColor0.rgb * albedo * (0.5*dot(bump, lightDir)+0.5) * ao;
			 	
			 	fixed3 halfDir = normalize(lightDir + viewDir);
			 	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(bump, halfDir)), _Gloss);			
				UNITY_LIGHT_ATTENUATION(atten, i, worldPos);
				return fixed4(ambient + (diffuse + specular) * atten, 1.0);
			}			
			ENDCG
		}
		
		Pass { 
			Tags { "LightMode"="ForwardAdd" }
			
			Blend One One
		
			CGPROGRAM
			
			#pragma multi_compile_fwdadd
			// Use the line below to add shadows for point and spot lights
//			#pragma multi_compile_fwdadd_fullshadows
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _BumpMap;
			float4 _BumpMap_ST;
			float _BumpScale;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 tangent : TANGENT;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float4 uv : TEXCOORD0;
				float4 TtoW0 : TEXCOORD1;  
                float4 TtoW1 : TEXCOORD2;  
                float4 TtoW2 : TEXCOORD3;
				SHADOW_COORDS(4)
			};
			
			v2f vert(a2v v) {
			 	v2f o;
			 	o.pos = UnityObjectToClipPos(v.vertex);
			 
			 	o.uv.xy = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
			 	o.uv.zw = v.texcoord.xy * _BumpMap_ST.xy + _BumpMap_ST.zw;

				float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;  
                fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);  
                fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);  
                fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w; 
	
  				o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
			  	o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
			  	o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);  
			 	
			 	TRANSFER_SHADOW(o);
			 	
			 	return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
				fixed3 lightDir = normalize(UnityWorldSpaceLightDir(worldPos));
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(worldPos));
				
				fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
				bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
				
				fixed3 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;
				
			 	fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(bump, lightDir));
			 	
			 	fixed3 halfDir = normalize(lightDir + viewDir);
			 	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(bump, halfDir)), _Gloss);
			
				UNITY_LIGHT_ATTENUATION(atten, i, worldPos);

				return fixed4((diffuse + specular) * atten, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}


版权声明:本文为weixin_40687637原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。