前向渲染(ForwardBase)

作者:追风剑情 发布于:2018-12-25 15:22 分类:Shader

这个示例Shader支持全局光照(GI),光照处理、阴影处理(投射&接收)、Lightmap、雾效

开启雾效方式

1111.png

这里还要勾上才能在Scene窗口预览效果

222.png

Shader代码


Shader "Custom/ReceiveShadowsTest"
{
	Properties
	{
		[Header(Main Texture)]
		_MainTex ("Base (RGB)", 2D) = "white" {}

		[Space]

		//对纹理调色
		_Color ("Color", Color) = (1,1,1,1)
		//高光颜色
		_Specular ("Specular", Color) = (1,1,1,1)
		//控制高光亮度(值越小越亮)
		//[PowerSlider(2)] //定义滑条的值以指数曲线变化,参数定义了底数
		_Gloss ("Specular Power", Range(0, 30)) = 1

		//用个Toggle开关来控制是否显示光照图
		//[Toggle] //会隐式定义一个_USELIGHTMAP_ON开关 (大写属性名_ON)
		[Toggle(USE_LIGHTMAP)] //显示指定开关名称
		_UseLightmap ("Use Lightmap", Float) = 0

		[Space(5)]
		[Header(More Settings)]

		//枚举
		//渲染状态枚举只能使用UnityEngine.Rendering空间下的定义
		[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend Mode", Float) = 1
        [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend Mode", Float) = 1
		[Enum(Off, 0, On, 1)] _ZWrite ("ZWrite", Float) = 1
		[Enum(UnityEngine.Rendering.CompareFunction)] _ZTest ("ZTest", Float) = 4
		[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 2

		//需要在SubShader中声明
		//#pragma multi_compile _OVERLAY_NONE _OVERLAY_ADD _OVERLAY_MULTIPLY
		//keyword命名规则为: 大写属性名_枚举名
		//[KeywordEnum(None, Add, Multiply)] _Overlay ("Overlay mode", Float) = 0
	}
	SubShader
	{
		Tags { "RenderType" = "Opaque" }
		LOD 100

		Blend [_SrcBlend] [_DstBlend]
		ZWrite [_ZWrite]
		ZTest [_ZTest]
		Cull [_Cull]

		// 这个Pass同时处理未烘焙&已烘焙的物体显示
		Pass {
			//ForwardBase说明:
			//ambient, main directional light, vertex/SH lights and lightmaps are applied
			//只会接收环境光,最亮的平行光,逐顶点/球谐光以及光照图
			Tags { "LightMode"="ForwardBase" }

			CGPROGRAM
			//确保可以正常访问光照变量的值
			#pragma multi_compile_fwdbase
			#pragma vertex vert
			#pragma fragment frag
			#include "UnityCG.cginc"
			#include "Lighting.cginc"
			//计算阴影所用到的宏都在Autolight.cginc文件中
			#include "Autolight.cginc"

			//shader_feature这个定义的keyword无法通过脚本进行控制,只能通过Shader面板控制
			#pragma shader_feature USE_LIGHTMAP
			
			//multi_compile这个定义的keyword可以通过脚本设置开启或关闭
			//Material.EnableKeyword()、Material.DisableKeyword()
			//Shader.EnableKeyword()、Shader.DisableKeyword()
			//定义keyword,一个keyword会生成一个shader变种
			//#pragma multi_compile __ USE_LIGHTMAP

			//开启雾效
			#pragma multi_compile_fog
        	#define USING_FOG (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))

			struct appdata
			{
				//模型空间坐标
				//TRANSFER_SHADOW宏用到了这个变量名
				float3 vertex : POSITION;
				//模型空间法线
				float3 normal : NORMAL;
				//主纹理uv坐标
            	float2 uv : TEXCOORD0;
				//光照图uv坐标
				float2 uv1 : TEXCOORD1;
			};

			struct v2f
			{
				//裁剪空间坐标
				float4 pos : SV_POSITION;
				//世界空间坐标
				float3 worldPosition : TEXCOORD0;
				//世界空间法线
				float3 worldNormal : TEXCOORD1;
				//主纹理uv坐标
            	float2 uv : TEXCOORD2;
				//光照图uv坐标
				float2 uv1 : TEXCOORD3;
				
				//这个宏的作用很简单,就是声明一个用于对阴影纹理采样的坐标。
				//需要注意的是,这个宏的参数需要是下一个可用的插值寄存器的索引值
				SHADOW_COORDS(4)

				//雾
				#if USING_FOG
            	fixed fog : TEXCOORD5;
        		#endif
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed4 _Color;
			fixed4 _Specular;
			float _Gloss;

			v2f vert (appdata v)
			{
				v2f o;
				//模型坐标转裁剪空间坐标
				o.pos = UnityObjectToClipPos(v.vertex);
				//模型空间坐标转到世界空间坐标
				o.worldPosition = mul(unity_ObjectToWorld, v.vertex).xyz;
				//将法线从模型空间转到世界空间
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				//计算主纹理的uv坐标(考虑 缩放&平移)
            	o.uv = v.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;

				#if USE_LIGHTMAP
					//计算光照图的uv坐标(考虑 缩放&平移)
					o.uv1 = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
				#else
					//在顶点着色器返回之前添加这个宏.
					//这个宏用于在顶点着色器中计算上一步中声明的阴影纹理坐标
					TRANSFER_SHADOW(o);
				#endif

				//雾
				#if USING_FOG
					float3 eyePos = UnityObjectToViewPos(v.vertex);
					float fogCoord = length(eyePos.xyz);
					UNITY_CALC_FOG_FACTOR_RAW(fogCoord);
					o.fog = saturate(unityFogFactor);
				#endif

				return o;
			}

			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col;

				//环境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				//用纹理颜色作为漫反射颜色
				fixed3 albedo = tex2D(_MainTex, i.uv).rgb * _Color;

				#if USE_LIGHTMAP
					//光照图采样
					half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, i.uv1.xy);
					fixed3 bakedColor = DecodeLightmap(bakedColorTex);
					col = fixed4(ambient + albedo * bakedColor, 1.0);
				#else
					fixed3 worldNormal = normalize(i.worldNormal);
					fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPosition));
					fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPosition));
					fixed3 halfDir = normalize(worldLightDir + viewDir);
					//_LightColor0记录了最亮的平行光颜色
					//漫反射光
					fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
					//高光
					fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
					//对于Base Pass来说,它处理的逐像素光源类型一定是平行光。
					//由于平行光可以认为是没有衰减的,因此这里我们直接令衰减值为1.0
					//衰减值
					fixed atten = 1.0;
					//计算阴影值
					//如果关闭了阴影,SHADOW_COORDS和TRANSFER_SHADOW没有任何作用
					//如果关闭了阴影,SHADOW_ATTENUATION会返回1
					fixed shadow = SHADOW_ATTENUATION(i);//对阴影纹理采样
					col = fixed4(ambient + (diffuse + specular) * shadow * atten, 1.0);
				#endif

				//雾
				#if USING_FOG
					col.rgb = lerp(unity_FogColor.rgb, col.rgb, i.fog);
				#endif

				return col;
			}
			ENDCG
		}

		// 投射阴影Pass
		// 没这个Pass物体无法向其他物体投射阴影
		Pass
		{
			Name "ShadowCaster"
			Tags { "LightMode" = "ShadowCaster" }

			//开启深度写入
			ZWrite On
			//开启深度测试(<=)
			ZTest LEqual
			//关闭裁剪
			Cull Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 2.0
			//开启阴影投射
			#pragma multi_compile_shadowcaster
			#include "UnityCG.cginc"

			struct v2f {
				V2F_SHADOW_CASTER;
			};

			v2f vert( appdata_base v )
			{
				v2f o;
				TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
				return o;
			}

			float4 frag( v2f i ) : SV_Target
			{
				SHADOW_CASTER_FRAGMENT(i)
			}
			ENDCG
		}
	}
}


效果截图

4444.png3333.png

官方文档对Base Pass的说明

5555.png

标签: Shader

Powered by emlog  蜀ICP备18021003号-1   sitemap

川公网安备 51019002001593号