🌈

そろそろShaderをやるパート44 深度テクスチャで波打ち際の表現

2021/10/04に公開

そろそろShaderをやります

そろそろShaderをやります。そろそろShaderをやりたいからです。
パート100までダラダラ頑張ります。10年かかってもいいのでやります。
100記事分くらい学べば私レベルの初心者でもまあまあ理解できるかなと思っています。

という感じでやってます。

※初心者がメモレベルで記録するので
 技術記事としてはお力になれないかもしれません。

下準備

下記参考
そろそろShaderをやるパート1 Unite 2017の動画を見る(基礎知識~フラグメントシェーダーで色を変える)

デモ

前回のトゥーン調の水面Shaderをパワーアップさせてみました。
【参考リンク】:そろそろShaderをやるパート43 セルラーノイズでトゥーン調の波を作ってみる

水面とオブジェクトの境界線が白く、波打ち際のような表現となっています。

もう少し分かり易いものが下記です。
境界面が白くなるようなShaderです。

Shaderサンプル

Shader "Custom/ToonWave"
{
   Properties
    {
        _SquareNum ("SquareNum", int) = 5
        [HDR]_WaterColor("WaterColor", Color) = (0.09, 0.89, 1, 1)
        _WaveSpeed("WaveSpeed", Range(1,10)) = 1
        _FoamPower("FoamPower", Range(0,1)) = 0.6
        _FoamColor("FoamColor", Color) = (1, 1, 1, 1)
        _EdgeColor("EdgeColor", Color) = (1, 1, 1, 1)
        _DepthFactor("Depth Factor", float) = 1.0
    }
    SubShader
    {
        Tags { "RenderType"="Transparent" "Queue" = "Transparent" }
        Blend SrcAlpha OneMinusSrcAlpha

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 screenPos : TEXCOORD1;
            };

            float2 random2(float2 st)
            {
                st = float2(dot(st, float2(127.1, 311.7)),
                            dot(st, float2(269.5, 183.3)));
                return -1.0 + 2.0 * frac(sin(st) * 43758.5453123);
            }

            uniform sampler2D _CameraDepthTexture;
            int _SquareNum;
            fixed4 _WaterColor;
            fixed4 _FoamColor; 
            fixed4 _EdgeColor;
            float _WaveSpeed;
            float _FoamPower;
            float _DepthFactor;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.screenPos = ComputeScreenPos(o.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float2 st = i.uv;
                st *= _SquareNum; //格子状のマス目作成 UVにかけた数分だけ同じUVが繰り返し展開される

                float2 ist = floor(st); //各マス目の起点
                float2 fst = frac(st); //各マス目の起点からの描画したい位置

                float4 waveColor = 0;
                float m_dist = 100;

                //自身含む周囲のマスを探索
                for (int y = -1; y <= 1; y++)
                {
                    for (int x = -1; x <= 1; x++)
                    {
                        //周辺1×1のエリア
                        float2 neighbor = float2(x, y);

                        //点のxy座標
                        float2 p =  0.5 + 0.5 * sin(random2(ist+neighbor) +_Time.x *_WaveSpeed);

                        //点と処理対象のピクセルとの距離ベクトル
                        float2 diff = neighbor + p - fst;

                        m_dist = min(m_dist, length(diff));

                        waveColor =  lerp(_WaterColor,_FoamColor,smoothstep(1-_FoamPower,1,m_dist));
                    }
                }
                
                //深度の計算
                float4 depthSample = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos));
                half depth = LinearEyeDepth(depthSample);
                half screenDepth = depth - i.screenPos.w;
                float edgeLine = 1 - saturate(_DepthFactor * screenDepth);
                fixed4 finalColor = lerp(waveColor, _EdgeColor, edgeLine);
                
                return finalColor;
            }
            ENDCG
        }
    }
}

下記記事を参考にカメラから深度テクスチャを生成するコードを追加します。
【参考リンク】:【Unity】Shaderでオブジェクトの交差位置に色をつける

using UnityEngine;

[ExecuteInEditMode]
public class DepthTexture : MonoBehaviour 
{
  private Camera cam;

  void Start () 
  {
    cam = GetComponent<Camera>();
    cam.depthTextureMode = DepthTextureMode.Depth;
  }
}

このコードにより、Shader内でグローバルシェーダープロパティーとして定義された_CameraDepthTextureに深度テクスチャが代入されます。
LinearEyeDepthにサンプリングした深度テクスチャを渡せば深度が取得可能です。

より詳しい解説は下記がわかりやすかったです。
【参考リンク】:Unity のソフトパーティクルのシェーダについて調べてみた

参考リンク

【Unity】【シェーダ】スクリーンに対してテクスチャをマッピングする方法を完全解説する
ソフトパーティクルの仕組みを応用した表現

Discussion