其他分享
首页 > 其他分享> > 【Shader笔记】Unity基础光照

【Shader笔记】Unity基础光照

作者:互联网

参考书籍:Unity Shader入门精要

一、认识光照

1.1 光源

实时渲染中,通常光源为一个没有体积的点,用 l {l} l表示其光照方向。

1.1.1 如何量化光

答:使用辐照度

1.2 吸收和散射

光线由光源发射,与一些物体相交产生的结果:散射(scattering) 和 吸收(absorption)。

改变方向改变颜色改变密度
散射
吸收

1.2.1 散射

光线经物体表面散射后,有两种方向:

1.2.2 在Unity中区分散射方向

高光反射(specular):表示物体表面是如何反射光线的。
漫反射(diffuse):表示多少光线会被折射、吸收和三射出表面。

根据入射光线数量和方向,计算出射光线的数量和方向,使用出射度(exitnce)来描述。

1.3 着色

根据材质属性(如漫反射属性等)、光源信息(如光源方向、辐照度等),使用一个等式去计算沿某个观察方向的出射度的过程。这个等式也成为光照模型(Lighting Model)。

1.4 BRDF光照模型

BDRF(Bidirectional Reflectance Distribution Function):回答物体表面与光线是如何交互。

二、标准光照模型

只关心直接光照(direct light),即经过物体表面一次反射直接进入摄像机的光线。它将进入摄像机的光线分为 自发光、高光反射、漫反射、环境光 共四部分。

2.1 自发光(emissive)

解释:给定一方向后,表面本身向该方向发射多少辐射量。

注意:未使用全局光照(global illumination)情况下,自发光的表面不会照亮周围物体,仅本身显得更亮

2.2 高光反射 (specular)

解释:当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量

2.3 漫反射(diffuse)

解释:当光线从光源照射到模型表面,该表面会向每个方向散射多少辐射量

符合兰伯特定律:反射光线的强度与表面发现和光源方向之间的夹角的余弦值成正比。
c d i f f u s e = ( c l i g h t ∗ m d i f f u s e ) m a x ( 0 , n ∗ I ) {c_{diffuse} = (c_{light} *m_{diffuse})max(0, n*I)} cdiffuse​=(clight​∗mdiffuse​)max(0,n∗I)

注意:防止和避免法线和光源方向点乘的结果为负值。故使用 m a x ( 0 , n ∗ I ) {max(0,n*I)} max(0,n∗I)

2.4 环境光(ambient)

描述其他所有的间接光照。

间接光照:在多个物体之间反射,最后进入到摄像机。

2.5 逐像素与逐顶点

在片元着色器中计算:逐像素光照(per-pixel lighting)
在顶点着色器中计算:逐顶点光照(per-vertex lighting)

三、Unity Shader实现漫反射光照模型

漫反射计算公式 c d i f f u s e = ( c l i g h t ∗ m d i f f u s e ) m a x ( 0 , n ∗ I ) {c_{diffuse}=(c_{light}*m_{diffuse})max(0, n*I)} cdiffuse​=(clight​∗mdiffuse​)max(0,n∗I)

函数:saturate( x x x)
参数 x x x:用于操作的标量或矢量,可以是float、float2、float3等类型
描述:把 x {x} x截取在[0, 1]范围内,如果 x {x} x是一个矢量,那么会对它的每一个分量进行这样的操作。

3.1 逐顶点光照 实践

Shader "Unity Shaders Book/Chapter 6/Diffuse Vertex-Level" {
	Properties {
		_Diffuse ("Diffuse", Color) = (1,1,1,1)
	}
	
	SubShader {
		Pass {
			Tags { "LightMode"="ForwardBase" }
	
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse

			struct a2v {
				float4 vertex : POSITION;
				float4 normal : NORMAL;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				fixed3 color : COLOR;
			};

			v2f vert(a2v v) {
				v2f o;
				//从模型空间转换到裁剪空间
				o.pos = UnityObjectToClipPos(v.vertex);
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
				fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
				
				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"
}

3.1.1 属性 准备

Properties{}语义块中声明Color属性,用于控制漫反射颜色。

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

3.1.2 标签 LightMode

顶点着色器需写在Pass{}语义块,指明该Pass{}的光照模式

Tags { "LightMode"="ForwardBase" }

3.1.3 定义 顶点/片元着色器

分别定义 [vertex顶点着色器]/[fragment片元着色器] 为 [vert]/[frag]

#pragma vertex vert
#pragma fragment frag

3.1.4 Unity内置文件 Lighting.cginc

Unity提供的内置变量的文件,这里需要Lighting.cginc中的一些变量,故需要包含这文件

#include "Lighting.cginc"

3.1.5 存储 变量

fixed4 _Diffuse:定义一变量与Properties{}中的属性相匹配,由于颜色属性在[0, 1]范围内,故选择fixed4精度存储。

3.1.6 定义 结构体

定义顶点着色器输入结构体:

struct a2v {
	float4 vertex : POSITION;
	float4 normal : NORMAL;
};

定义顶点着色器输出结构体(等同于片元着色器输入结构体):

struct v2f {
	float4 pos : SV_POSITION;
	fixed3 color : COLOR;
};

3.1.7 实现 逐顶点漫反射

为实现漫反射,参照 2.3漫反射,需要以下四个参数:

代码内容如下

v2f vert(a2v v) {
	v2f o;
	o.pos = UnityObjectToClipPos(v.vertex);
		
	fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
	fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
	fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);				
	fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLight));
				
	o.color = ambient + diffuse;
	return o;
}

在Shader码中:

3.1.8 输出 颜色

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

3.1.9 回调(防该SubShader无法运行)

FallBack "Disffuse"

3.2 逐像素光照 实践

Shader "Unity Shaders Book/Chapter 6/Diffuse 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
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;

			struct a2v {
				float4 vertex : POSITION;
				float4 normal : NORMAL;
			};
			
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};

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

				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 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

				return fixed4(ambient + diffuse + specular, 1.0);
			}
			ENDCG
		}
	}
	FallBack "Diffuse"
}

3.2.1 属性 准备

Properties {
	_Diffuse ("Diffuse", Color) = (1,1,1,1)
	_Specular ("Specular", Color) = (1, 1, 1, 1)
	_Gloss ("Gloss", Range(8.0, 256)) = 20
}

3.2.2 标签 LightMode

参考3.1.2描述

3.2.3 定义 顶点/片元着色器

参考3.1.3描述

3.2.4 Unity内置文件 Lighting.cginc

参考3.1.4描述

3.2.5 存储 变量

fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;

_Diffuse_Specular[同为Color]:选择 fixed4 数据类型存储
_Gloss:选择 float 数据类型存储

3.2.6 定义 结构体

顶点着色器输入

struct a2v {
	float4 vertex : POSITION;
	float4 normal : NORMAL;
};

获取模型空间下的 顶点坐标 与 法线坐标。

片元着色器输入

struct v2f {
	float4 pos : SV_POSITION;
	float3 worldNormal : TEXCOORD0;
	float3 worldPos : TEXCOORD1;
};

获取从顶点着色器转片元着色器的数据,如模型在模型空间下的坐标、法线坐标和纹理坐标。

3.2.7 实现 逐像素光照

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

	return o;
}

3.2.8 输出 颜色

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 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);

	return fixed4(ambient + diffuse + specular, 1.0);
}

3.2.9 回调

参考 3.1.9

3.3 Blinn-Phong 光照模型

Blinn模型没有使用反射方向,而是引入一个新的矢量 h h h。
计算高光公式如下:
c s p e c u l a r = ( c l i g h t ∗ m s p e c u l a r m a x ( 0 , n ∗ h ) ) {c_{specular}=(c_{light}*m_{specular}max(0, n*h))} cspecular​=(clight​∗mspecular​max(0,n∗h))

fixed4 frag(v2f i) : SV_Target {
	...
	fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.world.xyz);
	fixed3 halfDir = normalize(worldLightDir + viewDir);
	fixed3 specular = _LightColor0.xyz * _Specular.xyz + pow(max(0, dot(worldNormal, halfDir)), _Gloss);
	
	return fixed4(ambient + diffuse + specular, 1.0);
} 

在这里插入图片描述

四、Unity内置函数

UnityCG.cginc

函数名描述
float3 WorldSpaceViewDir(float4 v)输入一个模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向
float3 UnityWorldSpaceViewDir(float4 v)输入一个世界空间中的顶点坐标,返回世界空间中从该点到摄像机的观察方向
float3 ObjSpaceViewDir(float4 v)输入一个模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向
float3 WorldSpaceLightDir(float4 v)仅可用于前向渲染中。输入一个模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向
float3 UnityWorldSpaceLightDir(float4 v)仅可用于前向渲染中,输入一个世界空间中的顶点位置。返回世界空间中从该点到光源的光照方向
float3 ObjSpaceLightDir(float4 v)仅可用于前向渲染中,输入一个模型空间中的顶点位置。返回模型空间中从该点到光源的光照方向
float3 UnityObjectToWorldNormal(float3 norm)把法线方向从模型空间转换到世界空间中
float3 UnityObjectToWorldDir(in float3 dir)把方向矢量从模型空间变换到世界空间中
float3 UnityWorldToObjectDir(float3 dir)把方向矢量从世界空间变换到模型空间中

标签:fixed4,Shader,fixed3,Unity,worldNormal,float4,Diffuse,光照
来源: https://blog.csdn.net/qq_51026638/article/details/116425691