《Unity Shader入门精要》笔记:中级篇(1)

  3.2 游戏引擎技术
  • 本篇博客主要为个人学习所编写读书笔记,不用于任何商业用途,以及不允许任何人以任何形式进行转载。
  • 本篇博客会补充一些扩展内容(例如其他博客链接)。
  • 本篇博客还会提供一些边读边做的效果截图。文章内所有数学公式都由Latex在线编辑器生成。
  • 本篇博客主要提供一个“glance”,知识点的总结。如有需要请到书店购买正版。
  • 博客提及所有官方文档基于2022.2版本,博客会更新一些书中的旧的知识点到2022.2版本。
  • 如有不对之处欢迎指正。
  • 我创建了一个游戏制作交流群:637959304 进群密码:(CSGO的拆包密码)欢迎各位大佬一起学习交流,不限于任何平台(U3D、UE、COCO2dx、GamesMaker等),以及欢迎编程,美术,音乐等游戏相关的任何人员一起进群学习交流。

更复杂的光照

  • Unity的渲染路径(Rendering Path):前向渲染路径,延迟渲染路径、顶点光照。可以在每个摄像机中的渲染路径设置中选择不同的渲染方式。
image 181 1024x461 - 《Unity Shader入门精要》笔记:中级篇(1)
  • Tags{“LightMode” = “ForwardBase”,之前所写的代码中,该条语句的意思为告诉Unity,该Pass使用前向渲染路径中的ForwardBase路径。如果没有指定路径,那么一些光照可能不会被正确赋值。
image 182 1024x437 - 《Unity Shader入门精要》笔记:中级篇(1)
  • 前向渲染有三种处理光照的方式:逐顶点处理,逐像素处理,球谐函数(Spherical Harmonics,SH)处理。决定一个光源使用哪种处理模式取决于它的类型和渲染模式。
  • 光源的类型(Type)分为平行光和其他类型的光源。
    渲染模式(Render Mode)是指该光源是否是重要的。如果设置为Important,则Unity会当成逐像素光源进行处理
  • 渲染一个物体,Unity会根据场景中各个光源的设置以及光源对物体的影响程度对光源进行重要度排序。一定数目的光源会按照逐像素处理,最多有4个光源按逐顶点的方式处理,剩下的光源可以按SH方式处理。
    判断规则:
    1、场景中最亮的平行光总是按逐像素处理。
    2、渲染模式被设置为不重要的光源,会按照逐顶点或SH处理。
    3、渲染模式被设置成重要的光源,会按照逐像素处理。
    4、如果根据以上的规则得到的逐像素光源数下雨Quality Setting中的设置数量,则会有更多光源以逐像素方式进行渲染。
image 183 - 《Unity Shader入门精要》笔记:中级篇(1)
  • 内置的光照变量和函数
  • 内置着色器helper函数,传送门
  • 内置着色器变量:传送门
  • 前向渲染可以使用的内置光照变量(下图仅供参考,具体请参阅上两行的传送门手册)
image 184 - 《Unity Shader入门精要》笔记:中级篇(1)
  • 前向渲染可以使用的内置光照函数(同样仅供参考)
image 185 - 《Unity Shader入门精要》笔记:中级篇(1)
  • 顶点光照渲染:前向渲染的一个子集,使用逐顶点的方式来计算光照。对硬件配置要求最少,运算性能最高,但同时效果相对最差。
  • 延迟渲染:如果场景中使用了大量实时光照,那么推荐使用延迟渲染。
    缺点:1、不支持真正的抗锯齿(anti-aliasing)功能 2、不能处理半透明物体 3、对显卡有要求,显卡必须支持MRT、Shader Mode3.0及以上、深度渲染纹理以及双面的模板缓冲。(英伟达yes!)
  • 使用延迟渲染:需要提供两个Pass
    1、第一个Pass用于渲染G缓冲。在这个Pass中,我们会把物体的漫反射颜色、高光反射颜色、平滑度、法线、自发光和深度等信息渲染到屏幕空间的G缓冲中。对于每个物体来说,这个Pass仅会执行一次。
    2、第二个Pass用于计算真正的光照模型。这个Pass会使用上一个Pass中渲染的数据来计算最终的光照颜色,再存储到帧缓冲中。

Unity的光源类型

  • Unity一共支持四种光源类型:平行光、点光源、聚光灯和面光源(area light)。面光源仅在烘焙时才可发挥作用。
  • 平行光:通常作为阳光使用。几何属性只有方向没有位置。
  • 点光源:一个点发出的光源,照亮空间有限。需要再Scene视图中开启光照才能看到预览效果。点光源会随着物体逐渐远离而衰减。
image 187 - 《Unity Shader入门精要》笔记:中级篇(1)
  • 聚光灯:由空间一块锥形区域定义。可以用于表示由一个特定位置出发、向特定方向延展的光。
image 188 - 《Unity Shader入门精要》笔记:中级篇(1)
  • 如何实现阴影:最常使用的方法为Shadow Map技术,该技术会把摄像机的位置放在与光源重合的位置上,那么场景中该光源的阴影区域就是那些摄像机看不到的地方。利用含有ShadowCaster标签的Pass来处理位置关系。
    物体接收来自其他物体的阴影:对阴影映射纹理进行采样,把采样和光照结果相乘产生阴影效果。
    物体投射向其他物体的阴影:把该物体加入到光源的阴影映射纹理计算中,让其他物体在对阴影纹理映射采样时可以得到该物体的相关信息。
  • 处理不同的光源类型
    1、在UnityShader中访问光源的5个属性:位置,方向,颜色,强度以及衰减。
    2、Bass Pass和Addtional Pass的使用
// Blinn-Phong模型
//编写两个Pass分别来接收和投射阴影
Shader "Example/Blinn_Phong" {
	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
			#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"
}
image 190 - 《Unity Shader入门精要》笔记:中级篇(1)
image 191 - 《Unity Shader入门精要》笔记:中级篇(1)

LEAVE A COMMENT