【UnityShader】フローマップ #43
前回の成果
視差オクルージョンマップを完成させた。
今回やること
フローマップを制作します。
事前準備
Scene上にPlaneを配置します。
また、こちらの画像はMainTextureとFlowMapに使用します。
MainTexture
FlowMap
ソースコード
Shader "Unlit/Flowmap" { Properties { _MainTex ("Texture", 2D) = "white" {} _FlowMap ("Flow Map", 2D) = "white" {} _FlowSpeed ("Flow Speed", float) = 1.0 _FlowPower ("Flow Power", float) = 1.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _FlowMap; float _FlowSpeed; float _FlowPower; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { float2 flowDir = tex2D(_FlowMap, i.uv) - 0.5; flowDir *= _FlowPower; float progress = frac(_Time.x * _FlowSpeed); float2 uv = i.uv + flowDir * progress; return tex2D(_MainTex, uv); } ENDCG } } }
まずは簡易的にフローマップを実装します。
フローマップ
フローマップとは、水などの流れの方向を決めるテクスチャです。
詳しく説明すると、水など流体の流れを2次元で表現するために使用するものです。
Y-upでX方向への動きを赤チャンネル(R)に、Z方向への動きを緑チャンネル(G)に渡します。
X(R)=128,Z(G)=128で静止状態となります。
uv値をズラす
float2 flowDir = tex2D(_FlowMap, i.uv) - 0.5;
flowDir *= _FlowPower;
ここでは、フローマップのuv値を0~1.0から-0.5~0.5にします。
そして、これをメインテクスチャのuv値に加算することにより、フローマップで定義した方向にuvをズラします。
メインテクスチャに適応させる
float progress = frac(_Time.x * _FlowSpeed);
float2 uv = i.uv + flowDir * progress;
備忘録程度に下記に関数の詳細を記載します。
_Time
xyzw | 値 |
---|---|
x | time / 20 |
y | time |
z | time * 2 |
w | time * 3 |
frac
// xの小数値を返す
frac(x)
frac関数により、0~1の間でループします。
それを上記で出したflowDirに乗算することにより、0~flowDirの値をループさせuv値を変化させていきます。
結果
流れているっぽくなりました。
※ Flow Speed = 1.0, Flow Power = 1.0
ただ、今は0~flowDirをループしています。
なので、下記のgifのようにループの切れ目がわかってしまいます。
ループの切れ目を修正
ループの切れ目があまりにも不自然なため、修正します。
ソースコード
fragment shaderを以下に置き換えてください。
fixed4 frag (v2f i) : SV_Target { float2 flowDir = tex2D(_FlowMap, i.uv) - 0.5; flowDir *= _FlowPower; float progress1 = frac(_Time.x * _FlowSpeed); float progress2 = frac(_Time.x * _FlowSpeed + 0.5); float2 uv1 = i.uv + flowDir * progress1; float2 uv2 = i.uv + flowDir * progress2; float lerpRate = abs((0.5 - progress1) / 0.5); fixed4 col1 = tex2D(_MainTex, uv1); fixed4 col2 = tex2D(_MainTex, uv2); return lerp(col1, col2, lerpRate); }
uvを2つ作成する
float2 flowDir = tex2D(_FlowMap, i.uv) - 0.5; flowDir *= _FlowPower; float progress1 = frac(_Time.x * _FlowSpeed); float progress2 = frac(_Time.x * _FlowSpeed + 0.5); float2 uv1 = i.uv + flowDir * progress1; float2 uv2 = i.uv + flowDir * progress2;
progress1は前のものと同じです。
progress2で、uvを0.5ズラしています。
補間する
float lerpRate = abs((0.5 - progress1) / 0.5); fixed4 col1 = tex2D(_MainTex, uv1); fixed4 col2 = tex2D(_MainTex, uv2); return lerp(col1, col2, lerpRate);
ここで2つのuv値を補間することにより切れ目を自然にしています。
progress1が0か1に近づくほど、lerpRateは1に近づきます。
progress1が0か1の状態は、ループの切れ目の部分です。
その状態の時にuvをズラしたprogress2を使用することにより、自然にしています。
progress1とlerpRate
progress1 | lerpRate |
---|---|
0 | 1.0 |
0.1 | 0.8 |
0.2 | 0.6 |
0.3 | 0.4 |
0.4 | 0.2 |
0.5 | 0.0 |
0.6 | 0.2 |
0.7 | 0.4 |
0.8 | 0.6 |
0.9 | 0.8 |
1.0 | 1.0 |
結果
ループの切れ目が自然になりました。
※ Flow Speed = 10.0, Flow Power = 1.0
ソースコードにコメントを付与
Shader "Unlit/Flowmap" { Properties { _MainTex ("Texture", 2D) = "white" {} _FlowMap ("Flow Map", 2D) = "white" {} _FlowSpeed ("Flow Speed", float) = 1.0 _FlowPower ("Flow Power", float) = 1.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _FlowMap; float _FlowSpeed; float _FlowPower; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { // フローマップのuv値を-0.5~0.5に float2 flowDir = tex2D(_FlowMap, i.uv) - 0.5; flowDir *= _FlowPower; // timeを0~1間でループ float progress1 = frac(_Time.x * _FlowSpeed); // 補間用に0.5ズラす float progress2 = frac(_Time.x * _FlowSpeed + 0.5); // uv + 0~flowDir float2 uv1 = i.uv + flowDir * progress1; float2 uv2 = i.uv + flowDir * progress2; // progress1が0か1に近づく時(ループの切れ目)にlerpRateが1に近づくように float lerpRate = abs((0.5 - progress1) / 0.5); fixed4 col1 = tex2D(_MainTex, uv1); fixed4 col2 = tex2D(_MainTex, uv2); // ループの切れ目を補間する return lerp(col1, col2, lerpRate); } ENDCG } } }
今回は以上となります。
ここまでご視聴ありがとうございました!