ShaderGraphでVoronoiエッジ距離

環境

Unity2022.2.2f1

ShaderGraph14.0.4

概要

CustomFunctionノードを使用して、標準のvoronoiノードにプラスしてエッジまでの距離を返すようにしたノード用の関数を作成してみました。

※for文を[unroll]しないと自分の環境だとバグるので入れています。

コード

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Hashes.hlsl"

float2 Unity_Voronoi_RandomVector_Deterministic_float (float2 UV, float offset)
{
    Hash_Tchou_2_2_float(UV, UV);
    return float2(sin(UV.y * offset), cos(UV.x * offset)) * 0.5 + 0.5;
}

void VoronoiWithDistance_float(float2 UV, float AngleOffset, float CellDensity, out float Out, out float Cells, out float Distance)
{
    // first pass: regular voronoi
    float2 g = floor(UV * CellDensity);
    float2 f = frac(UV * CellDensity);
    float t = 8.0;
    float3 res = float3(8.0, 0.0, 0.0);

    float2 mr;

    [unroll]
    for (int y = -1; y <= 1; y++)
    {
        [unroll]
        for (int x = -1; x <= 1; x++)
        {
            float2 lattice = float2(x, y);
            float2 offset = Unity_Voronoi_RandomVector_Deterministic_float(lattice + g, AngleOffset);
            float d = distance(lattice + offset, f);
            if (d < res.x)
            {
                res = float3(d, offset.x, offset.y);
                Out = res.x;
                Cells = res.y;

                mr = lattice + offset - f;
            }
        }
    }
    // second pass: distance to borders
    float md = 8.0;
    [unroll]
    for (int y = -2; y <= 2; y++)
    {
        [unroll]
        for (int x = -2; x <= 2; x++)
        {
            float2 lattice = float2(x, y);
            float2 offset = Unity_Voronoi_RandomVector_Deterministic_float(lattice + g, AngleOffset);
            float2 r = lattice + offset - f;

            if (dot(mr - r, mr - r) > 0.0001)
            {
                md = min(md, dot(0.5 * (mr + r), normalize(r - mr)));
            }
        }
    }
    Distance = md;//float3(md, mr.x, mr.y);
}

void VoronoiGetBorder_float(float Distance, float Length, out float Out)
{
    Out = 1.0 - smoothstep(0.0, Length, Distance);
}

参考

Inigo Quilez :: computer graphics, mathematics, shaders, fractals, demoscene and more

【Unity】リピート感なしのテクスチャマッピングの再現【Blender】 - 夜はCGの時間

Unity Shader Graph custom node for precise voronoi borders · GitHub

[Unity] Shader Graph でノイズ関数を改造してタイリングに対応するカスタムノードを作る(改) - Qiita

わけわからない

標準のVoronoiノードが正しく動作しない。赤くなる。何でこんな結果になるのか。

コードでも赤になる。

ただしVoronoi関数のforの上に[unroll]を付けると直る。

正しいのかはわからない。

Shader "Unlit/TestVoronoi"
{
    Properties
    {
    }

    CGINCLUDE
    //#pragma enable_d3d11_debug_symbols
    #include "UnityCG.cginc"
    // Graph Includes
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Hashes.hlsl"
        
    // Graph Functions
    float2 Unity_Voronoi_RandomVector_Deterministic_float (float2 UV, float offset)
    {
        Hash_Tchou_2_2_float(UV, UV);
        return float2(sin(UV.y * offset), cos(UV.x * offset)) * 0.5 + 0.5;
    }
        
    void Unity_Voronoi_Deterministic_float(float2 UV, float AngleOffset, float CellDensity, out float Out, out float Cells)
    {
        float2 g = floor(UV * CellDensity);
        float2 f = frac(UV * CellDensity);
        float t = 8.0;
        float3 res = float3(8.0, 0.0, 0.0);
        [unroll]
        for (int y = -1; y <= 1; y++)
        {
            [unroll]
            for (int x = -1; x <= 1; x++)
            {
                float2 lattice = float2(x, y);
                float2 offset = Unity_Voronoi_RandomVector_Deterministic_float(lattice + g, AngleOffset);
                float d = distance(lattice + offset, f);
                if (d < res.x)
                {
                    res = float3(d, offset.x, offset.y);
                    Out = res.x;
                    Cells = res.y;
                }
            }
        }
    }
        
    void Unity_ColorspaceConversion_HSV_RGB_float(float3 In, out float3 Out)
    {
        float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
        float3 P = abs(frac(In.xxx + K.xyz) * 6.0 - K.www);
        Out = In.z * lerp(K.xxx, saturate(P - K.xxx), In.y);
    }

    ENDCG

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

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

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float voroValue;
                float voroCells;
                Unity_Voronoi_Deterministic_float(i.uv, 6, 5, voroValue, voroCells);
                fixed3 rgb;
                Unity_ColorspaceConversion_HSV_RGB_float(float3(voroCells, 1, 1), rgb);
                return fixed4(rgb, 1);
            }
            ENDCG
        }
    }
}