上一篇傳送門:
https://blog.csdn.net/qq_27534999/article/details/100925621
頂點色在卡通渲染中有挺多應用,本篇會在上一篇的基礎上,運用模型頂點色來控制細節。塞爾達荒野之息不一定是用這種方法,也可能是用額外的貼圖來實作,這裡算是抛磚引玉一下,擴充一下思路。(不過這方法效果還挺不錯哦!)
用頂點色控制細節還是有很多好處的,首先就是效果比較平滑(畢竟自動插值),之前曾嘗試過用額外貼圖控制,結果到處都是狗牙鋸齒你懂的,挺難受的。
先上頂點色調整後的最終效果:
然後看看之前的效果:
之前的效果還是比較粗糙的,有許多細節不是很完美:
①脖子部分被頭遮蓋,應該有陰影
②臉部不希望有太多陰影,應盡量保持明亮
③除了頭發外,其他地方不希望出現高光
④大腿部分的邊緣光不夠明顯,頭發部分的邊緣光又過多了
可知,①、②屬于陰影問題,③屬于高光問題,④屬于邊緣光問題。
有些問題,可以通過拆分模型,給不同的材質球參數來解決,但是這樣會讓材質球個數變多,進而SetPassCall變多,在移動端上可能造成性能問題。是以,頂點色處理無疑是個好辦法,我們可以用頂點色的R、G、B通道分别控制陰影、高光、邊緣光。
接下來将一步步講解如何用頂點色來對細節做修正:
一、準備模型
首先确認模型是否帶有頂點色資訊,在 Unity 内選中模型 Mesh 資源,即可在右下角觀察。
如果是跟我一樣下載下傳的模型的話,很遺憾地會發現隻有 uv 和 skin ,沒有頂點色資訊,這裡得自己用 3ds max 或 maya 重新導出一下(随意指定一下頂點色後導出即可)。
之後,觀察到視窗裡有 colors 就沒問題了。
二、準備工具
可以直接在 3ds Max 或者 Maya 裡繪制頂點色,不過個人還是習慣在 Unity 裡繪制,這樣也友善觀察調整。
(想在 3dsMax 裡刷頂點色的請看下一篇:https://blog.csdn.net/qq_27534999/article/details/101080649)
一般這類工具會有公司的程式專門來開發,如果沒有的話,可以去 Unity 的 Asset Store 搜尋插件來用,這邊推薦一個 TOZ Vertex Painter,小巧且免費。
插件導入後,在 Window -> TOZ -> Tools -> Vertex Painter 打開面闆,如下圖所示:
此時點選模型時,視窗可能會報錯。會提示模型這邊需要添加 Mesh Collider,且要使用 Mesh Filter 和 Mesh Renderer (這個插件不支援 Skinned Mesh Renderer),這些都要處理一下。模型設定如下圖所示:
這時候再選中模型,就可以愉快地刷頂點色了~!刷完後點選 SAVE NEW MESH 會另存為一個新的模型檔案。
三、Shader 修改
接下來是 Shader 的修改,擷取頂點色資訊然後對陰影、高光做出調整。
首先,在結構體中加入頂點色,當然别忘了 vert 中要指派:
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
fixed4 color : COLOR;
};
struct v2f
{
float2 uv : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
UNITY_FOG_COORDS(3)
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
};
...
v2f vert (appdata v)
{
v2f o;
...
o.color = v.color;
...
return o;
}
然後是針對陰影、高光、邊緣光的計算略有修改,R通道控制陰影,G通道控制高光,B通道控制邊緣光。
R通道:原點乘值區間為[-1,1],這裡我希望控制陰影頂點色的修改能夠覆寫這個區間,因為有些部位我希望永遠是暗部,這樣最極端的情況是把點乘值-1修正為1,這裡用 (頂點色 - 0.5)* 4。
G、B通道:高光、邊緣光兩個通道的點乘值,稍微修正就好,不需要太大的調整,是以這裡用 (頂點色 - 0.5)* 2。
fixed diffValue = dot(worldNormal, worldLightDir)+(i.color.r-0.5)*4;
...
fixed spec = dot(worldNormal, worldHalfDir)+(i.color.g-0.5)*2;
...
fixed rimValue = pow(1 - dot(worldNormal, worldViewDir+(i.color.b-0.5)*2), _RimPower);
這樣,Shader的修改就完成了。
四、刷頂點色
剛改完Shader後,視覺表現會很奇怪(見左圖),這是因為頂點色預設黑色(0, 0, 0)。
如果要恢複到原來的狀态,根據我們之前修改 Shader 的公式,隻要将所有頂點色塗上灰色(0.5, 0.5, 0.5)即可(見左圖)。
接下來着手解決之前提出的四個問題,根據我們的需求,對頂點色RGB進行調整即可。
①脖子部分被頭遮蓋,應該有陰影 —— 脖子部分 R 值為0
②臉部不希望有太多陰影,應盡量保持明亮 —— 臉部 R 值調高
③除了頭發外,其他地方不希望出現高光 —— 除頭發外其他部位 G 值為0
④大腿部分的邊緣光不夠明顯,頭發部分的邊緣光又過多了 —— 大腿 B值調高,頭發 B值調低
調整後的效果如下圖所示:
效果明顯比之前要好多了~!
五、成果
按照慣例,最後給出完整 Shader 如下:
Shader "Custom/ToonShadingSimple_v3"
{
Properties
{
[Header(Main)]
_MainTex ("Texture", 2D) = "white" {}
_Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)
_RimColor ("RimColor", Color) = (1.0, 1.0, 1.0, 1.0)
_ShadowThreshold ("ShadowThreshold", Range(-1.0, 1.0)) = 0.2
_ShadowBrightness ("ShadowBrightness", Range(0.0, 1.0)) = 0.6
_RimThreshold ("RimThreshold", Range(0.0, 1.0)) = 0.35
_RimPower ("RimPower", Range(0.0, 16)) = 4.0
_Specular ("Specular", Color) = (1, 1, 1, 1)
_SpecularScale("Specular Scale", Range(0, 0.1)) = 0.02
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Cull Back
Tags { "LightMode"="ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
// make fog work
#pragma multi_compile_fog
#include "UnityCG.cginc"
#include "Lighting.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
fixed4 color : COLOR;
};
struct v2f
{
float2 uv : TEXCOORD0;
float3 worldNormal : TEXCOORD1;
float3 worldPos : TEXCOORD2;
UNITY_FOG_COORDS(3)
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
};
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed4 _RimColor;
fixed _ShadowThreshold;
fixed _ShadowBrightness;
fixed _RimThreshold;
half _RimPower;
fixed4 _Specular;
fixed _SpecularScale;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.worldNormal = mul(v.normal, (float3x3)unity_WorldToObject);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
o.color = v.color;
UNITY_TRANSFER_FOG(o,o.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
//return i.color;
fixed3 worldNormal = normalize(i.worldNormal); //法線 N
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); //光照方向 L
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos)); //視角方向 V
fixed3 worldHalfDir = normalize(worldLightDir + worldViewDir); //高光計算用
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
fixed spec = dot(worldNormal, worldHalfDir)+(i.color.g-0.5)*2;
// w值也可用一個較小的值代替,效果差别不大
fixed w = fwidth(spec)*2.0;
fixed4 specular = _Specular * lerp(0,1,smoothstep(-w, w, spec+_SpecularScale-1)) * step(0.001, _SpecularScale);
fixed diffValue = dot(worldNormal, worldLightDir)+(i.color.r-0.5)*4;
fixed diffStep = smoothstep(-w+_ShadowThreshold, w+_ShadowThreshold, diffValue);
fixed4 light = _LightColor0 * 0.5 + 0.5;
fixed4 diffuse = light * col * (diffStep + (1 - diffStep) * _ShadowBrightness) * _Color;
// 模仿參考文章的方法,感覺效果不是太好
// fixed rimValue = 1 - dot(worldNormal, worldViewDir);
// fixed rimStep = step(_RimThreshold, rimValue * pow(dot(worldNormal,worldLightDir), _RimPower));
fixed rimValue = pow(1 - dot(worldNormal, worldViewDir)+(i.color.b-0.5)*2, _RimPower);
fixed rimStep = smoothstep(-w+_RimThreshold, w+_RimThreshold, rimValue);
fixed4 rim = light * rimStep * 0.5 * diffStep * _RimColor;
fixed4 final = diffuse + rim + specular;
// apply fog
UNITY_APPLY_FOG(i.fogCoord, final);
return final;
}
ENDCG
}
}
}
謝謝觀賞~!
應美術需求,下一篇将講解如何在 3dsMax 裡加載自定義 Shader 刷頂點色
下一篇傳送門:
https://blog.csdn.net/qq_27534999/article/details/101080649
參考資料:
沒有具體參考資料,算是對頂點色應用的一次嘗試吧