Shader第二十五講:描邊及外發(fā)光(轉(zhuǎn))

描邊以及外發(fā)光一般有如下幾種實現(xiàn)方法:

【一貼圖加工】

原理:

直接在貼圖上對應(yīng)模型邊緣的位置畫描邊魄咕,凹的地方畫陰影輪廓,凸起的地方畫高光。

優(yōu)點:

(1)效率高谱俭,對渲染效率沒有增加任何負(fù)擔(dān)帮匾。

(2)畫風(fēng)可個性化啄骇。充分滿足定制的需求。

缺點:

(1)這種方法需要考慮視角光線的屬性瘟斜。

陰影高亮:

需要物體面和光源的相對關(guān)系不變缸夹。

描邊:

對于棱角分明的物體(如立方體,風(fēng)車) 可任意使用螺句。因為邊緣固定明未。

對于棱角不分明的物體(如球體人物等)需要物體面和相機(jī)的相對視角穩(wěn)定。因為邊緣和觀察角度有關(guān)壹蔓。

(2)增加工作量

代表作:

《武士》《武士II》

【二 法線與視線計算】(Rim Lighting)

原理:

正常來說趟妥,物體法線與視線(從頂點至相機(jī)的方向)角度越一致,就越是能被玩家看見的中間佣蓉。而邊緣一般與法線垂直披摄。由點乘即可計算輪廓光亲雪。

half rim =1.0- saturate(dot (normalize(IN.viewDir), o.Normal));

優(yōu)點:

(1)實現(xiàn)簡單,對渲染效率增加負(fù)擔(dān)極小疚膊。

(2)有漸變义辕,較真實。

缺點:

(1)只適用于法線較均勻過度的模型寓盗。而不適用于棱角分明的物體灌砖,如上圖中的立方體,故使用范圍與貼圖加工剛好相反傀蚌。

代表作:

《零世界》

代碼:

Shader"Example/Rim"{

Properties

{

_MainTex ("Texture",2D)

="white"{}

_BumpMap ("Bumpmap",2D)

="bump"{}

_RimColor ("Rim

Color",

Color) = (0.26,0.19,0.16,0.0)

_RimPower ("Rim

Power",

Range(0.5,8.0))

=3.0

}

SubShader

{

Tags {"RenderType"="Opaque"}

CGPROGRAM

#pragmasurface surfLambert

structInput {

float2

uv_MainTex;

float2

uv_BumpMap;

float3

viewDir;

};

sampler2D

_MainTex;

sampler2D

_BumpMap;

float4

_RimColor;

float_RimPower;

voidsurf (Input IN, inout SurfaceOutput o) {

o.Albedo =

tex2D (_MainTex, IN.uv_MainTex).rgb;

o.Normal =

UnpackNormal (tex2D (_BumpMap, IN.uv_BumpMap));

half rim =1.0- saturate(dot (normalize(IN.viewDir), o.Normal));

//saturate

限制值于[0,1]之間

o.Emission =

_RimColor.rgb * pow (rim, _RimPower);

}

ENDCG

}

Fallback"Diffuse"

}

【三法線外拓】

原理:

也有叫擠出的

用2個Pass 渲染物體2次基显,

第一遍:描邊,頂點沿法線方向外拓善炫。

第二遍:正常渲染物體

優(yōu)點:

(1)效果最好撩幽。

(2)適用范圍廣。

缺點:

(1)對效率有一定影響箩艺。因為有2個Pass,所以DrawCall為正常的2倍

(2)對于法線過度不均勻的模型窜醉。輪廓會有縫隙,如上圖立方體的左上角和右上角艺谆。

代表作:

《變身吧 主公》

代碼:

Shader"Toon/BasicOutline"{

Properties{

_Color("MainColor",Color)=(.5,.5,.5,1)

_OutlineColor("OutlineColor",Color)=(0,0,0,1)

_Outline("Outlinewidth",Range(.002,0.03))=.005

_MainTex("Base(RGB)",2D)="white"{}

_ToonShade("ToonShaderCubemap(RGB)",CUBE)=""{TexgenCubeNormal}

}

CGINCLUDE

#include"UnityCG.cginc"

structappdata{

float4vertex:POSITION;

float3normal:NORMAL;

};

structv2f{

float4pos:POSITION;

float4color:COLOR;

};

uniformfloat_Outline;

uniformfloat4_OutlineColor;

v2fvert(appdatav){

v2fo;

o.pos=mul(UNITY_MATRIX_MVP,v.vertex);

float3norm=mul((float3x3)UNITY_MATRIX_IT_MV,v.normal);

float2offset=TransformViewToProjection(norm.xy);

o.pos.xy+=offset*o.pos.z*_Outline;

o.color=_OutlineColor;

returno;

}

ENDCG

SubShader{

Tags{"RenderType"="Opaque"}

UsePass"Toon/Basic/BASE"

Pass{

Name"OUTLINE"

Tags{"LightMode"="Always"}

CullFront

ZWriteOn

ColorMaskRGB

BlendSrcAlphaOneMinusSrcAlpha

CGPROGRAM

#pragmavertexvert

#pragmafragmentfrag

half4frag(v2fi):COLOR{returni.color;}

ENDCG

}

}

SubShader{

Tags{"RenderType"="Opaque"}

UsePass"Toon/Basic/BASE"

Pass{

Name"OUTLINE"

Tags{"LightMode"="Always"}

CullFront

ZWriteOn

ColorMaskRGB

BlendSrcAlphaOneMinusSrcAlpha

CGPROGRAM

#pragmavertexvert

#pragmaexclude_renderersshaderonly

ENDCG

SetTexture[_MainTex]{combineprimary}

}

}

Fallback"Toon/Basic"

}

【四 Offset】

使用offset指令榨惰,這種方法能夠避免法線外拓方法中產(chǎn)生的法線過渡不均勻的問題,但同時會產(chǎn)生新的問題静汤,將普通物體置于其和相機(jī)之間有時候會读串,產(chǎn)生顯示錯誤,如右下圖的小黑點漏出撒妈。

Shader"Custom/Cartoon_Offset"{

Properties{

_MainTex("Texture",2D)="white"{}

}

SubShader

{

//描邊

pass

{

Cullfront

offset-5,-1

CGPROGRAM

#pragmavertexvert

#pragmafragmentfrag

#include"UnityCG.cginc"

sampler2D_MainTex;

float4_MainTex_ST;

structv2f{

float4pos:SV_POSITION;

float2uv:TEXCOORD0;

};

v2fvert(appdata_basev)

{

v2fo;

o.pos=mul(UNITY_MATRIX_MVP,v.vertex);

o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);

returno;

}

float4frag(v2fi):COLOR

{

returnfloat4(0,0,0,0);

}

ENDCG

}

//繪制物體

pass

{

offset2,-1

CGPROGRAM

#pragmavertexvert

#pragmafragmentfrag

#include"UnityCG.cginc"

sampler2D_MainTex;

float4_MainTex_ST;

structv2f{

float4pos:SV_POSITION;

float2uv:TEXCOORD0;

};

v2fvert(appdata_basev)

{

v2fo;

o.pos=mul(UNITY_MATRIX_MVP,v.vertex);

o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);

returno;

}

float4frag(v2fi):COLOR

{

float4texCol=tex2D(_MainTex,i.uv);

float4outp=texCol;

returnoutp;

}

ENDCG

}

}

}

【五描邊加光照】

一:描邊

二 :光的特殊處理:光的離散化

主要就兩句代碼

//***漫反射光離散化***

diffuseF=floor(diffuseF*_DiffuseStep)/_DiffuseStep;

//***鏡面反射光離散化***

specF=floor(specF*_SpecFacStep)/_SpecFacStep;

Shader"Custom/mylightCartoon"{

Properties{

_OutlineColor("OutlineColor",Color)=(0,0,0,1)

_Outline("Outlinewidth",Range(.002,0.03))=.005

_MainTex("Base(RGB)",2D)="white"{}

_DiffuseStep("_DiffuseStep0.1-3",Range(0.1,3))=0.5

_SpecFacStep("_SpecFacStep0.1-3",Range(0.1,3))=0.5

}

SubShader

{

pass

{

Name"OUTLINE"

Tags{"LightMode"="Always"}

Cullfront

CGPROGRAM

#pragmavertexvert

#pragmafragmentfrag

#include"UnityCG.cginc"

sampler2D_MainTex;

float4_MainTex_ST;

uniformfloat_Outline;

uniformfloat4_OutlineColor;

structv2f{

float4pos:POSITION;

float4color:COLOR;

};

v2fvert(appdata_fullv)

{

v2fo;

o.pos=mul(UNITY_MATRIX_MVP,v.vertex);

float3norm=mul((float3x3)UNITY_MATRIX_IT_MV,v.normal);

float2offset=TransformViewToProjection(norm.xy);

o.pos.xy+=offset*o.pos.z*_Outline;

o.color=_OutlineColor;

returno;

}

float4frag(v2fi):COLOR

{

returni.color;

}

ENDCG

}

pass

{

tags{"LightMode"="Vertex"}

CGPROGRAM

#pragmavertexvert

#pragmafragmentfrag

#include"UnityCG.cginc"

#include"Lighting.cginc"

sampler2D_MainTex;

float4_MainTex_ST;

float_DiffuseStep;

float_SpecFacStep;

structv2f{

float4pos:SV_POSITION;

float2uv:TEXCOORD0;

float3normal:TEXCOORD1;

float3lightDir:TEXCOORD2;

floatatten:TEXCOORD3;

float3viewDir:TEXCOORD4;

};

float4x4inverse(float4x4input)

{

#defineminor(a,b,c)determinant(float3x3(input.a,input.b,input.c))

//determinant(float3x3(input._22_23_23,input._32_33_34,input._42_43_44))

float4x4cofactors=float4x4(

minor(_22_23_24,_32_33_34,_42_43_44),

-minor(_21_23_24,_31_33_34,_41_43_44),

minor(_21_22_24,_31_32_34,_41_42_44),

-minor(_21_22_23,_31_32_33,_41_42_43),

-minor(_12_13_14,_32_33_34,_42_43_44),

minor(_11_13_14,_31_33_34,_41_43_44),

-minor(_11_12_14,_31_32_34,_41_42_44),

minor(_11_12_13,_31_32_33,_41_42_43),

minor(_12_13_14,_22_23_24,_42_43_44),

-minor(_11_13_14,_21_23_24,_41_43_44),

minor(_11_12_14,_21_22_24,_41_42_44),

-minor(_11_12_13,_21_22_23,_41_42_43),

-minor(_12_13_14,_22_23_24,_32_33_34),

minor(_11_13_14,_21_23_24,_31_33_34),

-minor(_11_12_14,_21_22_24,_31_32_34),

minor(_11_12_13,_21_22_23,_31_32_33)

);

#undefminor

returntranspose(cofactors)/determinant(input);

}

v2fvert(appdata_fullv)

{

v2fo;

o.pos=mul(UNITY_MATRIX_MVP,v.vertex);

o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);

o.normal=v.normal;

#ifndefUSING_DIRECTIONAL_LIGHT

float3lightPos=mul(inverse(UNITY_MATRIX_MV),unity_LightPosition[0]).xyz;

o.lightDir=lightPos;

#else

o.lightDir=mul(inverse(UNITY_MATRIX_MV),unity_LightPosition[0]).xyz;

#endif

float3viewpos=mul(UNITY_MATRIX_MV,v.vertex).xyz;

float3toLight=unity_LightPosition[0].xyz-viewpos.xyz*unity_LightPosition[0].w;

floatlengthSq=dot(toLight,toLight);

o.atten=1.0/(1.0+lengthSq*unity_LightAtten[0].z);

o.viewDir=mul((float3x3)inverse(UNITY_MATRIX_MV),float3(0,0,1)).xyz;

returno;

}

float4frag(v2fi):COLOR

{

float4texCol=tex2D(_MainTex,i.uv);

i.normal=normalize(i.normal);

i.lightDir=normalize(i.lightDir);

i.viewDir=normalize(i.viewDir);

//(1)漫反射強度

floatdiffuseF=max(0,dot(i.normal,i.lightDir));

//***漫反射光離散化***

diffuseF=floor(diffuseF*_DiffuseStep)/_DiffuseStep;

//(2)鏡面反射強度

floatspecF;

float3H=normalize(i.lightDir+i.viewDir);

floatspecBase=max(0,dot(i.normal,H));

//shininess鏡面強度系數(shù)

specF=pow(specBase,32);

//***鏡面反射光離散化***

specF=floor(specF*_SpecFacStep)/_SpecFacStep;

//(3)結(jié)合漫反射光與鏡面反射光

float4outp=texCol*unity_LightColor[0]*(0.9+0.5*diffuseF*i.atten)+unity_LightColor[0]*specF*1;

returnoutp;

}

ENDCG

}

}

}

還有一種方法句惯,與使用floor離散化不同克婶。

將diffuse的強度映射至[0,1] 然后通過一張亮度表來紋理查詢

diff=smoothstep(0,1,diff);

floattoon=tex2D(_ToonMap,float2(diff,diff)).r;

Shader"Tut/Shader/Toon/Cel_2"{

Properties{

_ToonMap("MaptoToon",2D)="white"{}

_Color("MainColor",color)=(1,1,1,1)

_Outline("ThickofOutline",range(0,0.1))=0.02

_Factor("Factor",range(0,1))=0.5

_ToonEffect("ToonEffect",range(0,1))=0.5

}

SubShader{

pass{

Tags{"LightMode"="Always"}

CullFront

ZWriteOn

CGPROGRAM

#pragmavertexvert

#pragmafragmentfrag

#include"UnityCG.cginc"

float_Outline;

float_Factor;

structv2f{

float4pos:SV_POSITION;

};

v2fvert(appdata_fullv){

v2fo;

float3dir=normalize(v.vertex.xyz);

float3dir2=v.normal;

floatD=dot(dir,dir2);

dir=dir*sign(D);

dir=dir*_Factor+dir2*(1-_Factor);

v.vertex.xyz+=dir*_Outline;

o.pos=mul(UNITY_MATRIX_MVP,v.vertex);

returno;

}

float4frag(v2fi):COLOR

{

float4c=0;

returnc;

}

ENDCG

}//endofpass

pass{

Tags{"LightMode"="ForwardBase"}

CullBack

CGPROGRAM

#pragmavertexvert

#pragmafragmentfrag

#include"UnityCG.cginc"

sampler2D_ToonMap;

float4_LightColor0;

float4_Color;

float_ToonEffect;

structv2f{

float4pos:SV_POSITION;

float3lightDir:TEXCOORD0;

float3viewDir:TEXCOORD1;

float3normal:TEXCOORD2;

};

v2fvert(appdata_fullv){

v2fo;

o.pos=mul(UNITY_MATRIX_MVP,v.vertex);

o.normal=v.normal;

o.lightDir=ObjSpaceLightDir(v.vertex);

o.viewDir=ObjSpaceViewDir(v.vertex);

returno;

}

float4frag(v2fi):COLOR

{

float4c=1;

float3N=normalize(i.normal);

float3viewDir=normalize(i.viewDir);

float3lightDir=normalize(i.lightDir);

floatdiff=max(0,dot(N,i.lightDir));

diff=(diff+1)/2;

diff=smoothstep(0,1,diff);

floattoon=tex2D(_ToonMap,float2(diff,diff)).r;

diff=lerp(diff,toon,_ToonEffect);

c=_Color*_LightColor0*(diff);

returnc;

}

ENDCG

}//

pass{

Tags{"LightMode"="ForwardAdd"}

BlendOneOne

CullBack

ZWriteOff

CGPROGRAM

#pragmavertexvert

#pragmafragmentfrag

#include"UnityCG.cginc"

float4_LightColor0;

sampler2D_ToonMap;

float4_Color;

float_ToonEffect;

structv2f{

float4pos:SV_POSITION;

float3lightDir:TEXCOORD0;

float3viewDir:TEXCOORD1;

float3normal:TEXCOORD2;

};

v2fvert(appdata_fullv){

v2fo;

o.pos=mul(UNITY_MATRIX_MVP,v.vertex);

o.normal=v.normal;

o.viewDir=ObjSpaceViewDir(v.vertex);

o.lightDir=_WorldSpaceLightPos0-v.vertex;

returno;

}

float4frag(v2fi):COLOR

{

float4c=1;

float3N=normalize(i.normal);

float3viewDir=normalize(i.viewDir);

floatdist=length(i.lightDir);

float3lightDir=normalize(i.lightDir);

floatdiff=max(0,dot(N,i.lightDir));

diff=(diff+1)/2;

diff=smoothstep(0,1,diff);

floatatten=1/(dist);

diff=diff*atten;

floattoon=tex2D(_ToonMap,float2(diff,diff)).r;

diff=lerp(diff,toon,_ToonEffect);

half3h=normalize(lightDir+viewDir);

floatnh=max(0,dot(N,h));

floatspec=pow(nh,32.0);

floattoonSpec=floor(spec*atten*2)/2;

spec=lerp(spec,toonSpec,_ToonEffect);

c=_Color*_LightColor0*(diff+spec);

returnc;

}

ENDCG

}//

}

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子捧挺,更是在濱河造成了極大的恐慌缕粹,老刑警劉巖岛抄,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件璃诀,死亡現(xiàn)場離奇詭異,居然都是意外死亡谷暮,警方通過查閱死者的電腦和手機(jī)蒿往,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來湿弦,“玉大人瓤漏,你說我怎么就攤上這事。” “怎么了蔬充?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵蝶俱,是天一觀的道長。 經(jīng)常有香客問我饥漫,道長榨呆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任庸队,我火速辦了婚禮积蜻,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘彻消。我一直安慰自己竿拆,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布证膨。 她就那樣靜靜地躺著如输,像睡著了一般鼓黔。 火紅的嫁衣襯著肌膚如雪央勒。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天澳化,我揣著相機(jī)與錄音崔步,去河邊找鬼。 笑死缎谷,一個胖子當(dāng)著我的面吹牛井濒,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播列林,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼瑞你,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了希痴?” 一聲冷哼從身側(cè)響起者甲,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎砌创,沒想到半個月后虏缸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡嫩实,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年刽辙,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片甲献。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡宰缤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撵溃,我是刑警寧澤疚鲤,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站缘挑,受9級特大地震影響集歇,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜语淘,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一诲宇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧惶翻,春花似錦姑蓝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颅筋,卻和暖如春宙暇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背议泵。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工占贫, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人先口。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓型奥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親碉京。 傳聞我的和親對象是個殘疾皇子厢汹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內(nèi)容