這篇文章主要介紹unity3d如何實(shí)現(xiàn)基于屏幕空間的描邊,文中介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們一定要看完!
創(chuàng)新互聯(lián)建站是一家專(zhuān)注于成都做網(wǎng)站、網(wǎng)站建設(shè)與策劃設(shè)計(jì),開(kāi)江網(wǎng)站建設(shè)哪家好?創(chuàng)新互聯(lián)建站做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)十載,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:開(kāi)江等地區(qū)。開(kāi)江做網(wǎng)站價(jià)格咨詢(xún):18980820575
由于壓縮圖片效果不好,建議在私人博客查看原圖:
Outline based on Image Space yuyujunjun.github.io
圖中的碎面特效在淺色背景下效果不明顯。而如果在unity編輯器中選中該物體,unity會(huì)給它進(jìn)行描邊,而這個(gè)效果比較符合要求。
描邊方法多樣,原理大多都通俗易懂,這篇文章重點(diǎn)是介紹一些unity與之相關(guān)的API。
優(yōu)勢(shì)及應(yīng)用
可以描繪出對(duì)象身上一切鏤空的部位,不論是內(nèi)邊界還是外邊界。
可以根據(jù)需要判斷遮擋(無(wú)非就是多添加一個(gè)紋理緩存)
不足
無(wú)法判斷法向量突變的“邊界”,所以如果只使用這個(gè)方法,和卡通渲染給人的感覺(jué)可能會(huì)不同。
倒過(guò)來(lái)想,對(duì)于圖像中的某個(gè)對(duì)象(比如說(shuō),那個(gè)球),如果我們要對(duì)它進(jìn)行描邊,我們需要它的邊界位置,邊界大小和邊界顏色。
而從正面想,我們想要給一幅圖像上的一片像素點(diǎn)組成的形狀進(jìn)行描邊。首先我們需要獲取該形狀在圖像上的位置,隨后我們對(duì)它邊界處的像素點(diǎn)進(jìn)行標(biāo)定,這個(gè)步驟很自由,因?yàn)槔碚撋夏憧梢杂酶魇礁鳂拥臉?biāo)定方法,標(biāo)定各式各樣的形狀,只要保證在GPU上并行實(shí)現(xiàn)即可,最后我們?cè)賹⑦@些標(biāo)定位置的像素點(diǎn)覆蓋到原圖像上,即可進(jìn)行描邊。
步驟
第一步:
這一步就是記載其位置,圖示只是為了便于說(shuō)明,所以用了一張常規(guī)的渲染結(jié)果。實(shí)際上我們最終使用的是其深度圖,深度圖的好處是,深度圖變相標(biāo)明了有對(duì)象的區(qū)域和沒(méi)有對(duì)象的區(qū)域。沒(méi)有對(duì)象的區(qū)域深度值當(dāng)然全部都為1。
第二步:
第二步我們將第一步獲取到的圖片的邊界進(jìn)行拓展,對(duì)邊界區(qū)域?qū)懭胍粋€(gè)值??梢钥闯鲞@張圖片相較于上一張“粗獷”了許多。
關(guān)于如何判斷是否是該物體的邊界,因?yàn)槲覀兩弦粡垐D片已經(jīng)標(biāo)明了對(duì)象區(qū)域和非對(duì)象區(qū)域,那么我們只需要對(duì)每個(gè)像素點(diǎn)周?chē)蓸?,如果周?chē)邢袼攸c(diǎn)在對(duì)象區(qū)域內(nèi),則該像素點(diǎn)應(yīng)該為邊界。
這里有一個(gè)小trick,只要周?chē)蓸拥狞c(diǎn)的值加起來(lái)不為0,則可以判斷其為邊界。
代碼如下:
fixed4 col1 = tex2D(_MainTex,i.screenPos.xy); fixed4 col2 = tex2D(_MainTex,float2(i.screenPos.x + 4/_ScreenParams.x,i.screenPos.y)); fixed4 col3 = tex2D(_MainTex,float2(i.screenPos.x - 4/_ScreenParams.x,i.screenPos.y)); fixed4 col4 = tex2D(_MainTex,i.screenPos.xy); fixed4 col5 = tex2D(_MainTex,float2(i.screenPos.x ,i.screenPos.y+ 4/_ScreenParams.y)); fixed4 col6 = tex2D(_MainTex,float2(i.screenPos.x ,i.screenPos.y- 4/_ScreenParams.y)); if((col1.x + col1.y + col1.z + col2.x + col2.y + col2.z + col3.x + col3.y + col3.z + col4.x + col4.y + col4.z + col5.x + col5.y + col5.z+ col6.x + col6.y + col6.z)>0.01) return fixed4(_OutlineColor.rgb,i.vertex.z);
有趣的是,這一步可以非常復(fù)雜,而這里只是進(jìn)行了簡(jiǎn)單的擴(kuò)張。
第三步:
第三步我們便將邊界處的像素疊加上去,這時(shí)候,第一步的結(jié)果起了不同的作用。在第二步中,第一步的結(jié)果的目的是標(biāo)識(shí)物體位置以便于第二步勾畫(huà)邊界,而在第三步中,是為了和第二步的結(jié)果作減法來(lái)告訴我們哪些地方不是邊界,而是對(duì)象本身。
注意事項(xiàng)
雖然以上兩張圖(一張標(biāo)定位置,一張標(biāo)定邊界)就可以完成基于圖形空間的描邊,但事實(shí)上如果需要判斷遮擋性或者為邊界添加特殊的輔助效果,大可以再增加一些緩存,這個(gè)自己權(quán)衡就好。
使用替換的著色器對(duì)場(chǎng)景進(jìn)行渲染
功能:相機(jī)會(huì)照常渲染場(chǎng)景,但是場(chǎng)景中的物體會(huì)使用你所規(guī)定的shader渲染。
這個(gè)功能的用處是:比如說(shuō),需要渲染一張場(chǎng)景法線信息的貼圖,或者我們上面提到的我們要渲染的場(chǎng)景深度信息的貼圖。
這個(gè)功能對(duì)應(yīng)了兩個(gè)API,分別是:
Camera.RenderWithShader(Shader shader,string replacementTag) Camerea.SetReplacementShader(Shader shader,string replacementTag)
解釋一下replacementTag的用處,如果該tag為空,那么該相機(jī)能渲染的所有對(duì)象都會(huì)使用我們所定制的shader。如果有值,那么該相機(jī)只會(huì)渲染擁有特定Tag的材質(zhì)。比如:
Camerea.SetReplacementShader(depthshader,"RenderType")
相機(jī)只會(huì)渲染Render Type和depthshader的Render Type值相同的物體。
unity Shader中,如下方法指定shader的tag:
Tags { "TagName1" = "Value1" "TagName2" = "Value2" }
在Replacement Shader中,可以有多個(gè)subshader。對(duì)于一般的shader來(lái)說(shuō),subshader從上到下執(zhí)行,如果第一個(gè)subshader由于硬件原因無(wú)法被執(zhí)行,則會(huì)執(zhí)行下一個(gè),直到可以執(zhí)行為止。
而這里的subshader發(fā)揮著類(lèi)似的作用,如果subshader中有ReplacementTag,那么該subshader就會(huì)用于渲染真實(shí)場(chǎng)景的物體中,tag值和此subshader 的tag值相同的物體。
使用特定著色器將原始圖像拷貝到新圖像上
Graphics.Blit(RenderTexture src,RenderTexture dst,Material mat)
以上是“unity3d如何實(shí)現(xiàn)基于屏幕空間的描邊”這篇文章的所有內(nèi)容,感謝各位的閱讀!希望分享的內(nèi)容對(duì)大家有幫助,更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!