Skip to content

Instantly share code, notes, and snippets.

@simonbroggi
Created November 22, 2019 11:01
Show Gist options
  • Save simonbroggi/d2d8d8ca9b616f82666f52d7e5af5ef4 to your computer and use it in GitHub Desktop.
Save simonbroggi/d2d8d8ca9b616f82666f52d7e5af5ef4 to your computer and use it in GitHub Desktop.

Revisions

  1. simonbroggi created this gist Nov 22, 2019.
    125 changes: 125 additions & 0 deletions AmbientOcclusionDirection.osl
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,125 @@
    /*
    * Ambient Occlusion and Direction.
    *
    * Calculates AO and the average direction where the ambient light came from.
    *
    * Original AO code from https://github.com/sambler/osl-shaders/blob/master/ramps/BaAmbientOcclusion/BaAmbientOcclusion.osl
    *
    */

    void rng_seed(output int rng, int seed)
    {
    int chash = seed;
    if (chash == 0) chash = 1;
    rng = chash * 891694213;
    }

    float rng_uniform(output int rng)
    {
    float res = rng / float(2137483647) * 0.5 + 0.5;
    rng *= 891694213;
    return res;
    }

    void to_unit_disk(float x, float y, output float x_out, output float y_out)
    {
    float r, phi;
    float a = 2.0 * x - 1.0;
    float b = 2.0 * y - 1.0;

    if(a > -b) {
    if(a > b) {
    r = a;
    phi = M_PI_4 *(b/a);
    }
    else {
    r = b;
    phi = M_PI_4 *(2.0 - a/b);
    }
    }
    else {
    if(a < b) {
    r = -a;
    phi = M_PI_4 *(4.0 + b/a);
    }
    else {
    r = -b;
    if(b != 0.0) phi = M_PI_4 *(6.0 - a/b);
    else phi = 0.0;
    }
    }
    x_out = r * cos(phi);
    y_out = r * sin(phi);
    }

    void make_orthonormals(vector N, output vector a, output vector b)
    {
    if(N[0] != N[1] || N[0] != N[2]) a = cross(vector(1, 1, 1), N);
    else a = cross(vector(-1, 1, 1), N);

    a = normalize(a);
    b = cross(N, a);
    }

    vector sample_cos_hemisphere(vector N, float randu, float randv)
    {
    vector T, B;

    make_orthonormals(N, T, B);
    to_unit_disk(randu, randv, randu, randv);
    float costheta = sqrt(max(1.0 - randu * randu - randv * randv, 0.0));

    return randu * T + randv * B + costheta * N;
    }

    shader AmbientOcclusionAndDirection(
    normal Normal = N,
    float Distance = 1,
    int Samples = 8,
    output float AmbientAmount = 1,
    output normal NonOccludedDirection = Normal )
    {
    int i, rng, hits = 0;
    float f, randu, randv, ray_t;
    vector ray_P, ray_R;

    f = fmod(cellnoise(P*123456.0), 1.0);
    rng_seed(rng, int(f * 2137483647));

    vector accumulatedNonOccludedNormals = vector(0,0,0);

    for(i = 0; i < Samples; i++) {
    randu = rng_uniform(rng);
    randv = rng_uniform(rng);

    ray_R = sample_cos_hemisphere(Normal, randu, randv);

    if(trace(P, ray_R, "maxdist", Distance))
    {
    hits++;
    }
    else
    {
    accumulatedNonOccludedNormals += ray_R;
    }
    }

    if(Samples - hits > 0)
    {
    // ambient light rays found.
    // calculate the amount of ambient light and the direction where most of it came from

    AmbientAmount = 1.0 - (((float)hits)/Samples);

    NonOccludedDirection = normalize(accumulatedNonOccludedNormals);
    }
    else
    {
    // every ray has hit some geometry. no ambient light detected.

    AmbientAmount = 0.0;

    // should the resulting direction be equal to Normal, or zero???
    // NonOccludedDirection = vector(0,0,0);
    }
    }