1、
1.1 光源
光源方向 - l,法线n,辐射度与照射到物体表面时光线之间的距离成反比,因此与成正比。
可用l和n的点积得到
1.2 吸收和散射
散射改变光线方向,不改变密度和颜色;吸收只改变光线的密度和颜色,但不改变方向。散射到内部:折射、透射;散射到外部:反射
高光反射 - 表示物体表面如何反射光线,漫反射 - 表示有多少光线会被折射、吸收和散射出表面。
据入射光线的数量和方向,得出出射度,与辐照度有线性关系,比值即为材质的漫反射和高光反射属性
1.3 着色
据材质属性和光源信息,使用等式计算沿某个观察方向出射度的过程 - 光照模型
1.4 BRDF光照模型
光线与物体表面相交时会有多少光线被反射?反射方向有哪些?
给定入射光线和辐照度后BRDF可以给出在某个初涉方向上的光照能量分布。
2、标准光照模型
-自发光 ,给定一个方向时物体本身向该方向辐射多少。若未使用全局光照技术,自发光表面并不会真的照亮周围物体,而是本身看起来更亮
-高光反射 ,光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量
-漫反射 ,光线从光源照射到模型表面,该表面会向每个方向散射多少辐射量
-环境光 ,描述其他所有间接光照
2.1 环境光
光照模型的重点在于描述直接光照,但在真实世界中也可以被间接光照照亮。
环境光:
2.2 自发光
使用模型的自发光颜色:
2.3 漫反射
对物体表面随即散射到各个方向的辐射度进行建模。视角位置不重要,但入射光线角度很重要。
漫反射光照符合兰伯特定律:
n-表面法线 I-光源单位矢量 - 材质漫反射颜色 - 光源颜色,截取到0是怕后面来的光源照亮
2.4 高光反射
需要知道表面法线、视角方向、光源方向、反射方向等
直到前三个,则反射方向:
然后利用Phong模型计算高光反射部分:
其中是光泽度(反光度),控制高光区域亮点多宽,越大亮点越小。是材质的高光反射颜色,是光源的颜色和强度。
Blinn思想是避免计算而引入新矢量,然后
2.5 逐像素还是逐顶点
片元着色器:逐像素光照,顶点着色器:逐顶点光照
逐像素光照是对于每个像素得到其法线,再对面片之间顶点法线进行差值叫Phong着色
逐顶点光照,高洛德着色,再渲染图源内部进行线性插值,逐顶点光照依赖于线性插值,因此当模型中有非线性计算时(如高光反射)就会有问题。而在渲染图元内的对顶点颜色插值会导致渲染图元内部颜色总暗于顶点处最高颜色值,因此会产生棱角现象
Blinn-Phong光照模型不能表示菲涅尔反射,是各向同性则像毛发、金属等各向异性无法表示出来
3、Unity中的环境光和自发光
Shader中通过内置变量UNITY_LIGHTMODEL_AMBIENT得到环境光和颜色强度信息。
4、Unity Shader中实现漫反射光照模型
函数:saturate(x),x为用于操作的标量或矢量,可以是float/float2/float3,将x截取到[0,1]中
4.1 实践:逐顶点光照
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter 6/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;
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);
//LightColor0访问该Pass处理的光源颜色和强度信息
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.0f);
}
ENDCG
}
}
FallBack "Diffuse"
}
结果:
4.2 实践:逐像素光照
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;
fixed3 worldNormal : TEXCOORD0;//将顶点着色器中计算得到的光照颜色传给片元着色器
};
v2f vert(a2v v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);//顶点着色器的基本任务
o.worldNormal = normalize(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 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 color = ambient + diffuse;
return fixed4(color, 1.0f);
}
ENDCG
}
}
FallBack "Diffuse"
}
结果:
4.3 半兰伯特模型
绝大多数系数为0.5,即
按照公式改好
fixed halfLambert = dot(worldNormal, worldLightDir) * 0.5 + 0.5;
fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * halfLambert;
结果:
5、Unity Shader中实现高光反射光照模型
函数:reflect(i, n)
5.1 实践:逐顶点光照
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter6/SpecularVertex - Level" {
Properties {
_Diffuse("Diffuse", Color) = (1,1,1,1)
_Gloss("Gloss", Range(8.0, 256)) = 20 //高光反射颜色
_Specular("Specular", Color) = (1,1,1,1)//控制高光区域大小
}
SubShader {
Pass{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse; //因为color在0~1之间所以用fixed即可
fixed4 _Specular;
float _Gloss; //Gloss范围很大
struct a2v{
float4 vertex : POSITION;
float3 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 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
fixed3 diffuse = _Diffuse.rgb * _LightColor0.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));//reflect函数要求光源到点方向
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.0f);
}
ENDCG
}
}
FallBack "Specular"
}
j结果:
5.2 实践:逐像素光照
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Unity Shaders Book/Chapter6/SpecularPixel - Level" {
Properties {
_Diffuse("Diffuse", Color) = (1,1,1,1)
_Gloss("Gloss", Range(8.0, 256)) = 20 //高光反射颜色
_Specular("Specular", Color) = (1,1,1,1)//控制高光区域大小
}
SubShader {
Pass{
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Diffuse; //因为color在0~1之间所以用fixed即可
fixed4 _Specular;
float _Gloss; //Gloss范围很大
struct a2v{
float4 vertex : POSITION;
float3 normal : NORMAL;
};
struct v2f{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
fixed3 worldPos : TEXCOORD1;
};
v2f vert(a2v v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
return o;
}
fixed4 frag(v2f i) : SV_Target{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 diffuse = _Diffuse.rgb * _LightColor0.rgb * saturate(dot(worldNormal, worldLightDir));
fixed3 reflectDir = normalize(reflect(-worldLightDir, worldNormal));//reflect函数要求光源到点方向
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.0f);
}
ENDCG
}
}
FallBack "Specular"
}
结果:
5.3 Blinn-Phong光照模型
更改如下:
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
fixed3 halfDir = normalize(worldLightDir + viewDir);
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, halfDir)), _Gloss);
结果:
6、使用Unity内置的函数
函数名 | 描述 |
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) | 把方向矢量从世界空间转换到模型空间 |
只有在前向渲染中——WorldSpaceLightPos0等才会正确赋值。