SDFの視覚化(StackedPlane)

環境

Unity2022.2.14f1

概要

板ポリを視線奥方向に重ねることで、RayMarchingをしないで視覚化するという手法のテストです。

参考の記事に記載されているように、角ばったものや、3DTextureの視覚化には向かないようです。

雲や霧状のものの表現には使えそうではあります。

コード

using System.Collections;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using static UnityEngine.GraphicsBuffer;

public unsafe class TestStackedPlane : MonoBehaviour
{
    [SerializeField] private Material _material = null;
    [SerializeField] private int _sliceNum = 64;
    private GraphicsBuffer _gbMatrices = null;
    private Bounds _bounds;
    private static int _spMatrices = Shader.PropertyToID("_Matrices");
    private static int _spSliceNum = Shader.PropertyToID("_SliceNum");

#if UNITY_EDITOR
    private void OnValidate()
    {
        OnEnable();
    }
#endif
    private void OnEnable()
    {
        _gbMatrices = new GraphicsBuffer(Target.Structured, UsageFlags.LockBufferForWrite, 1, sizeof(Matrix4x4));
        _material.SetBuffer(_spMatrices, _gbMatrices);
        _material.SetInt(_spSliceNum, _sliceNum);
        _bounds = new Bounds(Vector3.zero, new Vector3(10000.0f, 10000.0f, 10000.0f));
    }

    private void OnDisable()
    {
        if(_gbMatrices != null)
            _gbMatrices.Dispose();
    }

    private void Update()
    {
        var matrices = _gbMatrices.LockBufferForWrite<Matrix4x4>(0, 1);
        matrices[0] = transform.localToWorldMatrix;
        _gbMatrices.UnlockBufferAfterWrite<Matrix4x4>(1);

        Graphics.DrawProcedural(_material, _bounds, MeshTopology.Quads, _sliceNum * 4, 1);
    }
}
Shader "Unlit/StackedPlane"
{
    Properties
    {
        _Color("Color", Color) = (1, 1, 1, 1)
        _SdfTex ("Sdf Texture", 3D) = "white" {}
    }

    CGINCLUDE
    #pragma multi_compile_fwdbase nolightmap nodirlightmap nodynlightmap
    #include "UnityCG.cginc"

    #define EPSILON 0.00001
    float _SliceNum;
    fixed4 _Color;
    sampler3D _SdfTex;
    StructuredBuffer<float4x4> _Matrices;

    struct appdata
    {
        uint vertexId : SV_VertexID;
        uint instanceId : SV_InstanceID;
    };

    struct v2f
    {
        float4 posCS : SV_POSITION;
        float3 posOS : TEXCOORD0;
    };
    ENDCG

    SubShader
    {
        Tags
        {
            "Queue"="Transparent"
            "RenderType"="Transparent"
            "IgnoreProjector"="True"
        }
        ZWrite Off
        Blend SrcAlpha One
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            v2f vert (appdata v)
            {
                v2f o;
                unity_ObjectToWorld = _Matrices[v.instanceId];
                const uint vnum = 4;
                uint vertexId = v.vertexId % vnum;
                uint faceId = v.vertexId / vnum;
                const float2 vertices[] =
                {
                    float2(-1, -1),
                    float2(-1,  1),
                    float2( 1,  1),
                    float2( 1, -1)
                };
                const float scale = 1.5;
                o.posOS = float3(vertices[vertexId], ((float)faceId / (float)(_SliceNum - 1)) * 2.0 - 1.0) * scale;
                o.posOS = mul(o.posOS, UNITY_MATRIX_V);
                o.posCS = UnityObjectToClipPos(float4(o.posOS, 1.0));
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                float4 color = float4(_Color.rgb, 0.0);
                float alpha = 1.0 / (_SliceNum - 1);
                float dist = tex3D(_SdfTex, i.posOS * 0.5 + 0.5);
                if(dist < EPSILON)
                    color.a += alpha;
                return color;
            }
            ENDCG
        }
    }
}

参考

Raymarchingではない3Dアプローチ - Qiita

1. Realtime 3D Spiral Noise FX — Late's Blender Stuff

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