shader-光照

unity渲染路径

渲染路径(Rendering Path)决定光照如何应用到shader中 ----> 在Pass中指明使用的渲染路径。

通过设置Pass的 LightMode标签实现;

向前渲染路径

没进行一次完整的前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息:一个颜色缓冲区,一个深度缓冲区。利用深度缓冲来决定一个片元是否可见,如果可见就更新颜色缓冲区的颜色值。

//遍历每个片元
             for (each primitive in this model){
                  if(failed in depth test){
                     //没通过深度测试,该片元不可见
                     discard;
                  }else{
                      //该片元可见,就进行光照计算
                      float4 color=Shading(material,pos,normal,lightDir,viewDir);
                      //更新帧缓冲
                      writeFrameBuffer(fragment,color);
                  }
             }         

对每个逐像素光照,都需要进行上面的完整渲染流程。如果在多个逐像素的光照下,需要执行多个pass,每个pass计算一个逐像素光源,在帧缓冲中将这个光照计算结果混合起来得到最终的颜色值。若存在大量逐像素光照下,需要执行的pass数目很大,所以渲染引擎会限制每个物体的逐像素光照数目。

unity的前向渲染路径有三种处理光照的方式:逐顶点处理、逐像素处理、球谐函数处理(SH);决定一个光源使用哪种处理模式取决于它的类型和渲染模式。光源类型指的是该光源是平行光还是其他类型的光源,而光源的渲染模式指的是该光源是否是重要的(Important)。

    Auto、Important、Not Important

在向前渲染中,unity会根据场景中的各个光源的设置以及这些光源对物体的影响程度对这些光源进行一个重要度排序。判断规则:

  • 场景中最亮的平行光总是按逐像素处理的。

  • 渲染模式被设置为Not Important 的光源,会按照逐顶点或SH处理。

  • 设置为Important的光源,会按照逐像素处理。

  • 若根据以上规则得到的逐像素光源小于Quality Setting中逐像素光源数量(Pixel Light Count),会有更多的光源以逐像素的方式进行渲染。如果超过是设置的逐像素光源数量,后面最多有4个光源按逐顶点的方式处理,剩下的光源按SH方式处理。

向前渲染的两种Pass:Base Pass、Additional Pass;

1. #pragma multi_compile_fwdbase和#pragma multi_compile_fwdadd。只有为base和add Pass这两个编译指令,才能得到正确的光照变量。如光照衰减值。

2. add Pass默认是没有阴影效果的。需要使用#pragma multi_compile_fwdadd_fullshadows。为点光源和聚光灯开启阴影效果,但是需要使用更多的shader变量。

3. base中,对一个物体来说,环境光和自发光只需要计算一次。add 中计算这两种光照会造成叠加多次环境光和自发光。

4. add中需要开启混合。每个add 可以和上一次光照结果的帧缓存进行叠加,从而得到多个光照的渲染效果。若没开启混合,则会覆盖上一次的渲染结果,造成物体在多个光源中,看起来只受该光源的影响。通场混合设置为Blend One One。

5. 前向渲染,通常会定义一个Base和Add ,base执行一次,add会根据影响该物体的其他逐像素光源的数目被多次调用,即每个逐像素光源会执行一次add。

根据使用的渲染路径,unity会把不同的光照变量传递给shader。

顶点照明渲染路径

顶点照明渲染路径对硬件配置要求低、运算性能最高,必然效果也是最差的一种类型,不支持逐像素才能得到的效果,如阴影、法线映射、高精度的高光反射等。不可以使用逐像素光照变量。按照逐顶点处理,这是最快速的渲染路径,现在基本不会使用。

延迟渲染路径

当场景中包含大量实时光源时,前向渲染性能会急速下降-----会进行大量的重复计算。除了前向渲染中使用的颜色缓冲和深度缓冲,延迟渲染还会使用额外的缓冲区,同称为G缓冲(Geometry-Buffer)。它存储了所关心的表面(距离摄像机最近的表面)的其他信息,如法线、位置、材质属性等。

延迟渲染只要包含两个pass,第一个pass中不进行光照计算,只计算那些片元是可见的---通过深度缓冲技术实现,发现一个片元是可见的,就把它的相关联信息存储到G缓冲区中。第二个pass利用G缓冲区的各个片元信息进行真正的关照计算。延迟渲染pass通常就是两个,与场景中包含的光源数目没有关系,延迟渲染的效率不依赖场景的复制度,而是和使用的屏幕空间的大小有关。因为所需要的信息都存储在缓冲区中,缓冲区可以理解为一张张2D图像,实际计算就是在这些图像空间中进行。

最适合在场景中光源数目众多是使用,延迟渲染中的每个光源都可以按照逐像素的方式处理。存在的缺点:

  • 不支持真正的抗锯齿(anti-aliasing)。
  • 不能处理半透明物体。
  • 对显卡有一定的要求,显卡必须支持MRT、shader mode3.0及以上、深度渲染纹理、双面的模板缓冲。
//第一个pass仅仅把光照计算需要的信息存储到GBuffer。
       //会把物体的漫反射颜色、高光反射颜色、平滑度、法线、自发光、深度等信息渲染到屏幕空间的GBuffer中,每个物体只会执行一次。
       //默认的G缓冲包含的渲染纹理:
       // RT0:ARGB32,RGB存储漫反射颜色,A没用到。
       // RT1:ARGB32,RGB存储高光反射颜色,A存储高光反射的指数部分。
       // RT2:ARGB2101010,RGB存储法线,A没有用到。
       // RT3:ARGB32(非HDR)或ARGBHalf(HDR),存储自发光+lightmap+反射探针。
       //深度缓冲和模板缓冲
       Pass{
          CGPROGRAM
             //遍历每个片元
             for (each primitive in this model){
                  for(each fragment covered by this primitive){
                      if(failed in depth test){
                         //没通过深度测试,该片元不可见
                         discard;
                      }else{
                          //该片元可见
                          //存储到GBuffer
                          writeGBuffer(material,pos,normal,lightDir,viewDir);
                      }
                  }
                  
             }          
          ENDCG
       }
       //第二个pass利用GBuffer中的信息进行真正的光照计算。
       //默认使用unity内置的standard光照模型。如使用其他,需要替换Internal-DeferredShading.shader文件。
       Pass{
          CGPROGRAM
             for (each pixel in this screen){
                  if(the pixel is valid){
                      //该像素有效
                      //读取对应的GBuffer
                      readGBuffer(pixel,pos,normal,lightDir,viewDir)
                      //根据读取到的数据进行光照计算
                      float4 color=shading(material,pos,normal,lightDir,viewDir)
                      //更新到帧缓冲
                      writeFrameBuffer(pixel,color);
                  }
             }          
          ENDCG
       }

渲染路径的选择--->根据游戏发布的平台选择渲染路径。

光源类型的影响

常用的光源有光源的位置、方向、颜色、强度、衰减。

  • 平行光:几何定义最简单,没有范围限制,几何属性没有位置(也就没有衰减的概念)只有方向。

  • 点光源: 由一个点发出、向所有方向延伸的光。随点光源球心点距离,强度逐渐变弱。

  • 聚光灯:最为复杂的一种,需要判断一个点是否在锥体内。

Shader "Unlit/10"
{
   Properties{
       _Diffuse("",Color)=(1,1,1,1)
       _Specular("",Color)=(1,1,1,1)
       _Gloss("",Range(1,256))=8
   }
   
   SubShader{
       Pass{
          //base pass
          Tags{"LightMode"="ForwardBase"}
          CGPROGRAM
             #pragma vertex vert
             #pragma fragment frag
            //该指令保证光照变量正确赋值
             #pragma multi_compile_fwdbase
             #include "Lighting.cginc"
             
             fixed4 _Diffuse;
             fixed4 _Specular;
             fixed _Gloss;
             struct a2v{
                float4 vertex:POSITION;
                float3 normal:NORMAL;
             };
             struct v2f{
                 float4 pos:SV_POSITION;
                 float3 worldPos:TEXCOORD0;
                 float3 worldNormal:TEXCOORD1;
             };             
             v2f vert(a2v a){
                v2f v; 
                v.pos=UnityObjectToClipPos(a.vertex);
                v.worldPos=mul(unity_ObjectToWorld,a.vertex).xyz;
                v.worldNormal=UnityObjectToWorldNormal(a.normal).xyz;
                return v;
             }            
             //对 base来说,像素光源处理一定是平行光。可以直接使用_WorldSpaceLightPos0获取平行光方向
             //_LightColor0获取颜色和强度
             fixed4 frag(v2f v):SV_Target{
                fixed3 worldNormal = normalize(v.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
			 	fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));

			 	fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - v.worldPos.xyz);
			 	fixed3 halfDir = normalize(worldLightDir + viewDir);
			 	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

				fixed atten = 1.0;
				
				return fixed4(ambient + (diffuse + specular) * atten, 1.0);
             }             
          ENDCG
       }
       Pass{
          // add pass 逐像素
          Tags{"LightMode"="ForwardAdd"}
          //如果不使用混合,会覆盖之前的光照结果
          Blend One One
          CGPROGRAM
              //该命令保证add中能得到正确的光照变量
              #pragma multi_compile_fwdadd
              #pragma vertex vert
              #pragma fragment frag
              #include "Lighting.cginc"
              
             fixed4 _Diffuse;
             fixed4 _Specular;
             fixed _Gloss;
             struct a2v{
                float4 vertex:POSITION;
                float3 normal:NORMAL;
             };
             struct v2f{
                 float4 pos:SV_POSITION;
                 float3 worldPos:TEXCOORD0;
                 float3 worldNormal:TEXCOORD1;
             };             
              v2f vert(a2v a){
                v2f v; 
                v.pos=UnityObjectToClipPos(a.vertex);
                v.worldPos=mul(unity_ObjectToWorld,a.vertex).xyz;
                v.worldNormal=UnityObjectToWorldNormal(a.normal).xyz;
                return v;
             } 
             fixed4 frag(v2f v):SV_Target{
                 fixed3 worldLightDir;
                 //光照位置、方向、衰减需要根据光照类型分别计算。
                 #ifdef USING_DIRECTIONAL_LIGHT 
                       worldLightDir=normalize(_WorldSpaceLightPos0.xyz);
                 #else            
                      //非平行光 _WorldSpaceLightPos0 世界光照 需要减去世界顶点位置 --》正确的光照方向
                       worldLightDir=normalize(_WorldSpaceLightPos0.xyz - v.worldPos.xyz);
                 #endif
                 
                 fixed3 worldViewDir=normalize(UnityWorldSpaceViewDir(v.worldPos));
                 fixed3 worldNormal=normalize(v.worldNormal);
                 
                 fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
                 fixed3 diffuse=_LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLightDir));
                 
                 fixed3 halfDir=(worldViewDir+worldLightDir);
                 fixed3 specular=_LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal,halfDir)),_Gloss);
                 fixed atten = 1.0;
				
				return fixed4(ambient + (diffuse + specular) * atten, 1.0);
             }        
          ENDCG
       }
   }
   FallBack ""
}

点光源排序--按照重要度进行排序(Important),如果颜色和强度都相同,则根据距离进行排序。

光照衰减

unity提供一张纹理作为查找表来在片元着色器中计算逐像素光照的衰减。好处-->不依赖复杂的数学公式,只要一个参数值采样纹理即可。缺点:需要预处理得到采样纹理,纹理大小也会影响衰减精度;不直观,数据存储到了查找表中,无法使用其他数学公式来计算衰减。这种方法可以在一定程度上提升性能,unity默认使用纹理查找方式计算逐像素的点光源和聚光灯的衰减。

unity内部使用的是_LightTexture0的纹理来计算光照衰减。如果对该光源使用了cookie,那么衰减查找纹理是_LightTextureB0。

//通过_LightTexture0 纹理采样得到给定点到该光源的衰减值,首先需要得到该光源空间中的位置,通过_LightMatrix0变换矩阵得到
                fixed3 lightCoord=mul(unity_WorldToLight,float4(v.worldPos,1)).xyz;
                //使用这个坐标对衰减纹理进行采样  _LightTexture0 默认的纹理来计算光照衰减
                //UNITY_ATTEN_CHANNEL 由宏定义得到衰减纹理中衰减值所在的分量,已得到最终的衰减值。
                fixed atten=tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;
                
                /*//通过公式计算  由于无法通过内置函数得到光源的范围、聚光灯朝向、张开角度等,得到的效果不好。
                float distance=length(_WorldSpaceLightPos0.xyz-v.worldPos.xyz);
                //得到线性衰减
                fixed atten_ =1.0/distance;*/

阴影

投射阴影和接收阴影。当一个光源发射光线遇到不透明物体时,这条光线将不再照亮其他物体,因此该物体就会向它旁边的物体投射阴影--光线无法到达的地方。实时渲染中使用名为Shadow Map的技术------首先把摄像机的位置放在与光源重合的位置上,那么场景中该光源的阴影部分就是摄像机看不到的部分。在前向渲染中,如果场景中最重要的平行光开启了阴影,unity会为该光源计算它的阴影映射纹理(ShadowMap)--本质是一张深度图,记录了从该光源的位置出发、能看到的场景中距离它最近的表面位置(深度信息)。

unity使用了额外的pass专门更新光源的阴影映射纹理,LightMode=ShadowCaster,渲染目标是阴影映射纹理(深度纹理)。首先把摄像机放在光源的位置上,调用该pass,通过顶点变换后得到光源空间下的位置,并据此输出深度信息到阴影映射纹理中。开启光源阴影后,渲染引擎首先会在当前渲染物体中找到ShadowCaster的pass,没有FallBack继续找指定的shader,还没有,则无法向其他物体投射阴影。找到后,使用该pass更新光源的阴影映射纹理。

总之:

想要一个物体接收其他物体的阴影,就必须在shader中对阴影映射纹理(包括屏幕空间的阴影图)进行采样,把采样结果和最后的光照结果相乘来产生阴影效果。

向其他物体投射阴影,把采样结果和最后的光照结果加入到光源的阴影映射纹理计算中,从而其他物体在对阴影映射纹理采样时可以得到该物体的相关信息。unity中这个过程通过执行LightMode为ShadowCaster的pass来实现。如果使用了屏幕空间的投影映射技术,还会使用该pass生成一张摄像机的深度纹理。

// Upgrade NOTE: replaced '_LightMatrix0' with 'unity_WorldToLight'

Shader "Unlit/11"
{
    Properties
    {
        _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        Pass
        {
            Tags{"LightMode"="ForwardBase"}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdbase
            
            #include "Lighting.cginc"
            //使用阴影时所用的宏都是这个文件定义的
            #include "AutoLight.cginc"
            
            fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
            
             struct a2v{
                float4 vertex : POSITION;
				float3 normal : NORMAL;
             };
             struct v2f{
                 float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				//声明一个用于对阴影纹理采样的坐标。参数需要是下一个可用的插值寄存器的索引值
				SHADOW_COORDS(2)
             };             
             v2f vert(a2v v) {
			 	v2f o;
			 	o.pos = UnityObjectToClipPos(v.vertex);
			 	o.worldNormal = UnityObjectToWorldNormal(v.normal);
			 	o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			 	// 在顶点着色器中计算阴影纹理坐标
			 	//注意:在计算时会使用v.vertex来计算坐标,所以在定义变量时,名称要一样
			 	TRANSFER_SHADOW(o);
			 	return o;
			} 
             fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
			 	fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
			 	fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
			 	fixed3 halfDir = normalize(worldLightDir + viewDir);
			 	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				fixed atten = 1.0;
				//计算阴影值
				fixed shadow = SHADOW_ATTENUATION(i);
				return fixed4(ambient + (diffuse + specular) * atten * shadow, 1.0);
			}
            ENDCG
        }
        Pass
        {
            Tags{"LightMode"="ForwardAdd"}
            Blend One One
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fwdadd
            //该指令用于点光源和聚光灯
            //#pragma multi_compile_fwdadd_fullshadows
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            
            fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
            
             struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
				float4 position : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};
			
			v2f vert(a2v v) {
			 	v2f o;
			 	o.position = UnityObjectToClipPos(v.vertex);
			 	o.worldNormal = UnityObjectToWorldNormal(v.normal);
			 	o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			 	return o;
			}
             fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				#ifdef USING_DIRECTIONAL_LIGHT
					fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				#else
					fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - i.worldPos.xyz);
				#endif
				
			 	fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
			 	fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
			 	fixed3 halfDir = normalize(worldLightDir + viewDir);
			 	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
			 	
				#ifdef USING_DIRECTIONAL_LIGHT
					fixed atten = 1.0;
				#else
					float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
					fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
				#endif
				
				return fixed4( (diffuse + specular) * atten , 1.0);
			}
            ENDCG
        }
    }
    //没有实现LightMode=ShadowCaster 调用内置的Specular->最终调用VertexLit
    FallBack "Specular"
}

内置的宏UNITY_LIGHT_ATTENUATION可进行内置的计算衰减和阴影。

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

Shader "Unlit/12" {
	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		
		Pass {
			// Pass for ambient light & first pixel light (directional light)
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			// Apparently need to add this declaration
			#pragma multi_compile_fwdbase	
			
			#pragma vertex vert
			#pragma fragment frag
			
			// Need these files to get built-in macros
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
                //占用了两个插值寄存器
				SHADOW_COORDS(2)
			};
			
			v2f vert(a2v v) {
			 	v2f o;
			 	o.pos = UnityObjectToClipPos(v.vertex);
			 	o.worldNormal = UnityObjectToWorldNormal(v.normal);
			 	o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			 	
			 	// Pass shadow coordinates to pixel shader
			 	TRANSFER_SHADOW(o);
			 	
			 	return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
			 	fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
			 	fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
			 	fixed3 halfDir = normalize(worldLightDir + viewDir);
			 	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

				// UNITY_LIGHT_ATTENUATION not only compute attenuation, but also shadow infos
				//// UNITY_LIGHT_ATTENUATION 内置的宏,即可计算衰减,也可计算阴影
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
				
				return fixed4(ambient + (diffuse + specular) * atten, 1.0);
			}
			
			ENDCG
		}
	
		Pass {
			// Pass for other pixel lights
			Tags { "LightMode"="ForwardAdd" }
			Blend One One
			CGPROGRAM
			// Apparently need to add this declaration
			#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 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				SHADOW_COORDS(2)
			};
			
			v2f vert(a2v v) {
			 	v2f o;
			 	o.pos = UnityObjectToClipPos(v.vertex);
			 	o.worldNormal = UnityObjectToWorldNormal(v.normal);
			 	o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			 	// Pass shadow coordinates to pixel shader
			 	TRANSFER_SHADOW(o);
			 	
			 	return o;
			}
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
			 	fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
			 	fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
			 	fixed3 halfDir = normalize(worldLightDir + viewDir);
			 	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
                 //不需要光源类型来处理衰减。内置的宏来处理计算
				// UNITY_LIGHT_ATTENUATION 内置的宏,即可计算衰减,也可计算阴影
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
			 	
				return fixed4((diffuse + specular) * atten, 1.0);
			}
			ENDCG
		}
	}
	FallBack "Specular"
}

透明测试的阴影

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

Shader "Unlit/13" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_Cutoff ("Alpha Cutoff", Range(0, 1)) = 0.5
	}
	SubShader {
		Tags {"Queue"="AlphaTest" "IgnoreProjector"="True" "RenderType"="TransparentCutout"}
		
		Pass {
			Tags { "LightMode"="ForwardBase" }
			
			Cull Off
			
			CGPROGRAM
			
			#pragma multi_compile_fwdbase
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _Cutoff;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
                //占用了三个插值寄存器
				SHADOW_COORDS(3)
			};
			
			v2f vert(a2v v) {
			 	v2f o;
			 	o.pos = UnityObjectToClipPos(v.vertex);
			 	
			 	o.worldNormal = UnityObjectToWorldNormal(v.normal);
			 	
			 	o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

			 	o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
			 	
			 	// Pass shadow coordinates to pixel shader
			 	TRANSFER_SHADOW(o);
			 	
			 	return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				
				fixed4 texColor = tex2D(_MainTex, i.uv);

				clip (texColor.a - _Cutoff);
				
				fixed3 albedo = texColor.rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));
							 	
			 	// UNITY_LIGHT_ATTENUATION not only compute attenuation, but also shadow infos
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
			 	
				return fixed4(ambient + diffuse * atten, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Transparent/Cutout/VertexLit"
}

透明混合的阴影

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

Shader "Unlit/14" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_AlphaScale ("Alpha Scale", Range(0, 1)) = 1
	}
	SubShader {
		Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
		
		Pass {
			Tags { "LightMode"="ForwardBase" }
			
			ZWrite Off
			Blend SrcAlpha OneMinusSrcAlpha
			
			CGPROGRAM
			
			#pragma multi_compile_fwdbase
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _AlphaScale;
			
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
				float4 texcoord : TEXCOORD0;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				float2 uv : TEXCOORD2;
				SHADOW_COORDS(3)
			};
			
			v2f vert(a2v v) {
			 	v2f o;
			 	o.pos = UnityObjectToClipPos(v.vertex);
			 	o.worldNormal = UnityObjectToWorldNormal(v.normal);
			 	o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
			 	o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
			 	// Pass shadow coordinates to pixel shader
			 	TRANSFER_SHADOW(o);
			 	
			 	return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
				fixed4 texColor = tex2D(_MainTex, i.uv);
				fixed3 albedo = texColor.rgb * _Color.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));

			 	// UNITY_LIGHT_ATTENUATION not only compute attenuation, but also shadow infos
				UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
			 	
				return fixed4(ambient + diffuse * atten, texColor.a * _AlphaScale);
			}
			
			ENDCG
		}
	} 
	//Transparent/VertexLit 并没有包含阴影投射的pass 所以无阴影效果
	//FallBack "Transparent/VertexLit"
	// 强制为半透明物体生成阴影
	FallBack "VertexLit"
}

这种效果是错误的。半透明物体上接收到的阴影没有穿透物体映射到地方,同时自身投射的阴影是应该或受到光的影响,而不是直接的投影。

 

可用的标准shader

diffuse

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

Shader "Unlit/15" {
	Properties {
		_Color ("Color Tint", Color) = (1, 1, 1, 1)
		_MainTex ("Main Tex", 2D) = "white" {}
		_BumpMap ("Normal Map", 2D) = "bump" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" "Queue"="Geometry"}

		Pass { 
			Tags { "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			#pragma multi_compile_fwdbase
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			
			fixed4 _Color;
			sampler2D _MainTex;
			float4 _MainTex_ST;
			sampler2D _BumpMap;
			float4 _BumpMap_ST;
			
			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 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
			
			 	fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(bump, lightDir));
				
				UNITY_LIGHT_ATTENUATION(atten, i, worldPos);
				
				return fixed4(ambient + diffuse * 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;
			
			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));
				
				UNITY_LIGHT_ATTENUATION(atten, i, worldPos);
				
				return fixed4(diffuse * atten, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Diffuse"
}

specular

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

Shader "Unlit/16" {
	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
	}
	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;
			
			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;

				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 albedo = tex2D(_MainTex, i.uv.xy).rgb * _Color.rgb;
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				
			 	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(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"
}
发布了24 篇原创文章 · 获赞 1 · 访问量 1245

猜你喜欢

转载自blog.csdn.net/birdswillbecomdragon/article/details/105284756