Shader 파트 48을 시작해서 조금 더 풍성한 블루밍 스타일의 웨이브를 만들려고 합니다.

63145 단어 Unityshadertech

쉐더를 할 시간이 얼마 안 남았어요.


하마터면 Shader를 할 뻔했다.쉐더를 할 시간이 많지 않을 것 같아서요.
100부까지 열심히 하겠습니다.10년이 걸려도 돼요.
100편의 글을 배웠다면 초보자라도 이해할 수 있을 것 같아요.
이렇게 하는 거야.
※ 초보자는 필기 레벨로 기록
기술 보도로는 도움이 안 될 것 같습니다.

미리 준비하다


아래 참조
Shader 파트 1Unite 2017 애니메이션을 볼 때가 많지 않아요. (기초지식~종이 스크레이퍼로 색깔 바꾸기)

데모


이렇게 됐어.

시위 행진의 요소는 다음과 같다.
• 물결 같은 하얀 거품 만들기
• 파도를 하얗게 하다
• 수면 아래로 비틀기
과거 쓴 기사 내용을 조합한 내용이다.
[연결된 과거 글]
Shader 사운드 43 셀카 소음으로 도문 스타일의 파도를 만들 뻔 했어요.
Shader의 44번째 부분을 깊은 무늬로 웨이브를 칠 때의 표현을 할 때가 많지 않아요.
Shader 파트 할 게 별로 없어요. 47 수면이 비뚤어져요.

Shader 샘플


Shader "Custom/DistortionWave"
{
    Properties
    {
        _SquareNum ("SquareNum", int) = 5
        _DistortionPower("Distortion Power", Range(0, 0.1)) = 0
        [HDR]_WaterColor("WaterColor", Color) = (0,0,0,0)
        _DepthFactor("Depth Factor", Range(0, 10)) = 1.0
        _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)
    }

    SubShader
    {
        Tags
        {
            "Queue" = "Transparent" "RenderType" = "Transparent"
        }

        //不当明度を利用するときに必要 文字通り、1 - フラグメントシェーダーのAlpha値 という意味
        Blend SrcAlpha OneMinusSrcAlpha

        //描画結果をテクスチャーとして取得可能に
        GrabPass
        {
            //ここで定義した名前で取得可能になる
            "_GrabPassTextureForDistortionWave"
        }

        //揺らぎの表現を頑張る 描画結果を利用する
        Pass
        {

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 grabPos : TEXCOORD1;
                float4 scrPos : TEXCOORD2;
            };

            sampler2D _CameraDepthTexture;
            sampler2D _GrabPassTextureForDistortionWave;
            float _DistortionPower;

            v2f vert(appdata v)
            {
                v2f o = (v2f)0;

                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.grabPos = ComputeGrabScreenPos(o.vertex);
                //ComputeScreenPosによってxyが0〜wに変換される
                o.scrPos = ComputeScreenPos(o.vertex);

                return o;
            }

           fixed4 frag(v2f i) : SV_Target
            {
                //サンプリングするUVをずらす sin波でゆらゆら
                float2 distortion = sin(i.uv.y * 50 + _Time.w) * 0.1f;
                distortion *= _DistortionPower;
                float4 depthUV = i.grabPos;
                //サンプリング用のUVによる歪みは少し大きくしておく
                //https://catlikecoding.com/unity/tutorials/flow/looking-through-water/
                depthUV.xy = i.grabPos.xy + distortion * 1.5f;
                //深度テクスチャをサンプリング
                float4 depthSample = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(depthUV));
                //既に描画済みのピクセルの深度情報
                float backgroundDepth = LinearEyeDepth(depthSample);
                //今描画しようとしているピクセルの深度情報
                float surfaceDepth = UNITY_Z_0_FAR_FROM_CLIPSPACE(i.scrPos.z);
                //Depthの差を利用した補間値
                float depthDiff = saturate(backgroundDepth - surfaceDepth);

                //w除算 普段はGPUが勝手にやってくれる
                //補間値を利用してUVをずらして良いピクセルとそのままにするピクセルを塗り分け
                float2 uv = (i.grabPos.xy + distortion * depthDiff) / i.grabPos.w;

                return tex2D(_GrabPassTextureForDistortionWave, uv);
            }
            ENDCG
        }
        
        //泡の表現を頑張る
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"


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

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

            float4 _WaterColor;
            int _SquareNum;
            float _WaveSpeed;
            float _FoamPower;
            float4 _FoamColor;
            float4 _EdgeColor;
            float _DepthFactor;
            sampler2D _CameraDepthTexture;

            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);
            }

            v2f vert(appdata v)
            {
                v2f o = (v2f)0;

                o.vertex = UnityObjectToClipPos(v.vertex);
                //ComputeScreenPosによってxyが0〜wに変換される
                o.scrPos = 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.scrPos));
                float screenDepth = LinearEyeDepth(depthSample) - i.scrPos.w;
                float edge = 1 - saturate(_DepthFactor * screenDepth);
                float4 color = lerp(waveColor, _EdgeColor, edge);
                return color;
            }
            ENDCG
        }
    }
}
처리 프로세스 자체는 저번 보도와 같고 아래와 같다.
①GrabPass를 통해 드로잉 결과 얻기
② 도면 결과를 이용하여 깊이 정보에 따라 수면 아래의 대상을 비뚤어진다
③ 수면을 그리며 파도의 파동과 파도의 충격을 계산할 때의 색

물리적 파동


나는 과거에 쓴 바람에 펄럭이는 깃발 같은 표현 Shader의 응용에 물리적으로 울퉁불퉁한 파의 표현을 더하고 싶다.

데모


옆으로 보면 이해하기 쉬운데 이렇게 Pleane이 파도처럼 위아래로 움직여요.

Shader 샘플


Shader "Custom/DistortionWave"
{
    Properties
    {
        _SquareNum ("SquareNum", int) = 5
        _DistortionPower("Distortion Power", Range(0, 0.1)) = 0
        [HDR]_WaterColor("WaterColor", Color) = (0,0,0,0)
        _DepthFactor("Depth Factor", Range(0, 10)) = 1.0
        _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)
        _Frequency("Frequency ", Range(0, 3)) = 1
        _Amplitude("Amplitude", Range(0, 1)) = 0.5
    }

    SubShader
    {
        Tags
        {
            "Queue" = "Transparent" "RenderType" = "Transparent"
        }

        //不当明度を利用するときに必要 文字通り、1 - フラグメントシェーダーのAlpha値 という意味
        Blend SrcAlpha OneMinusSrcAlpha

        //描画結果をテクスチャーとして取得可能に
        GrabPass
        {
            //ここで定義した名前で取得可能になる
            "_GrabPassTextureForDistortionWave"
        }
        
        //パスを跨いで利用できる変数や関数
        CGINCLUDE
        
        float _WaveSpeed;
        float _Amplitude;
        float _Frequency;

        #pragma vertex vert
        #pragma fragment frag
        
        #include "UnityCG.cginc"
        
        float vertex_wave(float2 vert,float waveSpeed,float amplitude,float frequency)
        {
             float2 factors = _Time.x * waveSpeed + vert * frequency;
             float2 offsetYFactors = sin(factors) * amplitude;
             return  offsetYFactors.x + offsetYFactors.y;
        }
        ENDCG

        //揺らぎの表現を頑張る 描画結果を利用する
        Pass
        {

            CGPROGRAM

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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float4 grabPos : TEXCOORD1;
                float4 scrPos : TEXCOORD2;
            };

            sampler2D _CameraDepthTexture;
            sampler2D _GrabPassTextureForDistortionWave;
            float _DistortionPower;
            
            v2f vert(appdata v)
            {
                v2f o = (v2f)0;
                
                v.vertex.y += vertex_wave(v.vertex.xz,_WaveSpeed,_Amplitude,_Frequency);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.grabPos = ComputeGrabScreenPos(o.vertex);
                //ComputeScreenPosによってxyが0〜wに変換される
                o.scrPos = ComputeScreenPos(o.vertex);

                return o;
            }

           fixed4 frag(v2f i) : SV_Target
            {
                //サンプリングするUVをずらす sin波でゆらゆら
                float2 distortion = sin(i.uv.y * 50 + _Time.w) * 0.1f;
                distortion *= _DistortionPower;
                float4 depthUV = i.grabPos;
                //サンプリング用のUVによる歪みは少し大きくしておく
                //https://catlikecoding.com/unity/tutorials/flow/looking-through-water/
                depthUV.xy = i.grabPos.xy + distortion * 1.5f;
                //深度テクスチャをサンプリング
                float4 depthSample = SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(depthUV));
                //既に描画済みのピクセルの深度情報
                float backgroundDepth = LinearEyeDepth(depthSample);
                //今描画しようとしているピクセルの深度情報
                float surfaceDepth = UNITY_Z_0_FAR_FROM_CLIPSPACE(i.scrPos.z);
                //Depthの差を利用した補間値
                float depthDiff = saturate(backgroundDepth - surfaceDepth);

                //w除算 普段はGPUが勝手にやってくれる
                //補間値を利用してUVをずらして良いピクセルとそのままにするピクセルを塗り分け
                float2 uv = (i.grabPos.xy + distortion * depthDiff) / i.grabPos.w;

                return tex2D(_GrabPassTextureForDistortionWave, uv);
            }
            ENDCG
        }
        
        //泡の表現を頑張る
        Pass
        {
            CGPROGRAM

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

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

            float4 _WaterColor;
            int _SquareNum;
            float _FoamPower;
            float4 _FoamColor;
            float4 _EdgeColor;
            float _DepthFactor;
            sampler2D _CameraDepthTexture;

            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);
            }

            v2f vert(appdata v)
            {
                v2f o = (v2f)0;

                v.vertex.y += vertex_wave(v.vertex.xz,_WaveSpeed,_Amplitude,_Frequency);;
                o.vertex = UnityObjectToClipPos(v.vertex);
                 //ComputeScreenPosによってxyが0〜wに変換される
                o.scrPos = 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.scrPos));
                //スクリーンに描画されるピクセルの深度情報
                float screenDepth = LinearEyeDepth(depthSample) - i.scrPos.w;
                float edge = 1 - saturate(_DepthFactor * screenDepth);
                float4 color = lerp(waveColor, _EdgeColor, edge);
                return color;
            }
            ENDCG
        }
    }
}
정점 Shader에 파도처럼 위아래로 흔들리는 처리가 기술되어 있다
두 경로 모두 같은 처리를 설명해야 하기 때문이다
솔직하게 쓰면 중복되는 코드가 늘어난다.
그래서 사용했다CGINCLUDEブロック.CGINCLUDEブロック 변수와 함수를 여러 경로 간에 공유할 수 있습니다.
//パスを跨いで利用できる変数や関数
CGINCLUDE

float _WaveSpeed;
float _Amplitude;
float _Frequency;

#pragma vertex vert
#pragma fragment frag

#include "UnityCG.cginc"

float vertex_wave(float2 vert,float waveSpeed,float amplitude,float frequency)
{
     float2 factors = _Time.x * waveSpeed + vert * frequency;
     float2 offsetYFactors = sin(factors) * amplitude;
     return  offsetYFactors.x + offsetYFactors.y;
}
ENDCG

참조 링크


[Unity][음영] CGINCLUDE 블록의 사용 방법

좋은 웹페이지 즐겨찾기