したかみ ぶろぐ

Unity成分多め

Unityで平面マッピング・Triplanarをする

始めに

shitakami.hatenablog.com

前回作成したマトリックスの模様をどうにかオブジェクトに貼り付けたいなと思い、調べたところ見つかったのが平面マッピングとTriplanarでした。

今回はこちらについてまとめようと思います。



平面マッピング(Planar)

「平面マッピング」という単語を調べたところ、wikipediaではこのように記載されていました。

テクスチャを貼り付けた平面を物体が存在する座標系上に配置し、それを物体に投影する。

個人的にはこの文章を読んでもよくわからないかったのですが、私なりに説明しますとテクスチャをワールド座標系の一方向から投影するという認識です。(間違っているかもしれません)


実装

基本的にテクスチャはUV座標をもとに貼り付けられますが、平面マッピングではワールド座標系の頂点座標をもとにテクスチャを貼り付けます。

なので、やることはほとんど頂点シェーダで完結します。 以下のシェーダではXZ平面でマッピングを行います。

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
    o.uv = TRANSFORM_TEX(worldPos.xz, _MainTex);
    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;
}


このシェーダでマテリアル作成し、テキトーにオブジェクトに貼り付けたものが次のgifになります。

見て分かる通り、ワールド座標系に貼り付けているのでオブジェクトを移動するとオブジェクトの模様も動きます。

f:id:vxd-naoshi-19961205-maro:20211010002138g:plain



しかし、一方向からのマッピングなので、投影方向と垂直でない面はテクスチャが伸びてしまいます。

f:id:vxd-naoshi-19961205-maro:20211010002349g:plain

f:id:vxd-naoshi-19961205-maro:20211010002950p:plain



Triplanar

この単語の日本語訳が見つからなかったので、私なりの訳は「三方向からの平面マッピング」です。

先ほどの平面マッピングでは一方向からのみでしたが、Triplanarでは三方向からマッピングを行うため、テクスチャが伸びてしまう現象を抑えることができます。

まずは単純に三方向からマッピングをするシェーダになります。

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
    o.worldPos = worldPos.xyz;
    o.normal = abs(UnityObjectToWorldNormal(v.normal));
    o.normal = o.normal / (o.normal.x + o.normal.y + o.normal.z);
    
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    float2 uv_front = TRANSFORM_TEX(i.worldPos.xy, _MainTex);
    float2 uv_side = TRANSFORM_TEX(i.worldPos.zy, _MainTex);
    float2 uv_top = TRANSFORM_TEX(i.worldPos.xz, _MainTex);
    // apply fog
    fixed4 col_front = tex2D(_MainTex, uv_front) * i.normal.z;
    fixed4 col_side = tex2D(_MainTex, uv_side) * i.normal.x;
    fixed4 col_top = tex2D(_MainTex, uv_top) * i.normal.y;
    fixed4 col = col_front + col_side + col_top;
    return col;
}



このシェーダをアタッチしたものがこちらになります。

三方向からテクスチャがブレンドされていることがわかります。


f:id:vxd-naoshi-19961205-maro:20211010004251g:plain



このままですと、見栄えが良くないので法線ベクトルをもとにどの方向からのテクスチャを描画するか判別します。

また、_Sharpness 変数を用意して、pow(normals, _Sharpness) を計算することで小さい値はより小さくなり、一番面が向いている方向に対してのマッピングが行われるようになります。

o.normal = abs(UnityObjectToWorldNormal(v.normal));
o.normal = pow(o.normal, _Sharpness);
o.normal = o.normal / (o.normal.x + o.normal.y + o.normal.z);


この計算を追加したシェーダをアタッチしたものがこちらになります。

テクスチャが重なっている個所が少なくなり、またマッピングされる方向の境目がある程度目立たなくなります。

f:id:vxd-naoshi-19961205-maro:20211010004541g:plain



最後に

平面マッピング頂点座標でテクスチャを貼るだけとても簡単にできるとわかりました。

今回は平面マッピングのみをまとめましたが、これを使うことでテクスチャが伸びてしまう現象などの解決策になることがわかりました。


次回はマトリックスのテクスチャと組み合わせたものについてまとめようと思います。



参考

英語の記事になりますが、とても丁寧でわかりやすかったです。

www.ronja-tutorials.com

www.ronja-tutorials.com


こちらは日本語のTriplanarの記事になります。

qiita.com