mcpField
W
water-fluid-shaders

water-fluid-shaders

github.com/adevra開発1648 回インストール31 週間前に更新
リポジトリ

water-fluid-shaders について

Unity の水や流体シェーダーの作成、変更、最適化を支援するスキルです。モバイルや WebGL の制約を重視し、テンプレートや機能別コスト、ノード構成を案内します。

主な機能

  1. 01モバイル向け性能制約
  2. 02URP HLSL のテンプレート
  3. 03Gerstner 波のガイド
  4. 04Shader Graph のノード設定
  5. 05水表現のコスト段階

活用シーン

  1. 01モバイル向け水シェーダーの作成
  2. 02岸辺の泡と深度色の追加
  3. 03頂点による波の動きの作成

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/adevra/unity-shader-agent-skills --skill water-fluid-shaders

インストール後、Claude Code で/water-fluid-shadersを入力して読み込まれたか確認します。

詳細

モバイル Unity 向け水・流体シェーダー

TRIGGER

ユーザーが Unity で水、海、川、湖、池、流体、溶岩、液体のシェーダーを作成、変更、最適化したい場合に、このスキルを使用します。特にモバイルや WebGL 向けの場合に適用します。トリガーには、「水シェーダー」、「海」、「波」、「泡」、「海岸線」、「屈折」、「コースティクス」、「Gerstner」、「流体」、「深度カラーリング」、「スタイライズド水」、「トゥーン水」や、水に関連するビジュアルエフェクトへの言及が含まれます。また、NVJOB、bmjoy、Alexander Ameye の水シェーダーリポジトリへの言及があった場合も適用します。


コア規則 — モバイル向け水の制約

  1. モバイルではテッセレーションを使用しません。 モバイル GPU はハードウェアテッセレーションを効率的にサポートしていません。代わりに、既存メッシュ上で頂点変位を使用します。

  2. 水シェーダーのテクスチャサンプルは 4 回以下にします。 モバイル向け水シェーダーの標準的な予算は、法線マップ 1 枚(タイリング、スクロール)、深度テクスチャ 1 枚(海岸線/泡用)、シーンカラー 1 枚(屈折用)、泡/ディテールテクスチャ 1 枚です。予算を超える場合は、まずシーンカラーによる屈折を削除します。

  3. 深度テクスチャと不透明テクスチャを URP Asset の設定で有効にする必要があります。 深度ベースの海岸線検出と屈折に必要です。これらを有効にするとコピー用パスが追加されるため、モバイルでは約 1ms の予算を確保します。コストが高すぎる場合は、代わりに頂点カラーによる深度表現を使用します。

  4. 法線は単一レイヤーにします。 デスクトップ向けの水では、異なるスケールの法線マップサンプルを 2~3 枚ブレンドすることがよくあります。モバイルでは、アニメーション用の UV スクロールとともに、法線マップのサンプルを 1 回だけ使用します。

  5. Gerstner 波は頂点シェーダーでのみ計算します。 波による変位をフラグメントごとに計算しないでください。モバイルでは波のオクターブ数を 2~3 に制限し、デスクトップでは 4 以上にします。

  6. ワールド位置と UV 座標を除き、すべての水の計算に半精度を使用します。


モバイル向け水シェーダー — 最小テンプレート(URP HLSL)

このテンプレートは、製品レベルの水シェーダーを作成するための出発点です。

Shader "Mobile/Water_Simple"
{
    Properties
    {
        _ShallowColor ("Shallow Color", Color) = (0.3, 0.8, 0.9, 0.6)
        _DeepColor ("Deep Color", Color) = (0.1, 0.2, 0.5, 0.9)
        _DepthMaxDistance ("Depth Max Distance", Float) = 3.0
        _NormalMap ("Normal Map", 2D) = "bump" {}
        _NormalScale ("Normal Scale", Float) = 0.5
        _WaveSpeed ("Wave Speed", Float) = 0.05
        _FoamColor ("Foam Color", Color) = (1, 1, 1, 1)
        _FoamDistance ("Foam Distance", Float) = 0.4
        _FoamCutoff ("Foam Cutoff", Float) = 0.5
    }
    
    SubShader
    {
        Tags {
            "RenderType" = "Transparent"
            "Queue" = "Transparent"
            "RenderPipeline" = "UniversalPipeline"
        }
        
        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
            
            CBUFFER_START(UnityPerMaterial)
                half4 _ShallowColor;
                half4 _DeepColor;
                half _DepthMaxDistance;
                float4 _NormalMap_ST;
                half _NormalScale;
                half _WaveSpeed;
                half4 _FoamColor;
                half _FoamDistance;
                half _FoamCutoff;
            CBUFFER_END
            
            TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap);
            
            struct Attributes
            {
                float4 positionOS : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 screenPos : TEXCOORD1;
                float3 positionWS : TEXCOORD2;
            };
            
            Varyings vert(Attributes v)
            {
                Varyings o;
                VertexPositionInputs posInputs = GetVertexPositionInputs(v.positionOS.xyz);
                o.positionCS = posInputs.positionCS;
                o.positionWS = posInputs.positionWS;
                o.screenPos = ComputeScreenPos(o.positionCS);
                o.uv = TRANSFORM_TEX(v.uv, _NormalMap);
                return o;
            }
            
            half4 frag(Varyings i) : SV_Target
            {
                // --- Depth-based coloring ---
                float2 screenUV = i.screenPos.xy / i.screenPos.w;
                float rawDepth = SampleSceneDepth(screenUV);
                float sceneEyeDepth = LinearEyeDepth(rawDepth, _ZBufferParams);
                float surfaceEyeDepth = i.screenPos.w;
                half depthDiff = saturate((sceneEyeDepth - surfaceEyeDepth) / _DepthMaxDistance);
                
                half4 waterColor = lerp(_ShallowColor, _DeepColor, depthDiff);
                
                // --- Animated normal map (single sample) ---
                float2 scrollUV = i.uv + _Time.y * _WaveSpeed;
                half3 normal = UnpackNormalScale(
                    SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, scrollUV),
                    _NormalScale
                );
                
                // --- Shoreline foam ---
                half foamDepth = saturate((sceneEyeDepth - surfaceEyeDepth) / _FoamDistance);
                half foam = step(foamDepth, _FoamCutoff);
                waterColor = lerp(waterColor, _FoamColor, foam * _FoamColor.a);
                
                // --- Simple fresnel ---
                half3 viewDir = normalize(_WorldSpaceCameraPos - i.positionWS);
                half fresnel = pow(1.0h - saturate(dot(half3(0, 1, 0), viewDir)), 3.0h);
                waterColor.a = lerp(waterColor.a, 1.0h, fresnel);
                
                return waterColor;
            }
            ENDHLSL
        }
    }
    
    Fallback "Universal Render Pipeline/Unlit"
}

Gerstner 波の実装

Gerstner 波は、リアルなトロコイド波の動きを生み出します。頂点シェーダー内でのみ計算します。

// Add to vertex shader — call for each wave octave
float3 GerstnerWave(float3 worldPos, float2 direction, float steepness, 
                     float wavelength, float speed)
{
    float k = 2.0 * PI / wavelength;
    float c = sqrt(9.8 / k); // Phase speed from dispersion relation
    float2 d = normalize(direction);
    float f = k * (dot(d, worldPos.xz) - c * speed * _Time.y);
    float a = steepness / k; // Amplitude
    
    return float3(
        d.x * a * cos(f),
        a * sin(f),
        d.y * a * cos(f)
    );
}

// In vertex shader:
Varyings vert(Attributes v)
{
    float3 worldPos = TransformObjectToWorld(v.positionOS.xyz);
    
    // 2-3 wave octaves for mobile (vary direction, wavelength, steepness)
    float3 wave1 = GerstnerWave(worldPos, float2(1, 0.5), 0.25, 8.0, 1.0);
    float3 wave2 = GerstnerWave(worldPos, float2(0.3, 1), 0.15, 4.0, 1.2);
    
    worldPos += wave1 + wave2;
    
    // Recalculate normals from wave tangent/binormal
    // (simplified — use cross product of partial derivatives)
    
    Varyings o;
    o.positionCS = TransformWorldToHClip(worldPos);
    // ... rest of vertex setup
    return o;
}

水の機能コスト別ティア

モバイル向けの予算に収まる機能を選ぶ際に使用します。

ティア 1:超低コスト(ローエンドモバイル、約 0.5ms)
  • 頂点カラーまたは UV ベースの深度カラーリング(深度テクスチャなし)
  • スクロールする法線マップ 1 枚
  • 泡なし、屈折なし
  • 不透明レンダリング(アルファブレンドなし)
ティア 2:標準的なモバイル向け(約 1~2ms)
  • 深度テクスチャによる海岸線カラーリング
  • 深度しきい値による海岸線の泡
  • スクロールする法線マップ 1 枚
  • フレネルベースのアルファ
  • Gerstner 波のオクターブ数 1~2
ティア 3:高品質なモバイル向け(約 2~4ms)
  • 屈折用の深度テクスチャと不透明テクスチャ
  • 2 枚の法線マップのブレンド
  • Gerstner 波(3 オクターブ)
  • アニメーションする泡テクスチャ
  • メインライトによるスペキュラハイライト
  • コースティクス(投影テクスチャまたはシェーダー生成)
ティア 4:デスクトップ専用
  • 平面反射または SSR
  • テッセレーション
  • FFT ベースの海洋シミュレーション
  • サブサーフェススキャタリング
  • Gerstner 波のオクターブ数 4 以上

Shader Graph の水 — ノード構成ガイド

Shader Graph を使用する場合、モバイル向けの効率的なノード構成は次のとおりです。

  1. 深度フェード:Scene Depth ノード → Fragment Eye Depth を減算 → 最大距離で除算 → 飽和 → 浅瀬色と深水色の間を補間

  2. 法線アニメーション:UV ノード → 加算(Time × 速度ベクトル) → Sample Texture 2D(法線マップ、Normal に設定) → Normal Strength ノード

  3. 海岸線の泡:同じ深度計算をより短い距離で行う → Step ノード → 泡の色を乗算 → 水の色に加算

  4. フレネル:Fresnel Effect ノード(べき乗値は約 3) → ベース値と 1.0 の間でアルファを補間

  5. 頂点変位(波):Position ノード(Object Space) → 正弦/余弦を使ったカスタムサブグラフ → Position 出力に加算

ノードの合計数:モバイル向けの水シェーダーは 60 ノード未満に抑えます。Sub Graph を使って整理してください。オーバーヘッドは増えませんが、グラフを読みやすく保てます。


参考リポジトリ — コピーして学習

参考にするのに最適なオープンソースの水シェーダーは次のとおりです。

リポジトリモバイル対応?機能ライセンス
nvjob/nvjob-water-shader-simple-and-fastはい法線マッピング、視差、テッセレーションなしMIT
nvjob/nvjob-water-shaders-v2はいV1 + 反射 + 岸辺の泡MIT
bmjoy/Stylized-Water-Shader-Unity-URPはいURP、深度カラー、泡、屈折—
N7RX/Simple-Water-Shaderはい軽量でモバイルを優先—
danielshervheim/unity-stylized-water一部対応マテリアルプリセットを備えたスタイライズド表現(約953★)—
MatrixRex/Uber-Stylized-Water一部対応Unity 6、高いカスタマイズ性—
tuxalin/water-shaderいいえマルチプラットフォーム、ガースナー波、コースティクス—

参考資料

コメント