知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UnityShader】ポリゴン分解シェーダー #76

前回の成果

シンプルなGeometry Shaderの中身を理解した。

soramamenatan.hatenablog.com


今回やること

Geometry Shaderを使用して、ポリゴンを分解します。

事前準備

Scene上にSphereを配置します。
そして、今回制作するMaterialをアタッチしてください。

f:id:soramamenatan:20201010143028p:plain


ソースコード

Shader "Unlit/MovePolygon" {
    Properties {
        _Color ("Color", Color) = (1, 1, 1, 1)
        _ScaleFactor ("Scale Factor", float) = 0.5
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag

            #include "UnityCG.cginc"

            fixed4 _Color;
            float _ScaleFactor;

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

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

            appdata vert (appdata v) {
                return v;
            }

            [maxvertexcount(3)]
            void geom (triangle appdata input[3], inout TriangleStream<g2f> stream) {
                float3 vec1 = input[1].vertex - input[0].vertex;
                float3 vec2 = input[2].vertex - input[0].vertex;
                float3 normal = normalize(cross(vec1, vec2));
                [unroll]
                for (int i = 0; i < 3; i++) {
                    appdata v = input[i];
                    g2f o;
                    v.vertex.xyz += normal * (_SinTime.w * 0.5 + 0.5) * _ScaleFactor;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    stream.Append(o);
                }
                stream.RestartStrip();
            }

            fixed4 frag (g2f i) : SV_Target {
                fixed4 col = _Color;
                return col;
            }
            ENDCG
        }
    }
    FallBack "Unlit/Color"
}

前回のシンプルなシェーダーから大きな変更は特にありません。


Shaderの解説

geom関数の中を解説します。


法線の取得

float3 vec1 = input[1].vertex - input[0].vertex;
float3 vec2 = input[2].vertex - input[0].vertex;
float3 normal = normalize(cross(vec1, vec2));

三角ポリゴンには3つの頂点が存在します。
1つの頂点から、他の2つの頂点を減算し2つのベクトルを出します。
そして、この2つのベクトルの外積を出します。
すると、それは三角ポリゴンの法線ベクトルとなります。

三角ポリゴンの法線ベクトルのイメージ

f:id:soramamenatan:20201023152507p:plain

生WebGL入門:初音ミクの美麗3Dモデルを表示する(中編):より引用


法線方向に移動させる

v.vertex.xyz += normal * (_SinTime.w * 0.5 + 0.5) * _ScaleFactor;

三角ポリゴンの法線が取得出来たので、その方向に移動させてみます。

_SinTimeは定義済のもので、時間の正弦となっています。

定義 意味
_SinTime.x t / 8
_SinTime.y t / 4
_SinTime.z t / 2
_SinTime.w t

正弦なので、-1.0 ~ 1.0の間を往復します。
それを0 ~ 1.0に修正するために、

_SinTime.w * 0.5 + 0.5

をしています。


結果

下記のgifのようにポリゴンが時間に応じて、分解されれば成功です。

f:id:soramamenatan:20201023153821g:plain

inspector

f:id:soramamenatan:20201023154052p:plain


カメラの距離に応じて分解

SinTimeを使用して分解できました。
次はカメラとの距離に応じてポリゴンを分解してみます。


事前準備

先ほどと全く同じです。
Scene上にSphereを配置します。
そして、今回制作するMaterialをアタッチしてください。

f:id:soramamenatan:20201010143028p:plain


ソースコード

Shader "Unlit/CameraPolygon" {
    Properties {
        _FarColor ("Far Color", Color) = (1, 1, 1, 1)
        _NearColor ("Near Color", Color) = (0, 0, 0, 1)
        _ScaleFactor ("Scale Factor", float) = 0.5
        _StartDistance ("Start Distance", float) = 3.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag

            #include "UnityCG.cginc"

            fixed4 _FarColor;
            fixed4 _NearColor;
            float _ScaleFactor;
            float _StartDistance;

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

            struct g2f {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
            };

            float rand(float2 seed) {
                return frac(sin(dot(seed.xy, float2(12.9898, 78.233))) * 43758.5453);
            }

            appdata vert (appdata v) {
                return v;
            }

            [maxvertexcount(3)]
            void geom (triangle appdata input[3], inout TriangleStream<g2f> stream) {
                float3 center = (input[0].vertex + input[1].vertex + input[2].vertex) / 3;
                float4 worldPos = mul(unity_ObjectToWorld, float4(center, 1.0));
                float3 dist = length(_WorldSpaceCameraPos - worldPos);

                float3 vec1 = input[1].vertex - input[0].vertex;
                float3 vec2 = input[2].vertex - input[0].vertex;
                float3 normal = normalize(cross(vec1, vec2));

                fixed destruction = clamp(_StartDistance - dist, 0.0, 1.0);
                fixed gradient = clamp(dist - _StartDistance, 0.0, 1.0);

                fixed random = rand(center.xy);
                fixed3 random3 = random.xxx;

                [unroll]
                for (int i = 0; i < 3; i++) {
                    appdata v = input[i];
                    g2f o;
                    v.vertex.xyz += normal * destruction * _ScaleFactor * random3;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    o.color = fixed4(lerp(_NearColor.rgb, _FarColor.rgb, gradient), 1);
                    stream.Append(o);
                }
                stream.RestartStrip();
            }

            fixed4 frag (g2f i) : SV_Target {
                fixed4 col = i.color;
                return col;
            }
            ENDCG
        }
    }
    FallBack "Unlit/Color"
}

ベースは同じとなります。


Shaderの解説

主な処理はカメラとポリゴンの距離ですので、そこを解説します。


カメラとポリゴンの距離

float3 center = (input[0].vertex + input[1].vertex + input[2].vertex) / 3;
float4 worldPos = mul(unity_ObjectToWorld, float4(center, 1.0));
float3 dist = length(_WorldSpaceCameraPos - worldPos);

まずは、三角ポリゴンの中心座標を取得しています。
次に中心座標をワールド座標系に変換しています。
unity_ObjectToWorldは現在のモデルの行列ですので、乗算してあげることでワールド座標系へと変換できます。
最後にカメラとの距離を出して、dist変数で保持しています。


結果

カメラとの距離に応じて、ポリゴンが分解されれば成功です。

f:id:soramamenatan:20201023160053g:plain

inspector

f:id:soramamenatan:20201023160059p:plain


ソースコードにコメントを付与

Shader "Unlit/CameraPolygon" {
    Properties {
        _FarColor ("Far Color", Color) = (1, 1, 1, 1)
        _NearColor ("Near Color", Color) = (0, 0, 0, 1)
        _ScaleFactor ("Scale Factor", float) = 0.5
        _StartDistance ("Start Distance", float) = 3.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag

            #include "UnityCG.cginc"

            fixed4 _FarColor;
            fixed4 _NearColor;
            float _ScaleFactor;
            float _StartDistance;

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

            struct g2f {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
            };

            float rand(float2 seed) {
                return frac(sin(dot(seed.xy, float2(12.9898, 78.233))) * 43758.5453);
            }

            appdata vert (appdata v) {
                return v;
            }

            [maxvertexcount(3)]
            void geom (triangle appdata input[3], inout TriangleStream<g2f> stream) {
                // カメラとポリゴンとの距離
                float3 center = (input[0].vertex + input[1].vertex + input[2].vertex) / 3;
                float4 worldPos = mul(unity_ObjectToWorld, float4(center, 1.0));
                float3 dist = length(_WorldSpaceCameraPos - worldPos);

                // ポリゴンの法線ベクトル
                float3 vec1 = input[1].vertex - input[0].vertex;
                float3 vec2 = input[2].vertex - input[0].vertex;
                float3 normal = normalize(cross(vec1, vec2));

                // カメラとの距離に応じてポリゴンを変化
                fixed destruction = clamp(_StartDistance - dist, 0.0, 1.0);
                // カメラとの距離に応じて色を変化
                fixed gradient = clamp(dist - _StartDistance, 0.0, 1.0);

                // ランダムな値
                fixed random = rand(center.xy);
                fixed3 random3 = random.xxx;

                [unroll]
                for (int i = 0; i < 3; i++) {
                    appdata v = input[i];
                    g2f o;
                    // 法線方向へ移動
                    v.vertex.xyz += normal * destruction * _ScaleFactor * random3;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    // 色を変化
                    o.color = fixed4(lerp(_NearColor.rgb, _FarColor.rgb, gradient), 1);
                    stream.Append(o);
                }
                stream.RestartStrip();
            }

            fixed4 frag (g2f i) : SV_Target {
                fixed4 col = i.color;
                return col;
            }
            ENDCG
        }
    }
    FallBack "Unlit/Color"
}

今回は以上となります。
ここまでご視聴ありがとうございました。


モデルを分解させてみた。


ソースコード

Shader "Unlit/CameraTexturePolygon" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _FarColor ("Far Color", Color) = (1, 1, 1, 1)
        _NearColor ("Near Color", Color) = (0, 0, 0, 1)
        _ScaleFactor ("Scale Factor", float) = 0.5
        _StartDistance ("Start Distance", float) = 3.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag

            #include "UnityCG.cginc"

            fixed4 _FarColor;
            fixed4 _NearColor;
            float _ScaleFactor;
            float _StartDistance;
            sampler2D _MainTex;
            float4 _MainTex_ST;

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

            struct g2f {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                fixed4 color : COLOR;
            };

            float rand(float2 seed) {
                return frac(sin(dot(seed.xy, float2(12.9898, 78.233))) * 43758.5453);
            }

            appdata vert (appdata v) {
                return v;
            }

            [maxvertexcount(3)]
            void geom (triangle appdata input[3], inout TriangleStream<g2f> stream) {
                // カメラとポリゴンとの距離
                float3 center = (input[0].vertex + input[1].vertex + input[2].vertex) / 3;
                float4 worldPos = mul(unity_ObjectToWorld, float4(center, 1.0));
                float3 dist = length(_WorldSpaceCameraPos - worldPos);

                // ポリゴンの法線ベクトル
                float3 vec1 = input[1].vertex - input[0].vertex;
                float3 vec2 = input[2].vertex - input[0].vertex;
                float3 normal = normalize(cross(vec1, vec2));

                // カメラとの距離に応じてポリゴンを変化
                fixed destruction = clamp(_StartDistance - dist, 0.0, 1.0);
                // カメラとの距離に応じて色を変化
                fixed gradient = clamp(dist - _StartDistance, 0.0, 1.0);

                // ランダムな値
                fixed random = rand(center.xy);
                fixed3 random3 = random.xxx;

                [unroll]
                for (int i = 0; i < 3; i++) {
                    appdata v = input[i];
                    g2f o;
                    // 法線方向へ移動
                    v.vertex.xyz += normal * destruction * _ScaleFactor * random3;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    // 色を変化
                    o.color = fixed4(lerp(_NearColor.rgb, _FarColor.rgb, gradient), 1);
                    stream.Append(o);
                }
                stream.RestartStrip();
            }

            fixed4 frag (g2f i) : SV_Target {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
    FallBack "Unlit/Color"
}

_MainTexにしただけ。


結果

f:id:soramamenatan:20201023160701g:plain

inspector

f:id:soramamenatan:20201023160701p:plain

参考サイト様

styly.cc

assetstore.unity.com