NaiveSurfaceNetsで穴堀り

環境

Unity2022.2.14f1

概要

以前作ったNaiveSurfaceNetsでボックスを作り、球状に穴を開けるテストです。

基本的に以前のコードのままですが、ComputeShaderに以下の穴を開ける関数と法線を算出する関数を追加しています。

モグラゲームとか作れそうです。

コード

#pragma kernel ChangeSdfVoxels
[numthreads(1, 1, 1)]
void ChangeSdfVoxels(uint3 id : SV_DispatchThreadID)
{
    uint3 start = SpherePos - SphereRadius;
    uint3 vpos = start + id;
    if(vpos.x > SdfVoxelSize - 1 || vpos.y > SdfVoxelSize - 1 || vpos.z > SdfVoxelSize - 1)
        return;
    
    uint index = (vpos.z * SdfVoxelSize * SdfVoxelSize) + (vpos.y * SdfVoxelSize) + (vpos.x);
    float value = 1.0 - (length(vpos - SpherePos) / (float)SphereRadius);
    SdfVoxels[index] = max(value, SdfVoxels[index]);
}

#pragma kernel GenerateNormals
[numthreads(1024, 1, 1)]
void GenerateNormals(uint3 id : SV_DispatchThreadID)
{
    if(id.x >= IndirectArgs[0] / 3)
        return;

    uint vindex = id.x * 3;
    float3 vec0 = Vertices[Indices[vindex + 1]] - Vertices[Indices[vindex]];
    float3 vec1 = Vertices[Indices[vindex + 2]] - Vertices[Indices[vindex]];
    float3 normal = normalize(cross(vec0, vec1));
    float QUANTIIZE_FACTOR = 32768.0;
    int3 inormal = (int3) (normal * QUANTIIZE_FACTOR);

    Normals.InterlockedAdd(normalStride * Indices[vindex + 0] + 0, inormal.x);
    Normals.InterlockedAdd(normalStride * Indices[vindex + 0] + 4, inormal.y);
    Normals.InterlockedAdd(normalStride * Indices[vindex + 0] + 8, inormal.z);

    Normals.InterlockedAdd(normalStride * Indices[vindex + 1] + 0, inormal.x);
    Normals.InterlockedAdd(normalStride * Indices[vindex + 1] + 4, inormal.y);
    Normals.InterlockedAdd(normalStride * Indices[vindex + 1] + 8, inormal.z);

    Normals.InterlockedAdd(normalStride * Indices[vindex + 2] + 0, inormal.x);
    Normals.InterlockedAdd(normalStride * Indices[vindex + 2] + 4, inormal.y);
    Normals.InterlockedAdd(normalStride * Indices[vindex + 2] + 8, inormal.z);
}
Shader "Unlit/GpuNaiveSurfaceNets2"
{
    Properties
    {
    }

    CGINCLUDE
    #include "UnityCG.cginc"
    #include "Lighting.cginc"

    struct appdata
    {
        uint vertexId : SV_VertexID;
    };

    struct v2f
    {
        float4 posCS : SV_POSITION;
        float3 normalOS : NORMAL;
        float3 lightDirOS : TEXCOORD0;
    };
    StructuredBuffer<float3> Vertices;
    StructuredBuffer<int3> Normals;
    StructuredBuffer<uint> Indices;
    sampler2D _MainTex;
    float4 _MainTex_ST;

    ENDCG

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

        Pass
        {
            CGPROGRAM
            #pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag

            v2f vert (appdata v)
            {
                v2f o;

                float3 vertex = Vertices[Indices[v.vertexId]];
                float4 posOS = float4(vertex, 1.0);
                o.posCS = UnityObjectToClipPos(posOS);
#if 0
                // flat shading
                uint vindex = v.vertexId - (v.vertexId % 3);
                float3 vec0 = Vertices[Indices[vindex + 1]] - Vertices[Indices[vindex]];
                float3 vec1 = Vertices[Indices[vindex + 2]] - Vertices[Indices[vindex]];
                o.normalOS = normalize(cross(vec0, vec1));
#else
                float QUANTIIZE_FACTOR = 32768.0;
                int3 n = Normals[Indices[v.vertexId]];
                o.normalOS = normalize(float3(n.x / QUANTIIZE_FACTOR, n.y / QUANTIIZE_FACTOR, n.z / QUANTIIZE_FACTOR));
#endif
                o.lightDirOS = ObjSpaceLightDir(posOS);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float3 normalOS = normalize(i.normalOS);
                float3 lightDirOS = normalize(i.lightDirOS);
                float NdotL = dot(normalOS, lightDirOS) * 0.5 + 0.5;
                fixed4 col = fixed4(1,0,0,1);
                col.rgb *= NdotL;
                return col;
            }
            ENDCG
        }
    }
}

参考

SDFの視覚化(NaiveSurfaceNets) - テキトープログラム( ..)φメモ

Question - Calculating Normals of a Mesh in Compute Shader - Unity Forum