Search Unity

Raymarching Issue with Normals

Discussion in 'General Graphics' started by Dragonzbw, Apr 3, 2021.

  1. Dragonzbw

    Dragonzbw

    Joined:
    Jan 20, 2013
    Posts:
    1
    Hello all, I'm trying to figure out raymarching using a custom-written shader and am having some issues with normal calculation. This is in URP. Basically, my shader works fine for rendering a sphere or torus, but making a cube causes weird black noise to appear in a circle pattern at certain distances.

    Here's the raymarching code w/normal calculation and distance calculation (sorry for weird code formatting, I dunno why it tabbed everything out like this but I don't feel like deleting all the extra spaces):
    Code (CSharp):
    1.  
    2. struct v2f
    3. {
    4.     float2 uv : TEXCOORD0;
    5.     float4 vertex : SV_POSITION;
    6.     float3 ro : TEXCOORD1;
    7.     float3 hitPos : TEXCOORD2;
    8. };
    9.  
    10. sampler2D _MainTex;
    11. float4 _MainTex_ST;
    12.  
    13. v2f vert (appdata v)
    14. {
    15.     v2f o;
    16.     o.vertex = UnityObjectToClipPos(v.vertex);
    17.     o.uv = TRANSFORM_TEX(v.uv, _MainTex);
    18.     o.ro = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1));
    19.     o.hitPos = v.vertex;
    20.     return o;
    21. }
    22.  
    23. // Get shortest dist from point p to the surface
    24. float GetDist(float3 p)
    25. {
    26.     return length(max(abs(p) - float3(.4,.4,.4), 0.0));
    27. }
    28.  
    29. float Raymarch(float3 ro, float3 rd)
    30. {
    31.     // dist from origin
    32.     float dO = 0;
    33.     // dist from surface
    34.     float dS;
    35.     // march
    36.     for(int i = 0; i < MAX_STEPS; i++)
    37.     {
    38.         // p is the current position of the raymarch
    39.         float3 p = ro + dO * rd;
    40.         // get shortest dist to surface from current position
    41.         dS = GetDist(p);
    42.         // march forward
    43.         dO += dS;
    44.         // if we've reached the surface or went too far, break
    45.         if(dS < SURF_DIST || dO > MAX_DIST) break;
    46.     }
    47.     return dO;
    48. }
    49.  
    50. float3 GetNormal(float3 p)
    51. {
    52.     float2 e = float2(.001, 0);
    53.     float3 n = GetDist(p) - float3(
    54.         GetDist(p - e.xyy),
    55.         GetDist(p - e.yxy),
    56.         GetDist(p - e.yyx)
    57.     );
    58.  
    59.     return normalize(n);
    60. }
    61.  
    62. fixed4 frag (v2f i) : SV_Target
    63. {
    64.     float2 uv = i.uv - .5;
    65.     // ray origin
    66.     float3 ro = i.ro;
    67.     float3 rd = normalize(i.hitPos - ro);
    68.  
    69.     float d = Raymarch(ro, rd);
    70.     fixed4 col = 0;
    71.  
    72.     if(d >= MAX_DIST)
    73.         discard;
    74.     else
    75.     {
    76.         float3 p = ro + rd * d;
    77.         float3 n = GetNormal(p);
    78.         col.rgb = n;
    79.     }
    80.     return col;
    81. }
    And here are some screenshots. As you can see, from certain angles and distances everything's fine, but if you're too close or too far away from the cube this weird noise shows up.




    ^The weird noise in question

    No idea what's causing this, I'm assuming the normals are being calculated as the opposite direction for those pixels, but I don't get why.