Unity3D中Shader光照模型基础

在讲Unity光照模型之前,先介绍图形学中的两个基础光照模型原理,将会更利于我们理解和使用Unity中的光照模型。

物体表面的光照颜色由入射光。物体材质,以及材质和光的交互规律共同决定的。

1、图形学中的两个基础光照模型

(1)漫反射与Lambert光照模型

粗糙的物体表面向各个方向等强度地反射光,这种等同度地散射现象称为光的漫反射(Diffuse Reflection)。产生光的漫反射现象的物体表面称为理想漫反射体,也称为Lambert反射体。

对于暴露在环境光下的Lambert反射体,可以用以下公式表示某点处的漫发射光强:

Iad= K*Ia

(约定:小写的字母表示下标,ad = ambient diffuse)

其中Ia表示环境光强度,k(0<k<1)为材质对环境光的反射系数,Iad是漫发射体与环境光交互时反射的光强。

理想的环境光是无序的,但是表面光强还依赖于光线的入射方向,这种现象可以用Lambert定律进行数学上的量化。

Lambert定律:当方向光照射到理想反射体上时,漫反射光的光强与入射光方向和入射点便面法向量夹角的余弦成正比。

Ild = K*Il*Cosa

Iid是漫反射体与方向光交互反射的光强,Il是方向光的光源强度,a是入射光线与定点法向量的夹角,k是材质的反射系数。

当入射角为零时,说明光线垂直于物体表面,漫反射光强最大;九十度时光线与物体表面平行,物体接收不到任何光线。

若N表示顶点单位法向量,L表示从顶点指向光源位置的单位向量(主意指向,不要弄反了),那么Cosa等价于N与L的点积。

即:

Ild = k*I*(N·L)

那么综合考虑环境光和方向光,Lambert光照模型可写为:

Idiff = Iad + Ild = k*Ia + k*Il*(N·L)

(****2****)镜面反射与****Phong****光照模型

Lambert模型较好的表现了粗糙表面的光照现象,如墙壁,纸张等,但是在用于诸如金属等有光泽效果的材质上则会显得呆板,表现不出光泽,主要原因是Lambert光照模型没有考虑表现这些表面的镜面反射效果。

一个光滑物体被光照射时,可以早某个方向上看到很强的反射光,这是因为在接近镜面反射角的一个区域内,反射了入射光的全部或大部分光强,这种现象称为镜面发射。

故此,Phong Bui Tuong提出一个计算镜面反射光强的光照模型,称为Phong模型,认为镜面反射的光强与反射光线和视线的夹角相关。

Phong模型数学表达式:

Ispec = Ks*Il*(V·R)^n

注:((V·R)^n表示(V·R)的n次方即(V·R)^n 。

Ks表示材质的镜面反射系数,n指高光指数,V表示从顶点到视点观察方向,R表示反射光方向。

高光指数n反映了物体表面的光泽程度。n越大,反射越集中,当偏离发射方向时,光线衰减的越厉害,只有当视线方向与反射光线非常接近时,才能看到镜面反射的高光现象。此时,镜面反射光将会在反射方向附近形成亮且小的光斑。n越小,表示物体表面越粗糙,反射光越分散,观察到的光斑区域越小,强度弱。

反射光方向向量R可以通过入射方向L(从顶点指向光源)和物体表面法向量L求出。

R+L = (2N·L)N

下面是一段Phong模型的cg代码:

void main_f( VertexScreen posIn,
 out float4 color : COLOR,
 uniform float4x4 worldMatrix,
 uniform float4x4 worldMatrix_IT,
 uniform float3 globalAmbient,
 uniform float3 eyePosition,
 uniform float3 lightPosition,
 uniform float3 lightColor,
 uniform float3 Kd,
 uniform float3 Ks,
 uniform float shininess)
{
 float3 worldPos = mul(worldMatrix,  posIn.objectPos).xyz;
 float3 N = mul(worldMatrix_IT,  posIn.objectNormal).xyz;
 N = normalize(N);
 //计算入射光方向、视线方向、反射光线方向
 float3 L = normalize(lightPosition - worldPos);
 float3 V = normalize(eyePosition - worldPos);
 float3 R = 2*max(dot(N, L), 0)*N-L;
 R = normalize(R);
 // 计算漫反射分量
 float3 diffuseColor = Kd * globalAmbient +Kd*lightColor*max(dot(N, L), 0);
 //计算镜面反射分量
 float3 specularColor = Ks *  lightColor*pow(max(dot(V, R), 0), shininess);
 color.xyz = diffuseColor + specularColor;
 color.w = 1;
}

(3)BlinnPhong光照模型

BlinnPhong光照模型混合和了Lambert的漫反射和标准的高光,渲染小股票有时比Phong高光更柔和、更平滑,此外它的处理速度相当快,因此成为许多CG软件中默认的光照渲染方法。

在Phong模型中,必须计算V·R的值,其中R为反射光线的单位向量,V为视线方向的单位向量,但是在BlinnPhong模型中,用N·H的值来取代V·R
BlinnPhong光照模型公式:

Ibp = Ks*Il(N·H)^n

其中N是入射点的单位法向量,H是“光入射方向L和视点方向V的中间向量”,通常也称为半角向量。
注:半角向量被广泛运用于各类光照模型,因为其既含有各种信息价值(光线入射方向以及视点方向),而且计算快速方便。

H = (L+V)/(|L+V|)

同样还有Cook-Torrence、BRDF等高级光照模型,可以自己去查资料拓展。

2、Unity中自定义光照模型

(1)、光照模型声明

光照模型函数的声明一般以“Lighting”开头的几个模板,可以被定义在你的Shader里的任何位置。

1、half4 Lighting<name> (SurfaceOutput s,half3 lightDir,half atten);
这个函数模板一般用于正向渲染路径(forward rendering path),并且是视线方向无关的。

2、half4 Lighting<name>(SurfaceOutput s,half3 lightDir,half3 viewDir,half atten);
一般用于forward rendering path,并且是视线方向相关的。

3、half4 Lighting<name>_OrePass(SurfaceOutput s,half4 light);
用于deferred lighting path。

你不需要声明所有的函数,一个光照模型是否使用view direction。如果一个光照模型不在deferred lighting起作用,你就不需要声明_PrePass函数,并且所有的Shader都会被编译成forward rendering path。

(2)、解码光照贴图

对于forward 和 deferred 光照,解码光照贴图可以被定制为何光照函数相似的方式。使用下面的函数取决于你是否使用了视线方向相关的光照模型。
解码基础的Unity光照贴图数据(通过color,totalcolor,indirectOnlyColor和scale参数)使用内置的DecodeLightmap函数。

定制的光照贴图解码函数会自动处理forward和deferred渲染路径。但是在deferred case Lighting<name> _PrePass函数将会在光照贴图解码之后被调用,并且光照参数将会包含实时光照和光照贴图。

如果需要的需要的话你可以用Unity内置的宏UNITY_PASS_PREPASSFINAL来区分forward和deferred渲染路径。

对于解码单个光照贴图的自定义函数:

1、half4 Lighting<Name> _SingleLingtingMap(SurfaceOutput s,fixed4 color);
用于不依赖视线方向的光照模型。

2、half4 Lighting<Name>(SurfaceOutput s,fixed4 color,half3 vieDir);
用于依赖视线方向的光照模型。

对于解码双光照贴图的自定义函数:

1、half4 Lighting<Name> _DualLightMap(SurfaceOurput s,fixed4 totalColor,fixed4 indirectionOnlyColor,half indirectFade);
用于不依赖视线方向的光照模型。
2、half4 Lighting<Name> _DualLightMap(SurfaceOurput s,fixed4 totalColor,fixed4 indirectionOnlyColor,half indirectFade,half3 viewDir);
用于依赖视线方向的光照模型。

对于解码定向光照贴图的自定义函数:

1、half4 Lighting<Name> _DirLightMap(SurfaceOutput s,fixed4 color,fixed4 scale,bool surfFuncWritesNormal);
用于不依赖视线方向的光照模型。

2、half Lighting<Name> _DirLightMap(SurfaceOutput s,fixed4 color,fixed4 scale,half3 viewDir,bool suefFuncWritesNormal,out half3 specColor);
用于依赖视线方向的光照函数。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容