轉(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ō)有兩種方式。
自己制作喇完。對(duì)著3D軟件中的材質(zhì)球截圖伦泥。
從網(wǎng)絡(luò)上獲取。在網(wǎng)絡(luò)上使用“matcap“等關(guān)鍵字搜索后獲得锦溪。
這邊提供幾個(gè)可以獲取MatCap貼圖的網(wǎng)址:
[1] https://www.pinterest.com/evayali/matcap/
[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
[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