您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“Unity Shader怎么實(shí)現(xiàn)卡通素描風(fēng)格渲染”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
卡通風(fēng)格渲染
卡通風(fēng)格渲染的游戲畫(huà)面通常物體顏色分界明顯,具有黑色的線條描邊??ㄍㄤ秩镜膶?shí)現(xiàn)有多種方法,基于色調(diào)的著色技術(shù)是其中之一,實(shí)現(xiàn)過(guò)程中通過(guò)使用漫反射系數(shù)對(duì)一維紋理進(jìn)行采樣,控制漫反射色調(diào)。之前通過(guò)一張漸變紋理來(lái)控制漫反射顏色實(shí)現(xiàn)過(guò)卡通風(fēng)格的渲染效果??ㄍL(fēng)格的高光效果往往是一塊分界明顯的色塊,而物體邊緣通常會(huì)有描邊。本節(jié)中將通過(guò)基于模型的方式進(jìn)行描邊,而不是之前的屏幕后處理的方式。
輪廓線渲染
輪廓線的渲染是實(shí)時(shí)渲染中應(yīng)用非常廣泛的一種效果。目前常用的5種繪制模型輪廓線的方法:
基于觀察角度和表面法線的輪廓線渲染
使用視角方向和表面法線的點(diǎn)乘結(jié)果得到輪廓線信息。簡(jiǎn)單快速,一個(gè)Pass可以得到結(jié)果,局限性較大,不能得到比較滿意的描邊效果。
正背面渲染
使用兩個(gè)Pass,一個(gè)渲染背面,另一個(gè)渲染正面面片。快速有效,適用于大多數(shù)表面平滑的模型。
基于圖像處理的輪廓線
之前屏幕后處理以及利用深度紋理就是采用的這種方式??梢杂糜谌魏文P?,但深度和法線變化很小的輪廓無(wú)法檢測(cè),比如緊貼的薄平面。
基于輪廓邊緣的輪廓線檢測(cè)
通過(guò)計(jì)算得到精確的輪廓邊,然后直接渲染,渲染出獨(dú)特的風(fēng)格。檢測(cè)一條邊是否為輪廓邊,只需檢測(cè)和這條邊相鄰的三角面片是否滿足:
(N0*V>0)!=(N1*V>0)
N0和N1分別是相鄰面片的法向,這種方式由于是單幀提取輪廓,當(dāng)幀數(shù)較低時(shí),會(huì)出現(xiàn)幀與幀之間的跳躍性。
最后一種是以上的綜合渲染方法
首先找到精確的輪廓邊,將模型和輪廓渲染到紋理,再通過(guò)圖形處理的方式識(shí)別輪廓線,在圖像空間下進(jìn)行風(fēng)格化渲染。
下面使用正背面渲染的方式進(jìn)行輪廓線的勾勒,之前的正背面渲染中,是直接將頂點(diǎn)在裁剪空間中向裁剪空間下的法線方向進(jìn)行偏移。這里使用觀察空間,在觀察空間下對(duì)頂點(diǎn)進(jìn)行觀察空間下的法向偏移,區(qū)別在于觀察空間是一個(gè)線性空間,盡管之前的效果也基本達(dá)到要求,但線性空間下的處理的結(jié)果會(huì)更加連貫。為了防止一些內(nèi)凹的模型在使用正面剔除后發(fā)生背面遮擋正面的情況,先對(duì)頂點(diǎn)法線的z分量進(jìn)行定值處理,再將法線歸一化后進(jìn)行擴(kuò)張。這樣可以使擴(kuò)張后背面更加扁平化,降低遮擋正面面片的可能性。即:
viewNormal.z=-0.5; viewNormal=normalize(viewNormalize); viewPos=viewPos+viewNormal*_Outline;
卡通風(fēng)格的高光通常表現(xiàn)為在模型上是一塊塊分界明顯的色塊。為了得到這種效果不再使用之前的高光計(jì)算模型。之前Blinn-Phong時(shí),使用法線方向點(diǎn)乘視角和光照方向和的一半,再與_Gloss參數(shù)進(jìn)行指數(shù)操作得到系數(shù):
float spec=pow(max(0,dot(normal,halfDir)),_Gloss);
對(duì)于卡通風(fēng)格的高光反射光照模型,同樣需要計(jì)算normal和halfDir的點(diǎn)乘結(jié)果,然后直接與一個(gè)閾值相比較,大于該閾返回1,小于該閾值返回0,以形成分界明顯的色塊界限:
float spec=dot(normal,halfDir); spec=step(threshold,spec);
CG的step函數(shù)實(shí)現(xiàn)和閾值比較返回0,1結(jié)果,第一個(gè)為參考值,第二個(gè)參數(shù)大于第一個(gè)參數(shù),返回1,否則返回0。
這種直接0,1的取值方式會(huì)在高光的邊緣區(qū)域形成鋸齒,因?yàn)橛?,1突變。為了得到高光邊緣叫平滑的效果,可以在邊界處的小塊區(qū)域內(nèi)進(jìn)行平滑處理。
float spec=dot(normal,halfDir); spec=lerp(0,1,smoothstep(-w,w,spec-threshold));
使用CG的smoothstep函數(shù),w是一個(gè)較小的值,當(dāng)spec-threshold小于-w時(shí),返回0,大于w時(shí),返回1,否則在0,1之間進(jìn)行插值。這樣的效果是在[-w,w]區(qū)間,即高光反射邊緣,進(jìn)行0到1的平滑過(guò)渡,防止出現(xiàn)鋸齒。w的值可以使用CG的fwidth函數(shù)得到鄰域像素之間的近似導(dǎo)數(shù)(像素之間的變化程度)值。
代碼實(shí)例:
Shader "Custom/Chapter14_ToonShading" { Properties{ _MainTex("MainTex",2D)="white"{} _Color("Color",Color)=(1,1,1,1) _RampTex("Ramp",2D)="white"{} _Outline("Outline",Range(0,1))=0.1 _OutlineColor("OutlineColor",Color)=(0,0,0,1) _Specular("SpecularColor",Color)=(1,1,1,1) _SpecularScale("Specular Scale",Range(0,0.1))=0.01 } SubShader{ Pass{ NAME "OUTLINE" Cull Front CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" fixed _Outline; fixed4 _OutlineColor; struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; }; struct v2f{ float4 pos:SV_POSITION; }; v2f vert(a2v v){ v2f o; float4 pos=mul(UNITY_MATRIX_MV,v.vertex); float3 normal=mul((float3x3)UNITY_MATRIX_IT_MV,v.normal); normal.z=-0.5; pos=pos+float4(normalize(normal),0)*_Outline; o.pos=mul(UNITY_MATRIX_P,pos); return o; } fixed4 frag(v2f i):SV_Target{ return fixed4(_OutlineColor.rgb,1); } ENDCG } Pass{ Tags{"LightMode"="ForwardBase"} Cull Back CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "Lighting.cginc" #include "UnityCG.cginc" #include "AutoLight.cginc" sampler2D _MainTex; float4 _MainTex_ST; fixed4 _Color; sampler2D _RampTex; fixed4 _Specular; fixed _SpecularScale; struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; float2 texcoord:TEXCOORD0; }; struct v2f{ float4 pos:SV_POSITION; float2 uv:TEXCOORD0; float3 worldNormal:TEXCOORD1; float3 worldPos:TEXCOORD2; SHADOW_COORDS(3) }; v2f vert(a2v v){ v2f o; o.pos=UnityObjectToClipPos(v.vertex); o.uv=TRANSFORM_TEX(v.texcoord,_MainTex); o.worldNormal=mul(v.normal,(float3x3)unity_WorldToObject); o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz; TRANSFER_SHADOW(o); return o; } fixed4 frag(v2f i):SV_Target{ fixed3 worldNormal=normalize(i.worldNormal); fixed3 worldLightDir=normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 worldViewDir=normalize(UnityWorldSpaceViewDir(i.worldPos)); fixed3 worldHalf=normalize(worldLightDir+worldViewDir); fixed4 c=tex2D(_MainTex,i.uv); fixed3 albedo=c.rgb*_Color.rgb; fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz*albedo; UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos); fixed diff=dot(worldNormal,worldLightDir); diff=(diff*0.5+0.5)*atten; fixed3 diffuse=_LightColor0.rgb*albedo*tex2D(_RampTex,float2(diff,diff)).rgb; fixed spec=dot(worldNormal,worldHalf); fixed w=fwidth(spec)*2.0; fixed3 specular=_Specular.rgb*lerp(0,1,smoothstep(-w,w,spec+_SpecularScale-1))*step(0.0001,_SpecularScale); //最后添加的step(0.0001,_SpecularScale);是為了控制當(dāng)Specular為0時(shí),不出現(xiàn)高光效果 return fixed4(ambient+diffuse+specular,1.0); } ENDCG } } FallBack "Diffuse" //這里的回調(diào)需要注意包含能夠處理陰影的特殊Pass }
實(shí)例效果:
素描風(fēng)格渲染
素描風(fēng)格的渲染在非真實(shí)渲染中應(yīng)用也比較流行。目前實(shí)時(shí)的素描風(fēng)格渲染是通過(guò)使用提前生成的素描紋理來(lái)實(shí)現(xiàn)的。
這些紋理組成色調(diào)藝術(shù)映射,紋理從左到右筆觸逐漸增多,用于模擬不同光照效果下的漫反射效果,從上到下對(duì)應(yīng)每張紋理的多級(jí)漸遠(yuǎn)紋理。
下面的過(guò)程不考慮多級(jí)漸遠(yuǎn)紋理的生成,直接使用6張紋理進(jìn)行渲染。首先在頂點(diǎn)著色器計(jì)算逐頂點(diǎn)光照,根據(jù)光照結(jié)果決定紋理的混合權(quán)重,然后傳遞給片元著色器,片元著色器根據(jù)權(quán)重混合6張紋理的采樣結(jié)果。
實(shí)例代碼:
Shader "Custom/Chapter14_Hatching" { Properties{ _Color("Color",Color)=(1,1,1,1) _TileFactor("Tile Factor",Float)=1 _Outline("Outline",Range(0,1))=0.1 _Hatch0("Hatch 0",2D)="white"{} _Hatch2("Hatch 1",2D)="white"{} _Hatch3("Hatch 2",2D)="white"{} _Hatch4("Hatch 3",2D)="white"{} _Hatch5("Hatch 4",2D)="white"{} _Hatch6("Hatch 5",2D)="white"{} //TileFactor為紋理的平鋪系數(shù),值越大,素描線條越密集 } SubShader{ Tags{"RenderType"="Opaque" "Queue"="Geometry"} UsePass "Custom/Chapter14_ToonShading/OUTLINE" //素描風(fēng)格往往也需要繪制輪廓線,使用之前的渲染輪廓Pass Pass{ Tags{"LightMode"="ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "AutoLight.cginc" fixed4 _Color; float _TileFactor; fixed _Outline; sampler2D _Hatch0; float4 _Hatch0_ST; sampler2D _Hatch2; float4 _Hatch2_ST; sampler2D _Hatch3; float4 _Hatch3_ST; sampler2D _Hatch4; float4 _Hatch4_ST; sampler2D _Hatch5; float4 _Hatch5_ST; sampler2D _Hatch6; float4 _Hatch6_ST; struct a2v{ float4 vertex:POSITION; float3 normal:NORMAL; half4 texcoord:TEXCOORD0; }; struct v2f{ float4 pos:SV_POSITION; float2 uv:TEXCOORD0; fixed3 hatchWeight0:TEXCOORD1; fixed3 hatchWeight1:TEXCOORD2; float3 worldPos:TEXCOORD3; SHADOW_COORDS(4) //6個(gè)權(quán)重值分別存儲(chǔ)在2個(gè)float3類型變量中 }; v2f vert(a2v v){ v2f o; o.pos=UnityObjectToClipPos(v.vertex); o.uv=v.texcoord.xy*_TileFactor; //_TileFactor用來(lái)控制素描線條的密集程度(TEX的WrapMode為Repeat) float3 worldLightDir=normalize(WorldSpaceLightDir(v.vertex)); float3 worldNormal=UnityObjectToWorldNormal(v.normal); float3 diff=max(0,dot(worldLightDir,worldNormal)); //這里的關(guān)鍵便是通過(guò)計(jì)算漫反射系數(shù)來(lái)區(qū)分采樣權(quán)重,并將權(quán)重與不同密集程度的TEX相對(duì)應(yīng) o.hatchWeight0=fixed3(0,0,0); o.hatchWeight1=fixed3(0,0,0); //使用世界空間下的光照方向和法線方向得到漫反射系數(shù) //初始化權(quán)重值,*7分為7個(gè)區(qū)間,并根據(jù)hatchFactor的值,為權(quán)重賦值 float hatchFactor=diff*7; if(hatchFactor>6){ //不做任何賦值,保持純白 } else if(hatchFactor>5.0){ o.hatchWeight0.x=hatchFactor-5.0; } else if(hatchFactor>4.0){ o.hatchWeight0.x=hatchFactor-4.0; o.hatchWeight0.y=1.0-o.hatchWeight0.x; } else if(hatchFactor>3.0){ o.hatchWeight0.y=hatchFactor-3.0; o.hatchWeight0.z=1.0-o.hatchWeight0.y; } else if(hatchFactor>2.0){ o.hatchWeight1.x=hatchFactor-2.0; } else if(hatchFactor>1.0){ o.hatchWeight1.x=hatchFactor-1.0; o.hatchWeight1.y=1.0-o.hatchWeight1.x; } else{ o.hatchWeight1.y=hatchFactor; o.hatchWeight1.z=1.0-o.hatchWeight1.y; } o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz; TRANSFER_SHADOW(o) return o; } fixed4 frag(v2f i):SV_Target{ fixed4 hatchTex0=tex2D(_Hatch0,i.uv)*i.hatchWeight0.x; fixed4 hatchTex1=tex2D(_Hatch2,i.uv)*i.hatchWeight0.y; fixed4 hatchTex2=tex2D(_Hatch3,i.uv)*i.hatchWeight0.z; fixed4 hatchTex3=tex2D(_Hatch4,i.uv)*i.hatchWeight1.x; fixed4 hatchTex4=tex2D(_Hatch5,i.uv)*i.hatchWeight1.y; fixed4 hatchTex5=tex2D(_Hatch6,i.uv)*i.hatchWeight1.z; //得到6張素描紋理采樣結(jié)果,并乘以對(duì)應(yīng)的權(quán)重 fixed4 whiteColor=fixed4(1,1,1,1)*(1.0-i.hatchWeight0.x-i.hatchWeight0.y-i.hatchWeight0.z-i.hatchWeight1.x-i.hatchWeight1.y-i.hatchWeight1.z); fixed4 hatchColor=hatchTex0+hatchTex1+hatchTex2+hatchTex3+hatchTex4+hatchTex5+whiteColor; //計(jì)算純白的占比程度,素描風(fēng)格中會(huì)有留白,并且高光部分也是白色 UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos); return fixed4(hatchColor.rgb*_Color.rgb*atten,1.0); //混合各個(gè)顏色,并與衰減和模型顏色相乘得到最終顏色 } ENDCG } } FallBack "Diffsue" }
實(shí)例效果:
“Unity Shader怎么實(shí)現(xiàn)卡通素描風(fēng)格渲染”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。