【Unity Shader入门精要学习笔记】Unity中的基础光照

光照模型

着色
着色指的是,根据材质的属性,比如漫反射属性,还有光源的信息,用一个等式去计算从某个方向去观察物体,该方向上光的出射度的过程。这个过程称为光照模型
标准光照模型
标准光照模型只关心光照,就是那些直接从光源发射出来照射到物体表面后,经过物体表面的一次反射直接进入摄像机的光线。
标准光照模型把进入到摄像机的光线分位4个部分:

  • 自发光:使用Cemissvie来表示。用来描述当给定一个方向时,一个表面本身会向该方向发射多少的辐射量。
  • 高光反射:使用Cspecular来表示。用于描述当光线照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量。
  • 漫反射:使用Cdiffuse来表示。用于描述光线照射到模型表面时,该表面会向每个方向散射多少辐射度。
  • 环境光:使用Cambient来表示。用来表示其他所有的间接光照。在标准光照模型中,场景中所有物体都使用一个通用的光照变量来表示环境光:Cambient = gambient.

自发光使用了材质的自发光颜色:Cemissvie = memissvie.

漫反射符合兰伯特定律(Lambert’s law):反射光线的强度与表面法线和光源方向之间夹角的余弦值成正比。
Cdiffuse = (Clight · mdiffuse)max(0, n·l).,Clight是光源颜色,mdiffuse是材质漫反射颜色.

在标准光照模型中,场景中所有物体都使用一个通用的光照变量来表示环境光:Cambient = gambient.

高光反射需要的信息比较多:
在这里插入图片描啊
表面法线,视角方向,光源方向,反射方向等。反射方向可以通过其他信息计算得到:
r = 2(n·l)n - l
Phong模型计算高光反射公式为:
Cspecular = (Clight · mspecular)max(0, v·r)mgloss
公式里,mgloss是光泽度或者反光度,用来控制高光区域的亮点有多大,mgloss越大,亮点就越小。
mspeuclar是材质的高光反射颜色。
Blinn-Phong模型引入一个新的矢量h
h = (v + l) / |v + l|
在这里插入图片描述
Blinn模型计算公式为:
Cspecular = (Clight · mspecular)max(0, n·h)mgloss

UnityShader中实现漫反射光照模型

漫反射计算公式为:
Cdiffuse = (Clight · mdiffuse)max(0, n·l).,Clight是光源颜色,mdiffuse是材质漫反射颜色.

逐顶点光照

Shader "Unity Shaders Book/Chapter 6/Diffuse Vertex-Level" {

	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
	}

	SubShader {
		Pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM
			
			#include "Lighting.cginc"

			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Diffuse;
			
			// application to vertex
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			// vertex to fragment
			struct v2f {
				float4 pos : SV_POSITION;
				fixed3 color : COLOR;
			};

			v2f vert(a2v v){
				v2f o;
				// 把顶点位置从模型空间转换到裁剪空间
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				// 得到环境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

				// normalize()进行归一化
				// 法线方向, 从模型空间转换到世界空间,使用模型到世界变换矩阵的逆矩阵_World2Object
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)_World2Object));
				
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);

				// 漫反射公式 saturate相当于max();
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb + saturate(dot(worldNormal, worldLight));
				o.color = ambient + diffuse;
				return o;
			}

			fixed4 frag(v2f i) : SV_Target {
				return fixed4(i.color, 1.0);
			}

			ENDCG
		}
	}
	Fallback "Diffuse"
}

LighMode是Pass标签的一种,用来定义该Pass在Unity中光照流水线的角色。我们要使用_LightColor0,需要引入 “Lighting.cginc”。
上述代码中,我们使用了UNITY_LIGHTMODEL_AMBIENT来获取环境光部分。
在计算法线和光源方向之间的点积时,需要将二者所处的坐标系统一后计算才有意义

逐像素光照

Shader "Unity Shaders Book/Chapter 6/Diffuse Pixel-Level" {

	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
	}

	SubShader {
		Pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM
			
			#include "Lighting.cginc"

			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Diffuse;
			
			// application to vertex
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			// vertex to fragment
			struct v2f {
				float4 pos : SV_POSITION;
				fixed3 worldNormal : TEXCOORD0;
			};

			v2f vert(a2v v){
				v2f o;
				// 把顶点位置从模型空间转换到裁剪空间
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
				return o;
			}

			// 片元着色器计算光照模型
			fixed4 frag(v2f i) : SV_Target {
				// 得到环境光
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

				fixed3 worldNormal = normalize(i.worldNormal);				
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb + saturate(dot(worldNormal, worldLight));
				fixed3 color = ambient + diffuse;

				return fixed4(color, 1.0);
			}

			ENDCG
		}
	}
	Fallback "Diffuse"
}

半兰伯特光照模型
计算公式为:
Cdiffuse = (Clight · mdiffuse)(α(n·l) + β).
半兰伯特模型没有使用max操作来防止点积值为负值,而是对结果进行了一个α倍的缩放和β大小的偏移,多数情况下,二者均为0.5:
Cdiffuse = (Clight · mdiffuse)(0.5(n·l) + 0.5).

UnityShader中实现高光反射模型

Phong模型计算高光反射部分的公式为:
Cspecular = (Clight · mspecular)max(0, v·r)mgloss

逐顶点光照

Shader "Unity Shaders Book/Chapter 6/Specular Vertex-Level" {
	
	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}

	SubShader {
		Pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM
			
			#include "Lighting.cginc"

			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			// application to vertex
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			// vertex to fragment
			struct v2f {
				float4 pos : SV_POSITION;
				fixed3 color : COLOR;
			};

			v2f vert(a2v v){
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				// 计算出反射光线方向并归一化
				fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));

				// 计算出世界坐标系中物体的视角方向,然后归一化
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
				// 高光反射公式计算
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
				o.color = ambient + diffuse + specular;
				return o;
			}

			// 片元着色器计算光照模型
			fixed4 frag(v2f i) : SV_Target {
				return fixed4(i.color, 1.0);
			}

			ENDCG
		}
	}
	Fallback "Specular"
}

Properties里,_Specular用来控制材质的高光反射颜色,_Gloss来控制高光区域的大小。

逐像素光照

Shader "Unity Shaders Book/Chapter 6/Specular Pixel-Level" {
	
	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}

	SubShader {
		Pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM
			
			#include "Lighting.cginc"

			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			// application to vertex
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			// vertex to fragment
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};

			v2f vert(a2v v){
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

				//o.color = ambient + diffuse + specular;
				return o;
			}

			// 片元着色器计算光照模型
			fixed4 frag(v2f i) : SV_Target {
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 worldNormal = normalize(i.worldNormal);
				//fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				// 使用UnigtCG公式
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.pos));

				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				// 计算出反射光线方向并归一化
				fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
				// 计算出世界坐标系中物体的视角方向,然后归一化
				//fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
				//使用UnigtCG公式
				fixed3 viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				// 高光反射公式计算
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
				fixed3 color = ambient + diffuse + specular;
				return fixed4(color, 1.0);
			}

			ENDCG
		}
	}
	Fallback "Specular"
}

Blinn-Phong光照模型
Blinn计算高光反射的公式为:
Cspecular = (Clight · mspecular)max(0, n·h)mgloss

而其中,h = (v + l) / |v + l|

Shader "Unity Shaders Book/Chapter 6/Specular BlinnPhong" {
	
	Properties {
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}

	SubShader {
		Pass {
			Tags { "LightMode" = "ForwardBase" }

			CGPROGRAM
			
			#include "Lighting.cginc"

			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			// application to vertex
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			// vertex to fragment
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};

			v2f vert(a2v v){
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;

				//o.color = ambient + diffuse + specular;
				return o;
			}

			// 片元着色器计算光照模型
			fixed4 frag(v2f i) : SV_Target {
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);

				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
				// 计算出反射光线方向并归一化
				fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
				// 计算出世界坐标系中物体的视角方向,然后归一化
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);

				fixed3 halfDir = normalize(worldLightDir + viewDir);
				// Blinn-Phong模型公式
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				fixed3 color = ambient + diffuse + specular;
				return fixed4(color, 1.0);
			}

			ENDCG
		}
	}
	Fallback "Specular"
}

Unity内置的函数

UnityCG.cginc中常用的帮助函数:
在这里插入图片描述
例如:UnityWorldSpaceViewDir的实现如下:

	inline float3 UnityWorldSpaceViewDir(in float3 worldPos)
	{
		return _WorldSpaceCameraPos.xyz - worldPos;
	}

就是从世界坐标系中某个点worldPos到世界坐标系中摄像机的向量
我们在使用这些函数前,需要把方向矢量归一化,我们使用normalize来进行此操作。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值