3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Unity URP】モンスターの咆哮エフェクトを作る!極座標(atan2)の切れ目解消と3D座標追従の数学

3
Posted at

アクションゲームでモンスターが咆哮した際や大爆発が起きた際、「ボスの口元を中心に空間が集中線状に歪む演出」を入れると画面の迫力が一気に跳ね上がります。

しかし、いざシェーダー(HLSL)で中心からの角度(atan2)を使って放射状のノイズを作ろうとすると、以下の3つの罠にハマりがちです。

  1. 画面比率(16:9など)の影響で、正円ではなく横長の楕円に歪んでしまう
  2. atan2 の戻り値($-\pi \sim \pi$)の境界である「左水平方向(180度)」にパキッとしたノイズの切れ目(シーム)が出る
  3. 3D空間のボスの位置と、シェーダー内の2DスクリーンUV座標が一致しない

本記事では、これら3つの課題を数学的に解決し、破綻のない「集中線ストリーク歪み」を実装するコアテクニックを解説します。


全体の処理フロー

C#側から渡された3Dターゲット座標をスクリーンUVに変換し、フラグメントシェーダー内で極座標ノイズを計算するまでの流れは以下の通りです。


1. アスペクト比補正と「中心クリアマスク」の計算

フルスクリーンシェーダーのUV座標は画面サイズに関わらず 0.0 〜 1.0 に正規化されています。そのまま距離(length)を計算すると、16:9の画面では横方向に引き伸ばされた楕円になってしまいます。

_ScreenParams.x / _ScreenParams.y をX成分に掛けて補正し、さらに「ボスの顔(中心)はクッキリ見せ、外周だけを歪ませる」ためのドーナツ状マスクを作ります。

// 発生中心(_RoarCenter.xy)から現在のピクセル(uv)へのベクトル
float2 dir = uv - _RoarCenter.xy;

// アスペクト比補正(16:9等の画面でも正円の距離場を作る)
float aspect = _ScreenParams.x / max(_ScreenParams.y, 1.0);
float2 dirAspect = float2(dir.x * aspect, dir.y);
float dist = length(dirAspect);

// 中心(_CenterClearRadius)は0、外周に向かって1になる滑らかなマスク
float radialMask = smoothstep(_CenterClearRadius, _CenterClearRadius + _MaskSmoothness, dist);
float activeEffect = _Intensity * radialMask;

2. atan2 の境界シーム(左端の切れ目)を完全に消す数式

放射状の集中線を作るには atan2(y, x) で角度を求め、それを1次元ノイズ関数(Noise1D)に入力します。
しかし、atan2 の値域は $-\pi \sim \pi$ です。そのまま Noise1D(angle * _StreakFrequency) のように書くと、左端($-\pi$ と $\pi$ が接する場所)で位相が不連続になり、くっきりと境界線が見えてしまいます。

これを防ぐには、①角度を 0.0 〜 1.0 に正規化した上で、②掛ける周波数(Frequency)を floor で整数に丸めるのが鉄則です。

// 1次元バリューノイズ関数
float Hash(float co)
{
    float3 p3 = frac(float3(co, co, co) * 0.1031);
    p3 += dot(p3, p3.yzx + 33.33);
    return frac((p3.x + p3.y) * p3.z);
}

float Noise1D(float x)
{
    float i = floor(x);
    float f = frac(x);
    f = f * f * (3.0 - 2.0 * f); // エルミート補間(Smoothstep)
    return lerp(Hash(i), Hash(i + 1.0), f);
}

// --- フラグメントシェーダー内 ---
// 1. atan2(-PI〜PI) を 0.0〜1.0 の正規化角度に変換
float normalizedAngle = (atan2(dirAspect.y, dirAspect.x) + PI) / TWO_PI;

// 2. 周波数を整数に丸めることで、0.0と1.0の境界での位相ズレを抑える
float freq = floor(_StreakFrequency);
float t = _Time.y * _StreakSpeed;

// 異なる周期・逆方向の2つのノイズを合成して単調さを消す
float streakNoise  = Noise1D(normalizedAngle * freq + t) * 2.0 - 1.0;
float streakNoise2 = Noise1D(normalizedAngle * (freq * 0.5) - t * 1.3) * 2.0 - 1.0;
float combinedStreak = (streakNoise * 0.6 + streakNoise2 * 0.4);

さらに、求めたノイズ値を使ってUVをズラす際、中心からの「法線方向(外側へ向かうベクトル)」だけでなく、直交する「接線方向(回転方向のベクトル)」を 0.5 ほど混ぜることで、空気の渦がうねるようなリアルな歪みになります。

// ゼロ除算防止の微小値(1e-5)を足して正規化
float2 normDir = normalize(dir + 1e-5);
// 法線に直交する接線ベクトル (-y, x)
float2 tangentDir = float2(-normDir.y, normDir.x);

// 放射方向+回転方向にUVオフセットを合成
float2 streakOffset = (normDir + tangentDir * 0.5) * (combinedStreak * _StreakStrength * activeEffect);

3. C#側での3Dワールド座標 → スクリーンUV自動変換

ボスの口元(Transform)を中心にエフェクトを発生させるには、Camera.WorldToViewportPoint を使います。Viewport座標は左下 (0, 0) 〜 右上 (1, 1) なので、そのままシェーダーのUV座標として渡せます。

ただし、ターゲットがカメラの背後(z < 0)に回り込んだ際に座標が反転する問題があるため、必ず viewportPos.z の符号チェックを入れます。

private static readonly int PropRoarCenter = Shader.PropertyToID("_RoarCenter");

private void UpdateRoarCenter(Material mat, Camera cam, Transform target)
{
    Vector3 viewportPos = cam.WorldToViewportPoint(target.position);
    
    // カメラの背後にいる場合は画面中央(0.5, 0.5)へフォールバック
    Vector2 centerUV = (viewportPos.z < 0f) 
        ? new Vector2(0.5f, 0.5f) 
        : new Vector2(viewportPos.x, viewportPos.x); // ※yはviewportPos.y

    centerUV.y = (viewportPos.z < 0f) ? 0.5f : viewportPos.y;
    mat.SetVector(PropRoarCenter, new Vector4(centerUV.x, centerUV.y, 0f, 0f));
}

さらに実践的な「咆哮・衝撃波システム」に仕上げるには?

本記事で紹介した「集中線ストリーク歪み」に、以下の要素を組み合わせると商用アクションゲームと同等の画面演出になります。

  • パルス振動付きラジアルブラー(放射状ブラー)との統合
  • 外周ほど強くRGBが分離するマルチサンプル色収差(Chromatic Aberration)
  • モバイルやSwitchでも負荷を落とさない #pragma multi_compile_local と [unroll] による静的ループ展開
  • GC Allocゼロで PlayRoar() 1行で減衰再生できるC#コントローラー&ワンクリックプリセット付きCustomEditor

📦 すぐ使える完成品パッケージ(BOOTH)

「自分のゲームに今すぐ30秒で導入したい!」という方向けに、シェーダー・3D座標追従スクリプト・専用インスペクター拡張(3種のプリセット&テスト再生ボタン付き)をまとめた完成品パッケージをBOOTHで公開しています。
👉 【URP】RoarFX - 咆哮・衝撃波ポストプロセスシェーダー(BOOTHリンク)

📘 全コードと最適化手法の完全解説本(Zenn Book)

また、ラジアルブラー・色収差の統合アルゴリズムから、ShaderGUI / CustomEditor の実装コード全文までをステップバイステップで解説した技術書もZennで公開中です。仕組みから深く学びたい方はぜひご覧ください!
👉 Unity URP 実践ポストプロセス開発:咆哮・衝撃波シェーダーとエディタ拡張の作り方(Zennリンク)

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?