Signed Distance Field (SDF) or ray-marching test using OSL

Tried to recreate in OSL this GLSL ray-marching tutorial from You Tube: https://www.youtube.com/watch?v=Ff0jJyyiVyw
And it works pretty fast for a CPU but require some additional setup by user in order for it to show anything.

2 Likes


float sdCapsule(vector p, vector a, vector b, float r) {
    vector ab = b-a;
    vector ap = p-a;
    float t = dot(ab, ap) / dot(ab, ab);
    t = clamp(t, 0., 1.);
    vector c = a + t*ab;

    return length(p-c) - r;
}

float sdCylinder(vector p, vector a, vector b, float r) {
    vector ab = b-a;
    vector ap = p-a;
    float t = dot(ab, ap) / dot(ab, ab);
    //t = clamp(t, 0., 1.);
    vector c = a + t*ab;
    float x = length(p-c)-r;
    float y = (abs(t-.5)-.5)*length(ab);
    float e = length(max(vector(x, y, 0.), 0.));
    float i = min(max(x, y), 0.);

    return e + i;
}

float sdTorus(vector p, vector r) {
    float x = length(vector(p[0], p[2], 0.) )-r[0];
    return length(vector(x, p[1], 0.))-r[1];
}

float dBox(vector p, vector s) {
    return length(max(abs(p)-s, 0.));
}

float GetDist(vector p) {
    vector s = vector(.0, .1, .6);

    float sphereDist =  length(p-s)-1;
    float planeDist = p[1];

    float cd = sdCapsule(p, vector(3, .5, 6), vector(3, 2.5, 6), .5);
    float td = sdTorus(p-vector(0, .5, 6), vector(1.5, .4, 0.));
    float bd = dBox(p-vector(-3.5, 1, 6), vector(1, .75, 1));
    float cyld = sdCylinder(p, vector(0, .3, 3), vector(3, .3, 5), .3);

    float d = min(cd, planeDist);
    d = min(d, td);
    d = min(d, bd);
    d = min(d, cyld);

    return d;
}

float RayMarch(vector viewOrigin, vector rd, int MAX_STEPS, int MAX_DIST) {
    float dO=0.;

    for(int i=0; i<MAX_STEPS; i++) {
        vector p = viewOrigin + rd*dO;
        float dS = GetDist(p);
        dO += dS;
        if(dO>MAX_DIST || dS<0.001) break;
    }

    return dO;
}

vector GetNormal(vector p) {
    float d = GetDist(p);
    vector e = vector(.001, .0, 0.);

    vector n = d - vector(
        GetDist(p-vector(e[0],  e[1],  e[1])),
        GetDist(p-vector(e[1],  e[0],  e[1])),
        GetDist(p-vector(e[1],  e[1],  e[0])));

    return normalize(n);
}

float GetLight(vector p, vector LightPos, float SURF_DIST, int MAX_STEPS, int MAX_DIST) {
    vector l = normalize(LightPos-p);
    vector n = GetNormal(p);

    float dif = clamp(dot(n, l), 0., 1.);
    float d = RayMarch(p+n*SURF_DIST*2., l, MAX_STEPS, MAX_DIST);
    if(d<length(LightPos-p)) dif *= .1;

    return dif;
}

shader mainImage(vector viewOrigin = 0, vector viewDirection = 0, vector LightPos = 0, int MAX_STEPS = 100, int MAX_DIST = 100, float SURF_DIST = 0.001,  output color fragColor = 0 )
{
    vector rd = normalize(viewDirection);
    float d = RayMarch(viewOrigin, rd, MAX_STEPS, MAX_DIST);
    vector p = viewOrigin + rd * d;
    float dif = GetLight(p, LightPos, SURF_DIST, MAX_STEPS, MAX_DIST);

    fragColor = color(dif, dif, dif);
}

3 Likes

There version with soft shadows and with controlling view direction by just rotating 3D viewport around:


float sdCapsule(vector p, vector a, vector b, float r) {
    vector ab = b-a;
    vector ap = p-a;
    float t = dot(ab, ap) / dot(ab, ab);
    t = clamp(t, 0., 1.);
    vector c = a + t*ab;
    return length(p-c) - r;
}

float sdCylinder(vector p, vector a, vector b, float r) {
    vector ab = b-a;
    vector ap = p-a;
    float t = dot(ab, ap) / dot(ab, ab);
    //t = clamp(t, 0., 1.);
    vector c = a + t*ab;
    float x = length(p-c)-r;
    float y = (abs(t-.5)-.5)*length(ab);
    float e = length(max(vector(x, y, 0.), 0.));
    float i = min(max(x, y), 0.);
    return e + i;
}

float sdTorus(vector p, vector r) {
    float x = length(vector(p[0], p[2], 0.) )-r[0];
    return length(vector(x, p[1], 0.))-r[1];
}

float dBox(vector p, vector s) {
    return length(max(abs(p)-s, 0.));
}

float GetDist(vector p) {
    vector s = vector(.0, .1, .6);

    float sphereDist =  length(p-s)-1;
    float planeDist = p[1];
    float cd = sdCapsule(p, vector(3, .5, 6), vector(3, 2.5, 6), .5);
    float td = sdTorus(p-vector(0, .5, 6), vector(1.5, .4, 0.));
    float bd = dBox(p-vector(-3.5, 1, 6), vector(1, .75, 1));
    float cyld = sdCylinder(p, vector(0, .3, 3), vector(3, .3, 5), .3);

    float d = min(cd, planeDist);
    d = min(d, td);
    d = min(d, bd);
    d = min(d, cyld);
    return d;
}

float RayMarch(vector viewOrigin, vector rd, int MAX_STEPS, float MAX_DIST, float SURF_DIST) {
    float dO=0.;
    for(int i=0; i<MAX_STEPS; i++) {
        vector p = viewOrigin + rd*dO;
        float dS = GetDist(p);
        dO += dS;
        if(dO>MAX_DIST || dS<SURF_DIST) break;
    }
    return dO;
}


shader mainImage(vector viewOrigin = vector(0,5,0), vector LightPos = vector(0,10,0), int MAX_STEPS = 100,
                 float MAX_DIST = 100, float SURF_DIST = 0.001, float shaddist = 0.001,
                 float shadhardness = 5.0, float shadintensity = 10.0,
                 // outputs
                 output color fragColor = 0, output vector surfPosition = 0, output vector surfNormal = 0)
{
    vector rd = -vector(I[1],I[2],I[0]);
    float d = RayMarch(viewOrigin, rd, MAX_STEPS, MAX_DIST, SURF_DIST);
    vector p = viewOrigin + rd * d;    
    vector l = normalize(LightPos-p);
    
    float dsurpo = GetDist(p);

    // surface normals
    vector e = vector(.001, .0, .0);
    vector n = dsurpo - vector(
        GetDist(p-vector(e[0],  e[1],  e[1])),
        GetDist(p-vector(e[1],  e[0],  e[1])),
        GetDist(p-vector(e[1],  e[1],  e[0])));
    vector snor = normalize(n);

    // light
    float dif = clamp(dot(snor, l), 0., 1.);
    

    // Softshadow
    float k = shadhardness;
    float res = 1.0;
    float t = shaddist;
    for( int i=0; i<50; i++ )
    {
        float h = GetDist( p + l*t );
        res = min( res, k*h/t );
        t += clamp( h, 0.02, 0.20 );
        if( res<0.005 || t>MAX_DIST ) break;
    }
    float Softshadow = clamp( res, 0.0, 1.0);
    
    dif *= max(0.0, pow(Softshadow*0.5+0.5, shadintensity));


    // outputs
    fragColor = color(dif, dif, dif);
    surfPosition = p;
    surfNormal = snor;
}