Search Unity

Raymarching and Signed Distance Function wrong normals

Discussion in 'Shaders' started by acropole, Jan 28, 2021.

  1. acropole

    acropole

    Joined:
    Aug 13, 2009
    Posts:
    171
    Hi,

    I have some precision issues with the following Signed Distance Function code, as we can see in the image below. I dont understand why and how to solve it.

    Code (CSharp):
    1. Shader "Hidden/Custom/Stairway"
    2. {
    3.     HLSLINCLUDE
    4.  
    5.     #include "Packages/com.unity.postprocessing/PostProcessing/Shaders/StdLib.hlsl"
    6.     #include "DistanceFunctions.cginc"
    7.     #include "hashes.cginc"
    8.  
    9.     TEXTURE2D_SAMPLER2D(_MainTex, sampler_MainTex);
    10.     TEXTURE2D_SAMPLER2D(_CameraDepthTexture, sampler_CameraDepthTexture);
    11.  
    12.     uniform float3
    13.         forward,
    14.         right,
    15.         up,
    16.         lightDir;
    17.  
    18.     uniform float
    19.         MaxDistance,
    20.         fov,
    21.         Cycle;
    22.  
    23.     uniform float aspect;
    24.     uniform float4 sphere;
    25.     uniform float4 MainColor;
    26.  
    27.     struct appdata
    28.     {
    29.         float4 vertex : POSITION;
    30.         float2 uv : TEXCOORD0;
    31.     };
    32.  
    33.     struct v2f
    34.     {
    35.         float4 vertex : SV_POSITION;
    36.         float2 uv : TEXCOORD0;
    37.         float4 ray : TEXCOORD1;
    38.         float2 texcoordStereo : TEXCOORD2;
    39.     };
    40.  
    41.     float mod(float x, float y) { return x - y * floor(x / y); }
    42.     float or(float x, float y) { return bool(x - floor(x / y)); }
    43.  
    44.     inline float4 ComputeNonStereoScreenPos(float4 pos) {
    45.         float4 o = pos * 0.5f;
    46.         o.xy = float2(o.x, o.y * _ProjectionParams.x) + o.w;
    47.         o.zw = pos.zw;
    48.         return o;
    49.     }
    50.  
    51.     inline float4 ComputeScreenPos(float4 pos)
    52.     {
    53.         float4 o = ComputeNonStereoScreenPos(pos);
    54.     #if defined(UNITY_SINGLE_PASS_STEREO)
    55.         o.xy = TransformStereoScreenSpaceTex(o.xy, pos.w);
    56.     #endif
    57.         return o;
    58.     }
    59.  
    60.     float fx(float3 p) { return cos(p.z / Cycle) - 5 * sin(p.z * 0.025) + 3 * cos(p.z * 0.2); };
    61.     float fy(float3 p) { return cos(p.z / Cycle) - 10 * sin(p.z * 0.035) + 5 * cos(p.z * 0.1); };
    62.  
    63.     float distanceField(float3 p)
    64.     {
    65.         float w = sphere.w,
    66.             x = fx(p),
    67.             z = trunc(p.z / Cycle);
    68.  
    69.         float l0 = w - length(float2(sin(z), cos(z)) * sphere.x - p.xy);
    70.         return l0;
    71.     }
    72.  
    73.     float3 normalField(float3 p)
    74.     {
    75.         float z = trunc(p.z / Cycle);
    76.         return float3(-normalize(p.xy - float2(sin(z), cos(z)) * sphere.x), 0);
    77.     }
    78.  
    79.     float4 raymarching(float3 ro, float3 rd, float depth)
    80.     {
    81.         float4 result = half4(0, 0, 0, 1);
    82.  
    83.         const int maxi = 128;
    84.         float t = 0;
    85.         float d;
    86.         float3 p, offset = 0, uv;
    87.  
    88.         for (int i = 0; i < maxi; i++)
    89.         {
    90.             if (t > MaxDistance)
    91.             {
    92.                 result = 0;
    93.                 break;
    94.             }
    95.  
    96.             p = ro + rd * t;
    97.             d = distanceField(p);
    98.             if (d < 0.00001)
    99.             {
    100.                 float3 n = normalField(p);
    101.                 float light = dot(-lightDir, n);
    102.                 result = float4(light, light, light, 1);
    103.                 break;
    104.             }
    105.  
    106.             t += d;
    107.         }
    108.  
    109.         return result;
    110.     }
    111.  
    112.     v2f vert (appdata v)
    113.     {
    114.         v2f o;
    115.  
    116.         o.vertex = float4(v.vertex.xyz, 1.0);
    117.  
    118.         o.ray = ComputeScreenPos(v.vertex);
    119.  
    120.         o.uv = TransformTriangleVertexToUV(v.vertex.xy);
    121. #if UNITY_UV_STARTS_AT_TOP
    122.         o.uv = o.uv * float2(1.0, -1.0) + float2(0.0, 1.0);
    123. #endif
    124.         o.texcoordStereo = TransformStereoScreenSpaceTex(o.uv, 1.0);
    125.  
    126.         return o;
    127.     }
    128.  
    129.     half4 frag (v2f i) : SV_Target
    130.     {
    131.         static float h = tan(fov);
    132.         static float w = h * aspect;
    133.  
    134.         float x = i.ray.x * 2.0f - 1.0f;
    135.         float y = i.ray.y * 2.0f - 1.0f;
    136.  
    137.         float3 dir = normalize(forward + x * w * right + y * h * up);
    138.         half4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, i.texcoordStereo);
    139.  
    140.         float4 result = raymarching(_WorldSpaceCameraPos, dir, 0);
    141.  
    142.         return float4(color * (1.0 - result.w) + result.xyz * result.w, 1.0);
    143.     }
    144.              
    145.     ENDHLSL
    146.  
    147.     SubShader
    148.     {
    149.         // No culling or depth
    150.         Cull Off
    151.             ZWrite Off
    152.             ZTest Always
    153.  
    154.             Pass
    155.         {
    156.             HLSLPROGRAM
    157.             #pragma vertex vert
    158.             #pragma fragment frag
    159.             #pragma target 3.0
    160.             ENDHLSL
    161.         }
    162.     }
    163. }
    sdf.jpg