unity 基于MatCap實(shí)現(xiàn)適于移動(dòng)平臺(tái)的“次時(shí)代”車漆Shader

轉(zhuǎn)自:http://blog.csdn.net/poem_qianmo/article/details/55803629

這篇文章將基于MatCap的思想挖垛,在Unity中實(shí)現(xiàn)了具有高度真實(shí)感的MatCap車漆Shader耘纱。采用MatCap思想的Shader奉瘤,用低廉的計(jì)算成本窃判,就可以達(dá)到類似PBS非常真實(shí)的渲染效果,可謂是在移動(dòng)平臺(tái)實(shí)現(xiàn)次時(shí)代渲染效果的一種優(yōu)秀解決方案。
本文以車漆Shader為例,但MatCap思想能實(shí)現(xiàn)的郎逃,并不局限于車漆Shader。本來(lái)準(zhǔn)備給本文取名《一種基于MatCap的低計(jì)算成本挺份、高真實(shí)感移動(dòng)平臺(tái)Shader解決方案》的褒翰,但這個(gè)名字太大了,遂改之匀泊。

先看一下最終的效果圖影暴。

一、MatCap概述

Material Capture(材質(zhì)捕獲)探赫,通常被簡(jiǎn)稱為MatCap,在Zbrush撬呢、Sculptris伦吠、Mudbox等3D軟件中有比較多的使用。

MatCap Shader的基本思路是,使用某特定材質(zhì)球的貼圖毛仪,作為當(dāng)前材質(zhì)的視圖空間環(huán)境貼圖(view-space environment map)搁嗓,來(lái)實(shí)現(xiàn)具有均勻表面著色的反射材質(zhì)物體的顯示∠溲ィ考慮到物體的所有法線的投影的范圍在x(-1,1),y(-1,1),構(gòu)成了一個(gè)圓形腺逛,所以MatCap 貼圖中存儲(chǔ)光照信息的區(qū)域是一個(gè)圓形。

基于MatCap思想的Shader衡怀,可以無(wú)需提供任何光照棍矛,只需提供一張或多張合適的MatCap貼圖作為光照結(jié)果的“指導(dǎo)”即可。


上圖來(lái)自(http://digitalrune.github.io/DigitalRune-Documentation/html/9a8c8b37-b996-477a-aeab-5d92714be3ca.htm

不像一般的Shader抛杨,需要提供光照够委,需要在Shader代碼中進(jìn)行漫長(zhǎng)的演算,基于MatCap思想的Shader相當(dāng)于MatCap貼圖就把光照結(jié)果應(yīng)該是怎樣的標(biāo)準(zhǔn)答案告知Shader怖现,我們只用在試卷下寫出答案茁帽,進(jìn)行一些加工即可。

需要注意屈嗤,MatCap Shader有一定的局限性潘拨。因?yàn)閺哪撤N意義上來(lái)說(shuō),基于MatCap的Shader饶号,就是某種固定光照條件下铁追,從某個(gè)特定方向,特定角度的光照表現(xiàn)結(jié)果讨韭。

正是因?yàn)槭沁x擇的固定的MatCap貼圖脂信,得到相對(duì)固定的整體光照表現(xiàn),若單單僅使用MatCap透硝,就僅適用于攝像機(jī)不調(diào)整角度的情形狰闪,并不適合攝像機(jī)會(huì)頻繁旋轉(zhuǎn),調(diào)節(jié)角度的情形濒生。但我們可以在某些Shader中埋泵,用MatCap配合與光照交互的其他屬性,如將MatCap結(jié)合一個(gè)作為光照反射的顏色指導(dǎo)的Reflection Cube Map罪治,就有了與光照之間的交互表現(xiàn)丽声。這樣,就可以適當(dāng)彌補(bǔ)MatCap太過(guò)單一整體光照表現(xiàn)的短板觉义。

關(guān)于MatCap雁社,《UnityShaders and Effects Cookbook》一書(shū)的Chapter 5: LightingModels中,The Lit Sphere lighting model一節(jié)也有一些涉及晒骇。

二霉撵、MatCap貼圖的獲取

需要使用基于MatCap Shader磺浙,合適的MatCap 貼圖必不可少。顯而易見(jiàn)徒坡,MatCap貼圖的獲取撕氧,一般來(lái)說(shuō)有兩種方式。

  1. 自己制作喇完。對(duì)著3D軟件中的材質(zhì)球截圖伦泥。

  2. 從網(wǎng)絡(luò)上獲取。在網(wǎng)絡(luò)上使用“matcap“等關(guān)鍵字搜索后獲得锦溪。

這邊提供幾個(gè)可以獲取MatCap貼圖的網(wǎng)址:

[1] https://www.pinterest.com/evayali/matcap/

[2]https://www.google.com.hk/search?q=MatCap&newwindow=1&safe=strict&hl=zh-CN&biw=1575&bih=833&tbm=isch&tbo=u&source=univ&sa=X&ved=0ahUKEwju8JDTpZnSAhUGn5QKHawODTIQsAQIIg

[3]http://pixologic.com/zbrush/downloadcenter/library/#prettyPhoto

三不脯、基于MatCap實(shí)現(xiàn)Physically Based Shading的思路簡(jiǎn)析

關(guān)于基于MatCap思想實(shí)現(xiàn)Physicallybased Shading,這篇文章(http://blog.csdn.net/ndsc_dw/article/details/50700201)提供了一定的思路海洼,簡(jiǎn)單來(lái)說(shuō)跨新,就是用幾張MatCap貼圖來(lái)提供PBS需要的光滑度和金屬度,來(lái)模擬出PBS的效果坏逢。繼續(xù)展開(kāi)下去就脫離本文的主線了域帐,有興趣的朋友可以深入進(jìn)行了解。

四是整、基于MatCap思想的車漆Shader實(shí)現(xiàn)

此車漆Shader肖揣,除了用到MatCap,主要還需要提供一個(gè)Reflection Cube Map作為反射的顏色指導(dǎo)浮入,就可以適當(dāng)彌補(bǔ)MatCap太過(guò)單一的整體光照表現(xiàn)的短板龙优,實(shí)現(xiàn)非常真實(shí)且高效的車漆Shader效果。

此Shader賦給Material后事秀,Material在Inspector中的屬性表現(xiàn)如下:

其中的MatCap貼圖為:

Shader源碼如下:

[cpp] view plain copy

Shader "ShaderPrac/Car Paint Shader"
{
Properties
{
//主顏色
_MainColor("Main Color", Color) = (1.0, 1.0, 1.0, 1.0)
//細(xì)節(jié)顏色
_DetailColor("Detail Color", Color) = (1.0, 1.0, 1.0, 1.0)
//細(xì)節(jié)紋理
_DetailTex("Detail Textrue", 2D) = "white" {}
//細(xì)節(jié)紋理深度偏移
_DetailTexDepthOffset("Detail Textrue Depth Offset", Float) = 1.0
//漫反射顏色
_DiffuseColor("Diffuse Color", Color) = (0.0, 0.0, 0.0, 0.0)
//漫反射紋理
_DiffuseTex("Diffuse Textrue", 2D) = "white" {}
//Material Capture紋理
_MatCap("MatCap", 2D) = "white" {}
//反射顏色
_ReflectionColor("Reflection Color", Color) = (0.2, 0.2, 0.2, 1.0)
//反射立方體貼圖
_ReflectionMap("Reflection Cube Map", Cube) = "" {}
//反射強(qiáng)度
_ReflectionStrength("Reflection Strength", Range(0.0, 1.0)) = 0.5
}

SubShader  
{  
    Tags  
    {  
        "Queue" = "Geometry"  
        "RenderType" = "Opaque"  
    }  

    Pass  
    {  
        Blend Off  
        Cull Back  
        ZWrite On  

        CGPROGRAM  
        #include "UnityCG.cginc"  
        #pragma fragment frag  
        #pragma vertex vert  

        float4 _MainColor;  
        float4 _DetailColor;  
        sampler2D _DetailTex;  
        float4 _DetailTex_ST;  
        float _DetailTexDepthOffset;  
        float4 _DiffuseColor;  
        sampler2D _DiffuseTex;  
        float4 _DiffuseTex_ST;  
        sampler2D _MatCap;  
        float4 _ReflectionColor;  
        samplerCUBE _ReflectionMap;  
        float _ReflectionStrength;  

        //頂點(diǎn)輸入結(jié)構(gòu)  
        struct VertexInput  
        {  
            float3 normal : NORMAL;  
            float4 position : POSITION;  
            float2 UVCoordsChannel1: TEXCOORD0;  
        };  

        //頂點(diǎn)輸出(片元輸入)結(jié)構(gòu)  
        struct VertexToFragment  
        {  
            float3 detailUVCoordsAndDepth : TEXCOORD0;  
            float4 diffuseUVAndMatCapCoords : TEXCOORD1;  
            float4 position : SV_POSITION;  
            float3 worldSpaceReflectionVector : TEXCOORD2;  
        };  

        //------------------------------------------------------------  
        // 頂點(diǎn)著色器  
        //------------------------------------------------------------  
        VertexToFragment vert(VertexInput input)  
        {  
            VertexToFragment output;  

            //漫反射UV坐標(biāo)準(zhǔn)備:存儲(chǔ)于TEXCOORD1的前兩個(gè)坐標(biāo)xy彤断。  
            output.diffuseUVAndMatCapCoords.xy = TRANSFORM_TEX(input.UVCoordsChannel1, _DiffuseTex);  

            //MatCap坐標(biāo)準(zhǔn)備:將法線從模型空間轉(zhuǎn)換到觀察空間,存儲(chǔ)于TEXCOORD1的后兩個(gè)紋理坐標(biāo)zw  
            output.diffuseUVAndMatCapCoords.z = dot(normalize(UNITY_MATRIX_IT_MV[0].xyz), normalize(input.normal));  
            output.diffuseUVAndMatCapCoords.w = dot(normalize(UNITY_MATRIX_IT_MV[1].xyz), normalize(input.normal));  
            //歸一化的法線值區(qū)間[-1,1]轉(zhuǎn)換到適用于紋理的區(qū)間[0,1]  
            output.diffuseUVAndMatCapCoords.zw = output.diffuseUVAndMatCapCoords.zw * 0.5 + 0.5;  

            //坐標(biāo)變換  
            output.position = mul(UNITY_MATRIX_MVP, input.position);  

            //細(xì)節(jié)紋理準(zhǔn)備準(zhǔn)備UV,存儲(chǔ)于TEXCOORD0的前兩個(gè)坐標(biāo)xy  
            output.detailUVCoordsAndDepth.xy = TRANSFORM_TEX(input.UVCoordsChannel1, _DetailTex);  
              
            //深度信息準(zhǔn)備,存儲(chǔ)于TEXCOORD0的第三個(gè)坐標(biāo)z  
            output.detailUVCoordsAndDepth.z = output.position.z;  

            //世界空間位置  
            float3 worldSpacePosition = mul(unity_ObjectToWorld, input.position).xyz;  

            //世界空間法線  
            float3 worldSpaceNormal = normalize(mul((float3x3)unity_ObjectToWorld, input.normal));  

            //世界空間反射向量  
            output.worldSpaceReflectionVector = reflect(worldSpacePosition - _WorldSpaceCameraPos.xyz, worldSpaceNormal);  
              
            return output;  
        }  

        //------------------------------------------------------------  
        // 片元著色器  
        //------------------------------------------------------------  
        float4 frag(VertexToFragment input) : COLOR  
        {  
            //鏡面反射顏色  
            float3 reflectionColor = texCUBE(_ReflectionMap, input.worldSpaceReflectionVector).rgb * _ReflectionColor.rgb;  

            //漫反射顏色  
            float4 diffuseColor = tex2D(_DiffuseTex, input.diffuseUVAndMatCapCoords.xy) * _DiffuseColor;  

            //主顏色  
            float3 mainColor = lerp(lerp(_MainColor.rgb, diffuseColor.rgb, diffuseColor.a), reflectionColor, _ReflectionStrength);  

            //細(xì)節(jié)紋理  
            float3 detailMask = tex2D(_DetailTex, input.detailUVCoordsAndDepth.xy).rgb;  

            //細(xì)節(jié)顏色  
            float3 detailColor = lerp(_DetailColor.rgb, mainColor, detailMask);  

            //細(xì)節(jié)顏色和主顏色進(jìn)行插值易迹,成為新的主顏色  
            mainColor = lerp(detailColor, mainColor, saturate(input.detailUVCoordsAndDepth.z * _DetailTexDepthOffset));  

            //從提供的MatCap紋理中宰衙,提取出對(duì)應(yīng)光照信息  
            float3 matCapColor = tex2D(_MatCap, input.diffuseUVAndMatCapCoords.zw).rgb;  

            //最終顏色  
            float4 finalColor=float4(mainColor * matCapColor * 2.0, _MainColor.a);  

            return finalColor;  
        }  

        ENDCG  
    }  
}  

Fallback "VertexLit"  

}

Shader注釋已經(jīng)比較詳細(xì),下面對(duì)代碼中也許會(huì)不太理解睹欲,需要注意的地方進(jìn)行說(shuō)明供炼。

要使用MatCap貼圖,主要是將法線從模型空間轉(zhuǎn)換到視圖空間窘疮,并切換到適合提取紋理UV的區(qū)域[0,1]袋哼。(需要將法線從模型空間轉(zhuǎn)換到視圖空間,關(guān)于一些推導(dǎo)可以參考http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix或者http://www.cnblogs.com/flytrace/p/3379816.html

Unity內(nèi)置的矩陣UNITY_MATRIX_IT_MV闸衫,是UNITY_MATRIX_MV的逆轉(zhuǎn)置矩陣涛贯,其作用正是將法線從模型空間轉(zhuǎn)換到觀察空間。于是頂點(diǎn)著色器vert中的這兩句代碼就很容易理解了:

[cpp] view plain copy

//MatCap坐標(biāo)準(zhǔn)備:將法線從模型空間轉(zhuǎn)換到觀察空間蔚出,存儲(chǔ)于TEXCOORD1的后兩個(gè)紋理坐標(biāo)zw
output.diffuseUVAndMatCapCoords.z =dot(normalize(UNITY_MATRIX_IT_MV[0].xyz), normalize(input.normal));
output.diffuseUVAndMatCapCoords.w= dot(normalize(UNITY_MATRIX_IT_MV[1].xyz), normalize(input.normal));

而得到的視圖空間的法線疫蔓,區(qū)域是[-1含懊,1],要轉(zhuǎn)換到提取紋理UV的區(qū)域[0,1],就需要乘以0.5并加上0.5衅胀,那么頂點(diǎn)著色器vert中接下來(lái)的的這句代碼也就可以理解了:

[cpp] view plain copy

//歸一化的法線值區(qū)間[-1,1]轉(zhuǎn)換到適用于紋理的區(qū)間[0,1]
output.diffuseUVAndMatCapCoords.zw= output.diffuseUVAndMatCapCoords.zw * 0.5 + 0.5;

稍后,在片元著色器frag中酥筝,用在頂點(diǎn)著色器vert中準(zhǔn)備好的法線轉(zhuǎn)換成的UV值滚躯,提取出MatCap的光照細(xì)節(jié)即可:

[cpp] view plain copy

//從提供的MatCap紋理中,提取出對(duì)應(yīng)光照信息
float3matCapColor = tex2D(_MatCap, input.diffuseUVAndMatCapCoords.zw).rgb;

此Car Paint Shader其他實(shí)現(xiàn)嘿歌,主要就是一些基本的Shader知識(shí)點(diǎn)的配合掸掏,如頂點(diǎn)坐標(biāo)轉(zhuǎn)換,反射宙帝,漫反射丧凤,細(xì)節(jié)紋理的計(jì)算,都是比較基礎(chǔ)的內(nèi)容步脓,這里就不再贅述愿待,直接看Shader源碼即可。

最后看幾張截圖靴患,然后就是相關(guān)Shader與MatCap資源的下載:

五仍侥、Shader源碼與MatCap資源下載

從Github下載:

【Github】Shader源碼與相關(guān)MatCap資源下載

參考與延伸

[1] http://wiki.unity3d.com/index.php/MatCap

[2] https://www.assetstore.unity3d.com/en/#!/content/8221

[3] http://blog.csdn.net/cubesky/article/details/38682975

[4] https://www.assetstore.unity3d.com/en/#!/content/76361

[5] http://www.cnblogs.com/flytrace/p/3379816.html

[6] http://www.cnblogs.com/flytrace/p/3395911.html

[[7] http://digitalrune.github.io/DigitalRune-Documentation/html/9a8c8b37-b996-477a-aeab-5d92714be3ca.htm](file:///C:/Users/Administrator/Google%20%E4%BA%91%E7%AB%AF%E7%A1%AC%E7%9B%98/Shader/blog/%5b7%5d%20http:/digitalrune.github.io/DigitalRune-Documentation/html/9a8c8b37-b996-477a-aeab-5d92714be3ca.htm)

[8] https://forum.unity3d.com/threads/_object2world-or-unity_matrix_it_mv.112446/

[9]http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/the-normal-matrix

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市鸳君,隨后出現(xiàn)的幾起案子农渊,更是在濱河造成了極大的恐慌,老刑警劉巖或颊,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件砸紊,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡囱挑,警方通過(guò)查閱死者的電腦和手機(jī)醉顽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)看铆,“玉大人徽鼎,你說(shuō)我怎么就攤上這事〉耄” “怎么了否淤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)棠隐。 經(jīng)常有香客問(wèn)我石抡,道長(zhǎng),這世上最難降的妖魔是什么助泽? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任啰扛,我火速辦了婚禮嚎京,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘隐解。我一直安慰自己鞍帝,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布煞茫。 她就那樣靜靜地躺著帕涌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪续徽。 梳的紋絲不亂的頭發(fā)上蚓曼,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音钦扭,去河邊找鬼纫版。 笑死,一個(gè)胖子當(dāng)著我的面吹牛客情,可吹牛的內(nèi)容都是我干的其弊。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼裹匙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼瑞凑!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起概页,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤籽御,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后惰匙,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體技掏,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年项鬼,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哑梳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡绘盟,死狀恐怖鸠真,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情龄毡,我是刑警寧澤吠卷,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站沦零,受9級(jí)特大地震影響祭隔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜路操,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一疾渴、第九天 我趴在偏房一處隱蔽的房頂上張望千贯。 院中可真熱鬧,春花似錦搞坝、人聲如沸搔谴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)己沛。三九已至,卻和暖如春距境,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背垮卓。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工垫桂, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人粟按。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓诬滩,卻偏偏與公主長(zhǎng)得像坯台,于是被迫代替她去往敵國(guó)和親友驮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • <轉(zhuǎn)>我也忘了轉(zhuǎn)自哪里,抱歉,感謝原作者 什么是Shader Shader(著色器)是一段能夠針對(duì)3D對(duì)象進(jìn)行操作...
    星易乾川閱讀 5,595評(píng)論 1 16
  • Babybus-u3d技術(shù)交流-【淺墨Unity3D Shader編程】之二 雪山飛狐篇:Unity的基本Shad...
    Babybus_Unity閱讀 5,667評(píng)論 4 61
  • 轉(zhuǎn)載注明出處:點(diǎn)擊打開(kāi)鏈接 Shader(著色器)是一段能夠針對(duì)3D對(duì)象進(jìn)行操作柏蘑、并被GPU所執(zhí)行的程序庙曙。Shad...
    游戲開(kāi)發(fā)小Y閱讀 3,371評(píng)論 0 4
  • 兩條車轍輾出一個(gè)人生 身后長(zhǎng)長(zhǎng)的空镜, 交織的鎖鏈 是夢(mèng)的裂痕 栓起歸鄉(xiāng)人的熱戀 奔赴似水的流年 皎潔的月色 混著白雪...
    圩原君閱讀 186評(píng)論 0 0
  • 沒(méi)有當(dāng)過(guò)兵的人,永遠(yuǎn)無(wú)法理解軍人的情感捌朴;沒(méi)有在部隊(duì)摸爬滾打過(guò)的人吴攒,自然也沒(méi)法理解戰(zhàn)友之間的深情厚誼。作為一名曾經(jīng)的...
    _小六閱讀 758評(píng)論 0 4