夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

ゼロから始めるUnityシェーダー開発 第5章 Unityでボロノイ図を描くシェーダーを書く

本日はシェーダー学習枠です。

今回はボロノイ図を描くシェーダーを書いていきます。

〇ボロノイ図とは?

ボロノイ図ボロノイテクスチャとも呼ばれていますが、これは波の作成などに使用されるノイズテクスチャです。

 座標上にランダムに割り当てた頂点同士の中点が1になるようにグラデーションがかかる処理です。

 ボロノイ図を使用することで先に上げているように波の作成やエフェクトの作成、ノーマルとして使用することで壁などの質感再現にも使用できます。

〇ボロノイ図を描くシェーダー

ボロノイ図を描く処理は以下のようになります。

            float2 random2(float2 uv) {
                return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);
            }
            
            // Voronoi Noise generator function
            float voronoiNoise(float2 uv) {
                float2 p = floor(uv);
                float2 f = frac(uv);
                
                float minDist = 1.0;
                float2 nearest;
                
                for(int j = -1; j <= 1; ++j) {
                    for(int i = -1; i <= 1; ++i) {
                        float2 g = float2(i, j);
                        float2 randomPoint = random2(p + g);
                        float2 diff = g + randomPoint - f;
                        float dist = length(diff);
                        
                        if (dist < minDist) {
                            minDist = dist;
                            nearest = diff;
                        }
                    }
                }
                
                return minDist;
            }

random2メソッドは座標上のランダムの点を指定するためのランダム処理メソッドです。

frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);

fracメソッドは少数部を返すメソッドです。ucとランダムの値の内積をとっています。

実際のボロノイ図を描くメソッドは以下です。 引数としてuvを与えます。

            float voronoiNoise(float2 uv) {
                float2 p = floor(uv);
                float2 f = frac(uv);
                
                float minDist = 1.0;
                float2 nearest;
                
                for(int j = -1; j <= 1; ++j) {
                    for(int i = -1; i <= 1; ++i) {
                        float2 g = float2(i, j);
                        float2 randomPoint = random2(p + g);
                        float2 diff = g + randomPoint - f;
                        float dist = length(diff);
                        
                        if (dist < minDist) {
                            minDist = dist;
                            nearest = diff;
                        }
                    }
                }
                
                return minDist;
            }

こちらは見ての通り分岐処理や負荷が高いメソッドを多用しているためパフォーマンス面ではあまり実用的ではないかもしれません。

〇シェーダー全文

ボロノイ図を描くシェーダーは以下です。

Shader "Custom/ElectricCircuit" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _NoiseScale ("Noise Scale", float) = 10.0
        _LineWidth ("Line Width", Range(0, 1)) = 0.1
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

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

            sampler2D _MainTex;

            float _NoiseScale;


            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }
            
            // Random function
            float2 random2(float2 uv) {
                return frac(sin(dot(uv, float2(12.9898, 78.233))) * 43758.5453);
            }
            
            // Voronoi Noise generator function
            float voronoiNoise(float2 uv) {
                float2 p = floor(uv);
                float2 f = frac(uv);
                
                float minDist = 1.0;
                float2 nearest;
                
                for(int j = -1; j <= 1; ++j) {
                    for(int i = -1; i <= 1; ++i) {
                        float2 g = float2(i, j);
                        float2 randomPoint = random2(p + g);
                        float2 diff = g + randomPoint - f;
                        float dist = length(diff);
                        
                        if (dist < minDist) {
                            minDist = dist;
                            nearest = diff;
                        }
                    }
                }
                
                return minDist;
            }
            
            fixed4 frag (v2f i) : SV_Target {
                float2 uv = i.uv;

                float noise = voronoiNoise(uv * _NoiseScale);

                fixed4 color = noise;

                return color;
            }
            ENDCG
        }
    }
}

UVに沿ってボロノイ図を描いています。