Home > database >  Normal estimation of signed distance function just won't work
Normal estimation of signed distance function just won't work

Time:09-27

I'm trying to create a compute shader in Unity that performs raymarching. So far I have an SDF of a box and a ray generation / marching function all of which seems to work fine:

struct Ray {
    float3 position;
    float3 direction;
};

Ray CreateCameraRay(float2 uv) {
    Ray ray;
    ray.position = mul(CameraToWorld, float4(0, 0, 0, 1)).xyz;
    ray.direction = mul(CameraInverseProjection, float4(uv, 0, 1)).xyz;
    ray.direction = mul(CameraToWorld, float4(ray.direction, 0)).xyz;
    ray.direction = normalize(ray.direction);
    return ray;
}

float sdf_box (float3 p)
{
    float3 c = float3(0.0f, 0.0f, 0.0f);
    float3 s = float3(1.0f, 1.0f, 1.0f);
    float x = max
    (   p.x - c.x - float3(s.x / 2., 0, 0),
        c.x - p.x - float3(s.x / 2., 0, 0)
    );
    float y = max
    (   p.y - c.y - float3(s.y / 2., 0, 0),
        c.y - p.y - float3(s.y / 2., 0, 0)
    );
    
    float z = max
    (   p.z - c.z - float3(s.z / 2., 0, 0),
        c.z - p.z - float3(s.z / 2., 0, 0)
    );
    float d = x;
    d = max(d,y);
    d = max(d,z);
    return d;
}

[numthreads(32,32,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
    uint width, height;
    Result.GetDimensions(width, height);
    float2 uv = id.xy / float2(width, height) * 2.0 - 1.0;
    Ray ray = CreateCameraRay(uv);

    float4 colour = float4(0, 0, 0, 0);
    bool cont = true;
    int step = 0;

    while (cont) {
        step  ;
        float dist = sdf_box(ray.position);
        ray.position  = ray.direction * dist;
        if (dist < 0.01f) {
            colour = float4(ray.position, 0.0f);
        }
        cont = (dist > 0.01f) && (step < 500);
    }

    Result[id.xy] = colour;
}

The above code generates the following image:

Successfully rendering a cube

The next step is to add a bit of lighting. To do this I need a surface normal function, which I've defined as:

float EPSILON = 0.001f;
float3 normal(float3 p) {
    float2 e = float2(EPSILON, 0);
    return normalize(float3(
        sdf_box(p   e.xyy) - sdf_box(p   e.xyy),
        sdf_box(p   e.yxy) - sdf_box(p   e.yxy),
        sdf_box(p   e.yyx) - sdf_box(p   e.yyx))
    );
}

Now, to check that the normal function is working properly I decided to draw the normal of each contact point on the box (by replacing colour = float4(ray.position, 0.0f); with colour = float4(normal(ray.position), 0.0f). What I'm expecting is for 3 of the 6 face of the cube to be coloured red, green and blue (i.e. the top face should be green as it has a normal of (0, 1, 0)). However, what I get instead is a totally black cube: broken cube

This seems wrong. The normalize() should mean that there is at least some colour if the normal generated is non-zero, which suggests to me that the normal of all the points is just (0, 0, 0). But this can't be right because nowhere in or on the cube has a surface normal of 0 (other than the centre point).

I've now tried 4 different normal evaluation functions which I've found online, NONE of which work. I'm seriously questioning my sanity at this point.

CodePudding user response:

Nevermind, I am stupid:

float EPSILON = 0.001f;
float3 normal(float3 p) {
    float2 e = float2(EPSILON, 0);
    return normalize(float3(
        sdf_box(p   e.xyy) - sdf_box(p   e.xyy),
        sdf_box(p   e.yxy) - sdf_box(p   e.yxy),
        sdf_box(p   e.yyx) - sdf_box(p   e.yyx))
    );
}

Should clearly be:

float EPSILON = 0.001f;
float3 normal(float3 p) {
    float2 e = float2(EPSILON, 0);
    return normalize(float3(
        sdf_box(p   e.xyy) - sdf_box(p - e.xyy),
        sdf_box(p   e.yxy) - sdf_box(p - e.yxy),
        sdf_box(p   e.yyx) - sdf_box(p - e.yyx))
    );
}

In my defense, I wrote 3 different normal functions before this one which also didn't work.

EDIT: Strangely enough, after messing around a little bit more, the normal is always (0, 0, 0) when the above code is run, but not when the below code is run?

float3 normal(float3 p) {
    float2 e = float2(0.001f, 0);
    return normalize(float3(
        sdf_box(p   e.xyy) - sdf_box(p - e.xyy),
        sdf_box(p   e.yxy) - sdf_box(p - e.yxy),
        sdf_box(p   e.yyx) - sdf_box(p - e.yyx))
    );
}

No idea why.

  • Related