うにてぃブログ

UnityやUnreal Engineの記事を書いていきます

【Unity】1サンプル Stochastic Tri-Planarマッピング

この記事では、Stochastic Tri-Planar Mapping(確率的トライプラナーマッピングの仕組みについて解説し、作成したShaderGraphを共有します。

Tri-Planar Mappingとは?

通常のテクスチャマッピングでは、メッシュに対して1つのUVセットを使用しますが、Tri-Planar Mappingは、以下の方法でテクスチャを適用します:

  • ワールド座標を利用して、
  • X軸 / Y軸 / Z軸のそれぞれの方向からテクスチャを投影し、
  • 法線の向きに応じて投影をブレンドする

この手法により、UV展開なしで自然なテクスチャ貼りが実現できます。

❓ なぜ"Stochastic"が必要なのか?

通常のTri-Planar Mappingでは、3軸をブレンドするため、計算負荷が高くなる傾向があります。しかし、遠くにあるオブジェクトは細部までの精度が求められないため、ある程度簡素な表示でも違和感なく見えます。

そこで登場するのが、
🎲 1サンプル Stochastic Tri-Planar Mapping(確率的トライプラナーです!

この手法では:

  • ピクセルごとに乱数を使って、X軸、Y軸、またはZ軸のいずれか1軸を選んでサンプリングします。

要するに、

「軽量で処理が速いけれど、近くで見ると若干のチラつきが見える」

という特徴があります!

🧱 Unity ShaderGraph用 実装コード

Shader - Shadertoy BETA を参考にUnity環境用に再構成したコードが以下です

//────────────────────────────────────────────
//  Stochastic Tri-Planar Sampling
//  Based on Shadertoy (https://www.shadertoy.com/view/3lS3Rm)
//────────────────────────────────────────────

inline float hash(float2 p)
{
    return frac(1.0e4 * sin(17.0 * p.x + 0.1 * p.y) * (0.1 + abs(sin(13.0 * p.y + p.x))));
}

inline float hash3D(float3 p)
{
    return hash(float2(hash(p.xy), p.z));
}

void StochasticTriPlanar_float
(
    UnityTexture2D Texture,
    UnitySamplerState Sampler,
    float3 PositionWS,
    float3 NormalWS,
    float TextureScale,
    out float4 Out
)
{
    float3 n = normalize(NormalWS);
    float3 nAbs = abs(n);

    float sqrt3_div3 = 0.57735026919;
    float3 a = max(nAbs - sqrt3_div3, 0.0);
    float3 w = a / max(dot(a, 1.0), 1e-5);

    float3 dx = ddx(n);
    float3 dy = ddy(n);
    float pixDeriv = length(float2(length(dx), length(dy)));
    float pixScale = rcp(pixDeriv + 1e-5);

    float h = hash3D(floor(n * pixScale));

    float2 uv, dudx, dudy;

    if (w.z > h)
    {
        uv   = PositionWS.xy;
        dudx = ddx(PositionWS.xy);
        dudy = ddy(PositionWS.xy);
    }
    else if ((w.z + w.y) > h)
    {
        uv   = PositionWS.xz;
        dudx = ddx(PositionWS.xz);
        dudy = ddy(PositionWS.xz);
    }
    else
    {
        uv   = PositionWS.zy;
        dudx = ddx(PositionWS.zy);
        dudy = ddy(PositionWS.zy);
    }

    uv *= TextureScale;
    dudx *= TextureScale;
    dudy *= TextureScale;

    Out = SAMPLE_TEXTURE2D_GRAD(Texture, Sampler, uv, dudx, dudy);
}

作成したShader GraphとHLSLコード

以下のリンクから、今回作成したStochastic Tri-Planar MappingのShader GraphとHLSLコードをご覧いただけます:

実際の結果

軸が定まっていない部分がジャギっているのが確認できるかと思います。以下の画像でその様子をご覧ください。

次に、UVを可視化した結果です。

遠くにオブジェクトを配置してみましたが、違和感が感じにくいことがわかると思います。
また、遠くにある場合はノーマルマップを使用しても変化に気づきづらく、したがってノーマルマップを利用したShaderは作成していません。