Shader第二十五講:描邊及外發光(轉)

描邊以及外發光一般有如下幾種實現方法:

【一貼圖加工】

原理:

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

優點:

(1)效率高,對渲染效率沒有增加任何負擔。

(2)畫風可個性化。充分滿足定制的需求。

缺點:

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

陰影高亮:

需要物體面和光源的相對關系不變。

描邊:

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

對于棱角不分明的物體(如球體人物等)需要物體面和相機的相對視角穩定。因為邊緣和觀察角度有關。

(2)增加工作量

代表作:

《武士》《武士II》

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

原理:

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

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

優點:

(1)實現簡單,對渲染效率增加負擔極小。

(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次,

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

第二遍:正常渲染物體

優點:

(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指令,這種方法能夠避免法線外拓方法中產生的法線過渡不均勻的問題,但同時會產生新的問題,將普通物體置于其和相機之間有時候會,產生顯示錯誤,如右下圖的小黑點漏出。

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鏡面強度系數

specF=pow(specBase,32);

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

specF=floor(specF*_SpecFacStep)/_SpecFacStep;

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

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

}//

}

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容