(一)基本的物理知识
光学中用辐照度来量化光。光线由光源发射出来以后会与一些物体相交,结果为散射或者吸收。散射分为折射和透射。
为了区分不同的散射方向,在光照模型中用不同的部分计算:高光反射和漫反射。
着色指的是根据材质属性(如漫反射属性等),光源信息(如光源方向、折射度),使用一个等式去计算沿某个观察方向的出射度的过程,我们也把这个等式称为光照模型。
(1).环境光:环境光模拟间接光照。
(2).自发光:材质的自发光颜色,自发光的表面不会照亮周围的表面。
(3).漫反射:漫反射中视角的位置不一样,反射是完全随机的。漫反射符合兰伯特定律,反射光的强度与表面法线和光源方向之间的夹角的余弦值成正比.
(4).高光反射:计算高光反射需要的信息比较多,如表面法线,视角方向,光源方向,反射方向等。常用的有Phong模型,Blinn模型
逐像素光照:在片元着色器中计算为逐像素光照,以每个像素为基础,得到它的法线(可以是对顶点法线插值得到的,也可以是从法线纹理中采样得到的),然后进行光照模型的计算,这种在面片之间对顶点法线进行查值得技术为Phong着色,PHONG插值或法线插值着色技术
逐像素光照:在顶点着色器中计算为逐顶点光照,也称为高洛得着色,我们会在每个顶点上计算光照,然后在渲染图元内部进行线性插值,最后输出像素颜色。
(二)在Unity实现简单的光照模型常用的函数
1.Unity环境光在window->lighting->Ambient Source/Ambient Color/Ambient Intensity中控制,在shader中通过UNITY_LIGHTMODEL_AMBIENT可以得到环境光的颜色和强度信息
2.saturate函数(Cg中提供的函数),防止一个标量或矢量为负值,转为[0,1]
saturate(x) x可以为float2 float3 float4
3 reflect(i,n)函数 法线方向n和入射方向i,计算反射方向的函数reflect(i,n)
4.UnityCG.cginc中有一些常用的帮助函数
_WorldSpaceLightPos0 获得当前世界空间的光照
光源方向 normalize(_WorldSpaceLightPos0.xyz);
视角方向 normalize(_WorldSpaceCameraPos.xyz-v.pos.xyz)
float3 WorldSpaceViewDir(float4 v) 输入一个模型空间中的顶点位置,返回世界空间中从该顶点到摄像机的观察方向,内部实现用UnityWorldSpaceViewDir
float3UnityWorldSpaceViewDir(float4 v) 输入一个世界空间中的顶点位置,返回世界空间中从该顶点到摄像机的观察方向
float3 UnityWorldSpaceViewDir(float4 v){
return _WorldCameraPos.xyz - worldPos;
}
float3 ObjSpaceViewDir(float4 v) 输入一个模型空间中的顶点位置,返回模型空间中从该顶点到摄像机的观察方向
float3 WorldSpaceLightDir(float4 v)输入一个模型空间的顶点位置,返回世界空间中该点到到光源的方向,内部实现用的UnityWorldSpaceLightDir,没有归一化
float3 UnityWorldSpaceLightDir(float4 v)输入一个世界空间的顶点位置,返回世界空间中该点到到光源的方向,没有归一化
float3 ObjSpaceLightDir(float4 v)输入一个模型空间的顶点位置,返回模型空间到光源的光照方向,没有归一化
float3 UnityObjectToWorldNormal(float3 normal)把法线从模型空间转换到世界空间
float3 UnityObjectToWorldDir(float3 dir)把方向矢量从模型空间转化到世界空间
float3 UnityWorldToObjectDir(float3 dir)把方向矢量从世界空间到模型空间
这些函数一般没有归一化
(三)漫反射光照模型的实现
1.逐顶点实现漫反射光照
// Upgrade NOTE: replaced '_World2Object' with 'unity_WorldToObject'
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
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;
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);//mul(UNITY_MATRIX_MVP,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
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"
}
2.逐像素实现漫反射光照
Shader "Unity Shaders Book/Chapter 6/Diffuse Pixel-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));