はじめに
テクスチャを読み込んでその情報から頂点座標をいじるシェーダーについて書きます。
頂点テクスチャフェッチという手法を使用します。
以下サンプルプロジェクトです。
サンプルプロジェクトにはフラグメントシェーダーのコードとサーフェスシェーダーでのコード両方入れてます。
今回はフラグメントシェーダーで説明しますが、サーフェスシェーダーも同じです!
github.com
頂点テクスチャフェッチとは
頂点テクスチャフェッチとは、頂点シェーダー内でテクスチャを参照する手法のことをいいます。
普通はテクスチャは画像として使用するのでフラグメントシェーダーで参照されます。
しかし、テクスチャは色以外の用途としても情報を格納することができます。
どういうことかというと、テクスチャと聞くと画像のイメージが強いですが、RGBAはただのfloat4つぶんの要素であり、テクスチャはそうしたfloat4つぶんのデータをただ格納しているだけです。
なので、そのデータを色以外の用途に使用することもできるわけです。
そのデータを頂点シェーダーで参照し、使用することを頂点テクスチャフェッチといいます。
頂点テクスチャフェッチの細かい内容については以下のサイトにとても詳しく書いてあるので、詳しくは以下をご覧ください!
wgld.org
今回つくるもの
今回はパーリンノイズのテクスチャをセットしてそのテクスチャ情報から頂点座標をイジります。
パーリンノイズのテクスチャは、フォトショップで フィルター>描画>雲模様2を白黒にして明るさとコントラストを変更することでつくれます。
こちらどうぞ使ってください↓↓
コードを書く
Unityのプロジェクトウィンドウから、Create > Shader > Unlit Shader で Shaderを作成します。以下のコードを記述します。
Shader "Custom/vtfSample_Fragment" { Properties { _MainTex ("Texture", 2D) = "white" {} _Tex2Dlod("Tex2DlodSample", 2D) = "white"{} _Scale("Scale", range(0, 10)) = 5 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Cull off Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float2 texcoord : TEXCOORD1; }; struct v2f { float2 uv : TEXCOORD0; float2 texcoord : TEXCOORD1; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; sampler2D _Tex2Dlod; float4 _Tex2Dlod_ST; half _Scale; v2f vert (appdata v) { v2f o; float d = tex2Dlod(_Tex2Dlod, float4(v.texcoord.xy, 0, 0)).r; v.vertex.y += d * _Scale; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } }
コードを記述したら、マテリアル にこちらのシェーダーを適用し、
プレーンにマテリアルをセットします。
すると、以下のような感じで頂点たちがでこぼこします。
コードの解説
重要なのはvert
関数の中のここです。
float d = tex2Dlod(_Tex2Dlod, float4(v.texcoord.xy, 0, 0)).r; v.vertex.y += d * _Scale;
バーテックスシェーダー内でテクスチャを参照する場合はtex2D
の代わりにtex2Dlod
を使用します。
tex2Dlod(テクスチャ、float4(U値, V値, 0, w値 = (値の指定0〜7))
です。なぜtex2Dlodを使用するかというと、以下の通りです。
これは テクスチャlodの値がdepth値(カメラからの距離)によって決定されるため depth値がvertexシェーダでメッシュのソートが解決されてPixelシェーダにデータが渡るときに計算が行われるので vertexシェーダ内ではlodの自動割り当てが出来なくなり 直接lod値を指定してあげる必要があるからです。
引用:UnityのシェーダーでVTF(vertex texture fetch) | ヤマヤタケシのブログ
tex2Dlodで取得した値を、v.vertex.y
に入れることで頂点のy軸の位置を変更しています。_Scale
はインスペクタから変更できるようにし、この値を乗算して頂点の移動具合を調整しています。
v.vertex.y += d * _Scale;
値の調整をする
これで頂点座標をテクスチャ情報から制御できました。
ただ、いまのままだと、_Scale
の値を上げると全体的な頂点座標がどんどん上に上がってしまいます。
何故こうなってしまうかというと、テクスチャの値はそもそも色として使用するために0から1の間の値しか格納されていないのです。マイナス値がありません。
つまりd
が0から1の値になっているところにScaleを乗算しているのでさらに値はプラス側に大きくなり、全体的に上にずれていくというわけです。
なので、0から1になっているdの値を-1から1に変更します。
float d = tex2Dlod(_Tex2Dlod, float4(v.texcoord.xy, 0, 0)).r; d = d * 2 -1; //0から1になっているdの値を-1から1に変換 v.vertex.y += d * _Scale;
0から1になっているd
の値に2を掛けて1を引くと-1から1になります。
なぜこれでそうなるかというと以下の通りです。
これで、元の位置のまま頂点が動くようになりました。
さいごに
以上でち🐼
これを使用することで、動かせば海の波のような凸凹感をつくれたり、地面の凸凹感を表現することができるかなとおもいます。