最近美術同事希望我能幫忙做一個由內而外流光的shader,效果類似下圖:
我最初的思路是利用模型的頂點座標:美術製作模型的時候就將模型的原點設在中心。這樣我在vertex shader中用length(i.vertex)求出每個頂點到中心的距離,進而在fragment shader中求出每個像素是否該顯示流光。
於是就想做個實驗,看看Unity中的模型頂點的分佈規律。我寫了一個shader,讓離原點越近的頂點顏色越偏黑,離原點越遠的頂點越偏紅。
Shader "Custom/VisualizeVertexPos" {
SubShader {
Pass {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
Lighting Off
Fog { Color (0,0,0,0) }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma only_renderers gles d3d9 gles3 metal
struct vertexInput {
float4 vertex : POSITION;
};
struct vertexOutput {
float4 pos : POSITION;
fixed4 col : COLOR;
};
vertexOutput vert(vertexInput i)
{
vertexOutput o;
o.pos = mul(UNITY_MATRIX_MVP, i.vertex);
o.col = fixed4(length(i.vertex / 10), 0, 0, 1);
return o;
}
float4 frag(vertexOutput i) : COLOR
{
return i.col;
}
ENDCG
}
}
//調試過程中註釋掉Fallback以便發現錯誤
//FallBack "Diffuse"
}
在Unity中創建一個Unity自帶的Plane,應用這個shader,效果如下:
看來Unity自帶Plane的座標原點正好是在Plane的中心。
不過接下來問題來了。當我將Plane複製一個之後,屏幕中的兩個Plane就都變成了純紅色。
但是如果將其中一個Plane換個Shader,比如換成Diffuse,另外一個Plane就又恢復成之前中間黑色的樣子了。
兩個Plane使用同一個材質同一個Shader時,它們倆的Draw Call會合批。因此可以推測Draw Call的合批會對頂點座標的大小產生影響。
還可以進一步印證這個推測。把其中一個Plane換成Cylinder,用同一個Shader,也是兩個模型都變成了純紅色。Stats顯示Draw Calls爲1,有1個Draw Call被合批。
轉動攝像機,讓Plane或Cylinder移出攝像機視椎體外,留在視椎體內的模型的顏色就會發生變化。當只有Cylinder留在視椎體內時,Cylinder的顏色就變成了更接近黑色的暗紅。
不過值得一提的是,並不是說兩個模型使用同一個材質同一個Shader時,它們倆的Draw Call會合批。
比如,添加一個Sphere並不會對已有的模型顏色產生影響。
就此請教了另一位同事,看來是因爲Sphere頂點數太多,無法與Plane的Draw Call合批。從Stats看也的確如此,Draw Calls是2,沒有合批:
另外,如果兩個模型的縮放(Scale)不相等,這兩個模型的Draw Call也無法合批。在上面兩個Plane使用相同材質相同shader的情況中,如果把其中一個Plane的Scale改成(0.5, 0.5, 0.5),那麼Draw Calls也是2,它們又會恢復成中心是黑色的樣子。
看來還不能基於頂點座標來做這個需求,只好採用紋理座標(TEXCOORD0)了。後來的實驗表明,紋理座標的大小是不會受Draw Call合批影響的。