Shader 파트 73 나비가 날개를 펼친다는 표현은 거의 없어요.

34317 단어 Unityshadertech

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


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

미리 준비하다


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

데모


나비가 날개를 치며 행진하다.
나비의 움직임은 쉐더, 움직이고 대량 생성되는 부분은 Particale이다.

Shader 샘플


Shader "Custom/Butterfly"
{
    Properties
    {
        [NoScaleOffset]_MainTex ("Texture", 2D) = "white" {}
        [HDR]_MainColor("MainColor",Color) = (1,1,1,1)
        _FlapSpeed ("Flap Speed", Range(0,20)) = 10
        _FlapIntensity ("Flap Intensity", Range(0,2)) = 1
        _MoveSpeed ("Move Speed", Range(0,5)) = 1
        _MoveIntensity ("Move Intensity", Range(0,1)) = 0.2
        _RandomFlap ("Random Flap", Range(1,2)) = 1
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Tansparent"
        }

        Pass
        {
            Cull off
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag


            #include "UnityCG.cginc"

            struct appdata
            {
                float2 uv : TEXCOORD0;
                //中心座標を受け取る変数
                float3 center : TEXCOORD1;
                //ランダムな値を受け取る変数
                float random : TEXCOORD2;
                //速度を受け取る変数
                float3 velocity : TEXCOORD3;
                float4 color : COLOR;
                float4 vertex : POSITION;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float4 color : COLOR;
            };

            sampler2D _MainTex;
            float4 _MainColor;
            float _FlapSpeed;
            float _FlapIntensity;
            float _MoveIntensity;
            float _MoveSpeed;
            float _RandomFlap;

            //ランダムな値を返す
            float rand(float2 co) //引数はシード値と呼ばれる 同じ値を渡せば同じものを返す
            {
                return frac(sin(dot(co.xy, float2(12.9898, 78.233))) * 43758.5453);
            }

            //非線形ブラウン運動を計算する
            float fbm(float x, float t)
            {
                return sin(x + t) + 0.5 * sin(2.0 * x + t) + 0.25 * sin(4.0 * x + t);
            }

            v2f vert(appdata v)
            {
                v2f o;

                //ローカル座標
                //Particle SystemのGameObjectが存在するところが原点となり、vertexにはこの原点から見た座標が入ってくる
                //そのため、パーティクルの中心座標を引いて計算を行い、もとに戻すという工程を踏む
                float3 local = v.vertex - v.center;

                //ランダムな値を計算
                float randomFlap = lerp(_FlapSpeed / _RandomFlap, _FlapSpeed, rand(v.random));
                float flap = (sin(_Time.w * randomFlap) + 0.5) * 0.5 * _FlapIntensity;
                //Sign(x)はxが0より大きい場合は1、小さい場合は-1を返す
                //これにより、x=0となる箇所から線対称に回転を計算できる
                half c = cos(flap * sign(local.x));
                half s = sin(flap * sign(local.x));
                /*       |cosΘ -sinΘ|
                  R(Θ) = |sinΘ  cosΘ|  2次元回転行列の公式*/
                half2x2 rotateMatrix = half2x2(c, -s, s, c);

                //羽の回転を反映
                local.xy = mul(rotateMatrix, local.xy);

                //進行方向を向かせるための回転行列を作成
                //正面は進行方向、すなわちParticleから取得したvelocity
                float3 forward = normalize(v.velocity);
                float3 up = float3(0, 1, 0);
                float3 right = normalize(cross(forward, up));

                //行列を作成
                //どうやら変数に詰めるときだけ行オーダーになっている?っぽい
                //なのでtransposeで転置を行う
                //すなわち以下でも可
                //float3x3 mat = float3x3(right.x,up.x,forward.x,
                //                        right.y,up.y,forward.y,
                //                        right.z,up.z,forward.z);
                float3x3 mat = transpose(float3x3(right, up, forward));

                //Velocity(正面方向)に応じた回転を反映
                v.vertex.xyz = mul(mat, local);

                //原点をもとの座標に戻す
                v.vertex.xyz += v.center;
                o.vertex = UnityObjectToClipPos(v.vertex);
                //上下の移動量を求めて反映 ワールド座標系で上下移動させる
                float move = fbm(87034 * v.random, _Time.w * _MoveSpeed) * _MoveIntensity;
                o.vertex.y += move;
                o.uv = v.uv;
                //頂点カラー
                o.color = v.color;
                return o;
            }

            float4 frag(v2f i) : SV_Target
            {
                //PlaneのZ軸が正の方向になるようにテクスチャーをサンプリング
                //テクスチャーをRepeatにしておく必要あり
                float4 col = tex2D(_MainTex, -i.uv);
                col.rgb *= _MainColor.rgb;
                //頂点カラーを適用 これでParticleの色を拾うようになる
                col *= i.color;
                //重なったところが透明に切り抜かれてしまうので透過領域をClipしておく
                clip(col.a - 0.01);
                return col;
            }
            ENDCG
        }
    }
}

Custom Vertex Streams에 사용되는 값


전제로 이 Shader는Partile SystemCustom Vertex Streams을 이용한다.
【참조 링크】: 셰더 하는 부분 46 Partical에서 셰더 가격을 줘야 될 것 같아요.
따라서 일반적인 플랜에도 다음과 같은 GIF의 동작이 있을 수 있다.

이 전제에서 Shader를 보면...
Partical System에서 3개의 매개변수를 수신했습니다.
struct appdata
{
    float2 uv : TEXCOORD0;
    //中心座標を受け取る変数
    float3 center : TEXCOORD1;
    //ランダムな値を受け取る変数
    float random : TEXCOORD2;
    //速度を受け取る変数
    float3 velocity : TEXCOORD3;
    float4 color : COLOR;
    float4 vertex : POSITION;
};
구체적으로 다음과 같다.
각 입자의 중심 좌표
・각 입자가 가지고 있는 랜덤 값
・각 입자의 속도를 나타내는 벡터
입자 측면의 설정은 다음과 같다.
원하는 값이 TEXCOORD에 들어갈 수 있도록 필요 없는 값을 넣어 순서를 조정합니다.
코드를 읽을 때 이해하기 위해서입니다.
필요한 값만 Custom Vertex Streams로 설정하는 것도 문제없다.

입자의 중심에 관하여


각 입자의 중심 좌표를 사용하여 계산하기 위해 다음과 같은 처리를 진행하였다.
float3 local = v.vertex - v.center;
~~
v.vertex.xyz += v.center;
그 이유로 Partical System의 GameObject가 존재하는 곳이 원점이 됩니다.
vertex에 이 원점에서 보이는 좌표가 있기 때문이다.
따라서 입자마다 정점 좌표
입자 중심에서 보이는 좌표로 다시 계산하다
원래 정점으로 돌아가 이런 공정을 밟았다.
이로써 후술한 各パーティクルにおける線対象な羽の動き 또는各パーティクルの正面方向に対する回転가 가능해졌다.

깃털의 운동


깃털의 운동은 꼭대기 면도기가 맡는다.
Pleane을 선 대상으로 위아래로 돌리면 깃털이 된다.
도해는 다음과 같다.

그림에서 X축의 좌표는 0 이상 또는 0 이하를 기준으로 선 객체로 나뉜다
이것은sign이라는 함수로 실현할 수 있다.
sign (x) x가 0보다 크면 1, 시간은 -1로 돌아갑니다.
float3 local = v.vertex - v.center;

~~

//Sign(x)はxが0より大きい場合は1、小さい場合は-1を返す
//これにより、x=0となる箇所から線対称に回転を計算できる
half c = cos(flap * sign(local.x));
half s = sin(flap * sign(local.x));
/*       |cosΘ -sinΘ|
  R(Θ) = |sinΘ  cosΘ|  2次元回転行列の公式*/
half2x2 rotateMatrix = half2x2(c, -s, s, c);

//羽の回転を反映
local.xy = mul(rotateMatrix, local.xy);

회전 정보


각 입자, 즉 나비를 회전시킬 때 허점이 없는 동작이 필요하다.
이를 실현하기 위해 속도 벡터를 정면으로 정의한다
나비가 정면에 비해 머리와 몸을 회전하는 것을 계산한다.
이를 위해 필요한 것은 회전 행렬이다.
회전 행렬은 세 개의 직교 단위 벡터로 나타낼 수 있습니다.

【인용원】: 생물 역학에서 운동 센서를 이용하다
나는 이 나비를 만들 때 선행 자료를 훑어보기 전에
다음 행렬은 회전 행렬이다.
그러나 자세히 보면 3개의 정교한 단위 벡터도 포함되어 있다.
X축 중심의 이 부분은 1열의 단위 벡터(1, 0, 0)로 표현됩니다.
  • X축 중심θ도 회전 시
  • \begin{pmatrix}
    x'\\
    y' \\
    z'\\
    1\\
    \end{pmatrix}
    =
    \begin{pmatrix}
    1 & 0 & 0 & 0\\
    0 & cosθ & -sinθ & 0 \\
    0 & sinθ & cosθ & 0\\
    0 & 0 & 0 & 1\\
    \end{pmatrix}
    \begin{pmatrix}
    x\\
    y\\
    z\\
    1\\
    \end{pmatrix}
    다시 말하자면'나비의 정면을 머리와 몸에 대고 회전시켜 계산한다'고 했다.
    이것은 세 개의 정교 단위 벡터로 표시할 수 있다
    이런 성질을 이용하여 해결하다.
    회전된 3개의 정교 단위 벡터만 알면 된다는 것이다.
    이번에는 속도 (velocity) 벡터가 정면으로 정의되었습니다
    첫 번째 단위의 벡터는 갑자기 해결될 것이다.
    그런 다음 회전된 공간에서 위로 향하는 벡터를 (0, 1, 0)로 정의합니다.
    이것은 두 단위의 벡터를 얻었다.
    이 두 단위는 벡터가 교차한다.
    따라서 다른 벡터는 외적에 따라 계산할 수 있다.
    여기까지의 절차는 다음과 같은 부분이다.
    //進行方向を向かせるための回転行列を作成
    //正面は進行方向、すなわちParticleから取得したvelocity
    float3 forward = normalize(v.velocity);
    float3 up = float3(0, 1, 0);
    float3 right = normalize(cross(forward, up));
    
    //行列を作成
    //どうやら変数に詰めるときだけ行オーダーになっている?っぽい
    //なのでtransposeで転置を行う
    //すなわち以下でも可
    //float3x3 mat = float3x3(right.x,up.x,forward.x,
    //                        right.y,up.y,forward.y,
    //                        right.z,up.z,forward.z);
    float3x3 mat = transpose(float3x3(right, up, forward));
    
    //Velocity(正面方向)に応じた回転を反映
    v.vertex.xyz = mul(mat, local);
    
    transpose는 행렬을 바꾸는 함수이다.
    기본적으로 Shader는 계산할 때 열순으로 계산한다
    변수를 넣을 때만 주문을 하고 사용했습니다transpose.
    굳이 쓸 필요가 없어요.
    좀 귀찮아서 다시 막힐 때를 위해 나는 필기를 남긴다.

    비선형 브라운 운동


    나비가 흔들거리는 동작을 재현하기 위해 복잡한 계산 공식을 채택했다.
    float fbm(float x, float t)
    {
        return sin(x + t) + 0.5 * sin(2.0 * x + t) + 0.25 * sin(4.0 * x + t);
    }
    
    이로써 신파가 불규칙적으로 소음을 섞은 계산 결과를 계산한다.

    Velocity 구성


    나비의 행진 방향에 관해서는Partical SystemVelocity over Lifetime을 사용합니다.
    다음과 같이 적용Random Between하여 허용 범위 내에서 임의성을 가지도록 한다
    느낌이 좋다.Y축을 Orbital로 회전하는 처리도 추가해 보았다.


    참조 링크


    [Unity] Partical System에서 vertex와 texcoord로 제작된 회전 애니메이션
    분형 브라운 운동과 역 도약
    면도기로 3D 콘서트 공연 업데이트~'러브 라이브! 학교 아이돌 축제 올스타즈'(스스타) 개발 사례~
    [Unity][URP]Y축 마루판 스크레이퍼
    공간과 플랫폼 간 – Unity 좌표 변환에 대한 이야기 –

    좋은 웹페이지 즐겨찾기