Unity中的基础光照
着色(shading) : 根据材质属性(如漫反射属性等)、光源信息(如光源方向、辐照度等),使用一个等式去计算沿某个观察方向的出射度的过程。把这个等式称为光照模型(Lighting Model)
标准光照模型
标准光照模型只关心直接光照,也就是那些直接从光源发射出来照射到物体表面后,经过物体表面的一次反射直接进入摄像机的光线。
标准光照模型仅仅是一个经验模型,它并不完全符合真实世界中的光照现象。
基本方法是,把进入摄像机的光线分为4个部分,每个部分使用一种方法来计算贡献度。
- 自发光(emissive) 用于描述给定一个方向时,一个表面本身会向该方向发射多少辐射量。注意:如果没有使用全局光照技术,这些自发光的表面并不会真的照亮周围的物体,而是它本身看起来更亮了而已。
- 高光反射(specular) 用于描述当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量。
- 漫反射(diffuse) 用于描述当光线从光源照射到模型表面时,该表面会向每个方向散射多少辐射量。
- 环境光(ambient) 用于描述其他所有间接光照。
环境光
在标准光照模型中,使用环境光近似模拟间接光照。环境光计算简单,通常是一个全局变量,即场景中所有物体都使用这个环境光。
自发光
光线也可以直接由光源发射进入摄像机,而不需要任何物体的反射。标准光照模型使用自发光来计算这个部分的贡献度。计算简单,就是直接使用了该材质的自发光颜色。
通常在实时渲染中,自发光表面往往并不会照亮周围的表面,也就是说这个物体并不会被当做一个光源。
漫反射
漫反射光照是用于那些被物体表面随机散射到各个方向的辐射度进行建模的。在漫反射中,视角的位置不重要,因为反射是完全随机的,因此可以认为在任何反射方向的分布都是一样的。
高光反射
这里的高光反射是一种经验模型,也就是说,它并不完全符合真实世界中的高光反射现象。可用于计算那些沿着完全镜面反射方向被反射的光线,让物体看起来是有光泽的。
Phone模型
Blinn 模型
漫反射光照模型计算
逐像素光照
在片元着色器中计算。
在逐像素光照中,会以每个像素为基础,得到它的法线(可以是对顶点法线插值得到,也可以是从法线纹理中采样得到),然后进行光照模型计算。这种面片之间对顶点法线进行插值的技术被称为 Phone着色。
Shader "Unity Shaders Book/Chapter6/DiffusePixel-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;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
};
v2f vert(a2v v) {
v2f o;
// Transform the vertex from object space to projection space
// 把顶点坐标从模型空间转换到裁剪空间中
o.pos = UnityObjectToClipPos(v.vertex);
// Transform the normal from object space to world space
// 把法线从模型空间转换到世界空间中
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target {
// Get ambient term 环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Get the normal in world space
fixed3 worldNormal = normalize(i.worldNormal);
// Get the light direction in world space
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 color = ambient + diffuse;
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
逐顶点光照
高洛德着色。
在顶点着色器中计算。
在逐顶点光照中,在每个顶点上计算光照,然后会在渲染图元内部进行线性插值,最后输出成像素颜色。
由于顶点数量往往远小于像素数目,因此逐顶点光照的计算量往往要小于逐像素光照。
但由于逐顶点光照依赖于线性插值来得到像素光照,因此当光照模型中有非线性计算(如计算高光反射)时,逐顶点光照会出问题
由于逐顶点光照会在渲染图元内部对顶点颜色进行插值,会导致渲染图元内部的颜色总是暗于顶点处的最高颜色值,在某些情况下会产生明显的棱角现象
Shader "Unity Shaders Book/Chapter6/DiffuseVertex-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;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR;
};
v2f vert(a2v v) {
v2f o;
// Transform the vertex from object space to projection space
// 把顶点坐标从模型空间转换到裁剪空间中
o.pos = UnityObjectToClipPos(v.vertex);
// Get ambient term 获取环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// 计算发现和光源之间的点积时,需要选择它们的坐标系,只有两者处于同一坐标空间下,点积才有意义
// Transform the normal from object space to world space
// 把法线从模型空间转换到世界空间中 由于法线是一个三维矢量,只需要截取前三行和前三列
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
// Get the light direction in world space
// 获取光源方向
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz);
// 计算漫反射需要四个参数 光源颜色 材质的漫反射颜色 表面法线 光源的单位矢量
// Compute diffuse term
// saturate 把参数截取到[0,1]范围内
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"
}
半兰伯特光照模型
Shader "Unity Shaders Book/Chapter6/HalfLambert" {
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;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
};
v2f vert(a2v v) {
v2f o;
// Transform the vertex from object space to projection space
o.pos = UnityObjectToClipPos(v.vertex);
// Transform the normal from object space to world space
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target {
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Get the normal in world space
fixed3 worldNormal = normalize(i.worldNormal);
// Get the light direction in world space
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;
fixed3 color = ambient + diffuse;
return fixed4(color, 1.0);
}
ENDCG
}
}
FallBack "Diffuse"
}
高光反射模型计算
逐顶点光照
Shader "Unity Shaders Book/Chapter6/SpecularVertex-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;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
fixed3 color : COLOR;
};
v2f vert(a2v v) {
v2f o;
// Transform the vertex from object space to projection space
o.pos = UnityObjectToClipPos(v.vertex);
// Get ambient term 获取环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
// Transform the normal from object space to world space
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
// Get the light direction in world space
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term 获取漫反射光
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
// Get the reflect direction in world space
// 计算入射方向关于关于表面法线放线的reflectDir
// 由于Cg的reflect函数的入射方向要求是 由光源指向交点出的, 因此需要对worldLightDir取反后再传给reflect函数
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
// Get the view direction in world space
// 通过_WorldSpaceCameraPos得到了世界空间中的摄像机位置,再把顶点坐标从模型空间变换到世界空间下,
// 通过和_WorldSpaceCameraPos相减 即可得到世界空间下的视角方向
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
// Compute specular term
// 高光反射计算需要四个参数:入射光线的颜色和强度、反射方向、视角方向、材质的高光反射系数
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"
}
逐像素光照
Shader "Unity Shaders Book/Chapter6/SpecularPixel-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;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v) {
v2f o;
// Transform the vertex from object space to projection space
o.pos = UnityObjectToClipPos(v.vertex);
// Transform the normal from object space to world space
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
// Transform the vertex from object spacet to world space
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
// Get the reflect direction in world space
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));
// Get the view direction in world space
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
// Compute specular term
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}
Blinn-Phone 光照模型
Shader "Unity Shaders Book/Chapter6/Blinn-Phong" {
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;
float3 normal : NORMAL;
};
struct v2f {
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
v2f vert(a2v v) {
v2f o;
// Transform the vertex from object space to projection space
o.pos = UnityObjectToClipPos(v.vertex);
// Transform the normal from object space to world space
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
// Transform the vertex from object spacet to world space
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
return o;
}
fixed4 frag(v2f i) : SV_Target {
// Get ambient term
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
// Compute diffuse term
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
// Get the view direction in world space
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
// Get the half direction in world space
fixed3 halfDir = normalize(worldLightDir + viewDir);
// Compute specular term
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
FallBack "Specular"
}