Shader第二十五讲:描边及外发光(转)

描边以及外发光一般有如下几种实现方法:

【一贴图加工】

Shader第二十五讲:描边及外发光(转)_第1张图片

原理:

直接在贴图上对应模型边缘的位置画描边,凹的地方画阴影轮廓,凸起的地方画高光。

优点:

(1)效率高,对渲染效率没有增加任何负担。

(2)画风可个性化。充分满足定制的需求。

缺点:

(1)这种方法需要考虑视角光线的属性。

阴影高亮:

需要物体面和光源的相对关系不变。

描边:

对于棱角分明的物体(如立方体,风车) 可任意使用。因为边缘固定。

对于棱角不分明的物体(如球体人物等)需要物体面和相机的相对视角稳定。因为边缘和观察角度有关。

(2)增加工作量

代表作:

《武士》《武士II》

【二 法线与视线计算】(Rim Lighting)

Shader第二十五讲:描边及外发光(转)_第2张图片
Shader第二十五讲:描边及外发光(转)_第3张图片

原理:

正常来说,物体法线与视线(从顶点至相机的方向)角度越一致,就越是能被玩家看见的中间。而边缘一般与法线垂直。由点乘即可计算轮廓光。

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"

}

【三法线外拓】

Shader第二十五讲:描边及外发光(转)_第4张图片
Shader第二十五讲:描边及外发光(转)_第5张图片

原理:

也有叫挤出的

用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第二十五讲:描边及外发光(转)_第6张图片
Shader第二十五讲:描边及外发光(转)_第7张图片

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

}

}

}

【五描边加光照】

Shader第二十五讲:描边及外发光(转)_第8张图片
Shader第二十五讲:描边及外发光(转)_第9张图片

Shader第二十五讲:描边及外发光(转)_第10张图片
Shader第二十五讲:描边及外发光(转)_第11张图片

一:描边

二 :光的特殊处理:光的离散化

主要就两句代码

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

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

}//

}

}

你可能感兴趣的:(Shader第二十五讲:描边及外发光(转))