介绍
1.光源:使用辐射度(irradiance)来量化光。可以使用光源方向l和表面法线n之间得夹角得余弦值(点积)来得到,也就是光源方向l dot 表面法线n。
2…高光反射(specular)部分表示物体表面是如何反射光线的;而漫反射(diffuse)则是表示有多少光线会被折射,吸收,和散射出表面。
3…标准光照模型包含了四个部分:自发光(emissive),高光反射(specular),漫反射(diffuse)环境光(ambient)
4…漫反射光照公式(diffuse):diffuse = (lightColor * diffuseColor)max(0,dot(n,L),[n是表面法线,L是光源方向]。最大值截取到0是为了让物体的背部不会被后面的光源照亮。
5.高光反射)(specular):高光比较复杂,需要表面法线,视角方向,光源方向,反射方向等信息。在这四个矢量中,只需要知道其中三个,第四个可以通过公式计算出来(r = 2(n*l)n – l);
这样,就可以利用phone来计算高光反射部分(specualr = (Dot(lightColor,specularColor)max(0,dot(v,r))m.gloss;[r是光源方向的反方向].其中,MainGloss是材质的光泽度(gloss),也被称为反光度(shininess)。r是光源的反方向。
6.逐顶点光照与逐像素光照最大的不同是在于光照模型在那一部分进行计算。前者是在顶点着色器中计算,后者实在片元着色器中计算。
逐像素光照模型
Shader "Unlit/Chapter6-DiffusepixelLevel"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1.0,1.0,1.0,1.0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
//输入结构
fixed4 _Diffuse;
// float4 _MainTex_ST;
struct appdata
{
float4 vertex : POSITION;
float3 normal: NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 worldNormal : TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
//顶点着色器不计算光照,只需要把世界空间下的法线传递给片元着色器即可
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //通过unity的内置变量来得到环境光部分
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.0);
}
ENDCG
}
}
}
逐顶点光照模型
Shader "Unlit/Chapter6-DiffuseVertexLevel"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1.0,1.0,1.0,1.0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
//输入结构
fixed4 _Diffuse;
// float4 _MainTex_ST;
struct appdata
{
float4 vertex : POSITION;
float3 normal: NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 color : COLOR;
};
v2f vert (appdata v)
{
v2f o;
//获得参数
o.pos = UnityObjectToClipPos(v.vertex); //顶点着色器最基本的任务就是把模型空间转换到裁剪空间
fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));//获得世界空间法线
fixed3 worldLight = normalize(_WorldSpaceLightPos0.xyz); //通过_WorldSpaceLightPos0获得光源方向
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //通过unity的内置变量来得到环境光部分
//normalize归一化是为了防止结果为负值
//_LightColor0是内置变量,用来访问该Pass处理的光源的颜色和强度信息
//saturate是cg提供的一种函数,作用是把参数截取到[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
}
}
}
半兰伯特光照模型
1.逐像素反射光照有一个问题,那就是在光照无法达到的区域,模型的外观通常是全黑的,所以,有一种改善技术被提出来,这就是半兰伯特模型,不过需要注意的是,半兰伯特模型是没有任何物理依据的,它仅仅只是一个视觉加强技术。
2.广义的半兰伯特公式:diffuse = (lightColor *MainDiffuse)(α(n * l)+β)
3.半兰伯特公式:diffuse = (lightColor *MainDiffuse)(0.5(n * l)+0.5)
Shader "Unlit/Chapter6-DiffusepixelLevel"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1.0,1.0,1.0,1.0)
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
//输入结构
fixed4 _Diffuse;
// float4 _MainTex_ST;
struct appdata
{
float4 vertex : POSITION;
float3 normal: NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 worldNormal : TEXCOORD0;
};
v2f vert (appdata v)
{
v2f o;
//顶点着色器不计算光照,只需要把世界空间下的法线传递给片元着色器即可
o.pos = UnityObjectToClipPos(v.vertex);
o.worldNormal = mul(v.normal,(float3x3)unity_WorldToObject);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; //通过unity的内置变量来得到环境光部分
fixed3 worldNormal = normalize(i.worldNormal); //在世界空间中获得法线信息
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz); //获得世界空间中的光方向
fixed halfLambert = dot(worldNormal,worldLightDir) * 0.5 + 0.5; //半兰伯特的光照计算公式
fixed3 diffuse = _LightColor0.xyz * _Diffuse * halfLambert;
fixed3 color = ambient + diffuse;
//输出稍有不同
return fixed4(color, 1.0);
}
ENDCG
}
}
}
逐像素光照的高光反射
1.Blinn-Phone光照模型。Blinn没有是用反射方向,而是引入一个新的矢量h,它是通过对视角方向v,和光照方向l相加后归一化得到的。
Blinn-Phone计算高光反射的公式如下:Specular = (c.linght * m.specular)max(0,n * h)m.gloss
高光反射的计算公式:
Specular = (c.linght * m.specular)max(0,v * r)m.gloss
计算高光反射需要知道四个参数:入射光线的颜色和强度c.linght,材质的高光反射系数m.specular,视角方向v与反射方向r。其中,反射方向的计算可以使用Cg提供的函数reflect(i,n);i是入射方向,n是法线方向。
Shader "Unlit/Chapter6-Specular Pixel-Level"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1.0,1.0,1.0,1.0)
_Specular("Specular",Color) = (1.0,1.0,1.0,1.0) //控制材质的高光反射颜色
_Gloss("Gloss",Range(8.0,256)) = 20 //控制高光区域大小
}
SubShader
{
LOD 100
Pass
{
//LightMode标签是Pass标签中的一种,只有正确定义了LightMode,才能使用unity内置的光照变量,例如_LightColor0
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
//输入结构
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
// float4 _MainTex_ST;
struct appdata
{
float4 vertex : POSITION;
float3 normal: NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 worldNormal : TEXCOORD0; //世界空间下的法线方向
fixed3 worldPos : TEXCOORD1; //世界空间下的顶点坐标
};
v2f vert (appdata 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; //通过unity的内置变量来得到环境光部分
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
}
}
}
Blinn-Phone光照模型的高光反射
Shader "Unlit/Chapter6-BlinnPhone"
{
Properties
{
_Diffuse ("Diffuse", Color) = (1.0,1.0,1.0,1.0)
_Specular("Specular",Color) = (1.0,1.0,1.0,1.0) //控制材质的高光反射颜色
_Gloss("Gloss",Range(8.0,256)) = 20 //控制高光区域大小
}
SubShader
{
LOD 100
Pass
{
//LightMode标签是Pass标签中的一种,只有正确定义了LightMode,才能使用unity内置的光照变量,例如_LightColor0
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
//输入结构
fixed4 _Diffuse;
fixed4 _Specular;
float _Gloss;
// float4 _MainTex_ST;
struct appdata
{
float4 vertex : POSITION;
float3 normal: NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
fixed3 worldNormal : TEXCOORD0; //世界空间下的法线方向
fixed3 worldPos : TEXCOORD1; //世界空间下的顶点坐标
};
v2f vert (appdata 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; //通过unity的内置变量来得到环境光部分
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);
//新矢量h
fixed3 halfDir = normalize(worldLightDir + viewDir);
//计算镜面反射公式
fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0,dot(worldNormal, halfDir)), _Gloss);
//输出稍有不同
return fixed4(ambient + diffuse + specular, 1.0);
}
ENDCG
}
}
}