WoodNeck / raytracing_practice

https://woodneck.github.io/raytracing_practice/index.html
0 stars 0 forks source link

Material (Lambert / Metal) #3

Open WoodNeck opened 5 years ago

WoodNeck commented 5 years ago

image

#define FLT_MAX 3.402823466e+38
precision mediump float;
uniform vec4 uScreenSize;
uniform float uTime;

const int NUM_OBJECTS = 4;

const int LAMBERTIAN = 0;
const int METAL = 1;

struct ray {
    vec3 o, d;
};
struct record {
    float t;
    vec3 p, n;
};
struct material {
    int type;
    vec3 albedo;
};
struct hitable {
    vec3 c;
    float r;
    material mat;
};

float rand(vec2 co) {
    return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453) * 2. - 1.;
}

vec3 randomInUnitSphere(float sd) {
    return vec3(
        rand(vec2(sd += .1, sd += .1)),
        rand(vec2(sd += .1, sd += .1)),
        rand(vec2(sd += .1, sd += .1))
    );
}

vec3 rayDirection(float fov, vec2 size, vec2 fragCoord) {
    vec2 xy = fragCoord - size / 2.0;
    float z = size.y / tan(radians(fov) / 2.0);

    return normalize(vec3(xy, -z));
}

bool sphere(hitable sp, ray r, float tMin, inout record hr, out material mat) {
    vec3 oc = r.o - sp.c;
    float a = dot(r.d, r.d);
    float b = dot(oc, r.d);
    float c = dot(oc, oc) - sp.r * sp.r;

    float d = b * b - a * c;
    if (d < 0.) return false;

    float s = sqrt(d);

    float val = (-b - s) / a;
    if (val > tMin && val < hr.t) {
        hr.p = r.o + val * r.d;
        hr.n = (hr.p - sp.c) / sp.r;
        hr.t = val;
        mat = sp.mat;
        return true;
    }
    val = (-b + s) / a;
    if (val > tMin && val < hr.t) {
        hr.p = r.o + val * r.d;
        hr.n = (hr.p - sp.c) / sp.r;
        hr.t = val;
        mat = sp.mat;
        return true;
    }

    return false;
}

bool worldHit(ray r, float tMin, float tMax, hitable[NUM_OBJECTS] sp, out record rec, out material mat) {
    bool hit = false;
    rec.t = tMax;

    for (int i = 0; i < NUM_OBJECTS; i++) {
        hit = sphere(sp[i], r, tMin, rec, mat) || hit;
    }

    return hit;
}

bool scatLamb(inout ray r, record rec, float sd) {
    vec3 t = normalize(rec.n + randomInUnitSphere(sd));
    r.o = rec.p;
    r.d = t;
    return true;
}

bool scatMetal(inout ray r, record rec) {
    vec3 reflected = reflect(normalize(r.d), rec.n);
    r.o = rec.p;
    r.d = reflected;
    return (dot(reflected, rec.n) > 0.);
}

vec3 color(ray r, hitable[NUM_OBJECTS] sp, float sd) {
    const int MAX_RECURSION = 50;
    float tMax = FLT_MAX;
    vec3 col = vec3(1);

    record hr;

    for (int i = 0; i < MAX_RECURSION; i++) {
        material mat;
        if (worldHit(r, 0.001, FLT_MAX, sp, hr, mat)) {
            bool scat = mat.type == LAMBERTIAN
                ? scatLamb(r, hr, sd)
                : scatMetal(r, hr);
            if (scat) {
                col *= mat.albedo;
            } else {
                return vec3(0, 0, 0);
            }
        } else {
            float t = .5 * r.d.y + .5;
            col *= mix(vec3(1), vec3(.5, .7, 1.), t);
            return col;
        }
    }
    return col;
}

void main() {
    vec3 eye = vec3(0., 0., 5.);
    const int ns = 100;

    hitable spheres[NUM_OBJECTS];
    spheres[0] = hitable(vec3(0, 0, -1), .5, material(LAMBERTIAN, vec3(.8, .3, .3)));
    spheres[1] = hitable(vec3(0, -100.5, -1), 100., material(LAMBERTIAN, vec3(.8, .8, 0)));
    spheres[2] = hitable(vec3(1, 0, -1), .5, material(METAL, vec3(.8, .6, .2)));
    spheres[3] = hitable(vec3(-1, 0, -1), .5, material(METAL, vec3(.8, .8, .8)));

    float sd = rand(gl_FragCoord.xy);
    vec3 col = vec3(0, 0, 0);
    float ins = 1. / float(ns);
    for (int i = 0; i < ns; i++) {
        float rv = rand(gl_FragCoord.xy + float(i) * ins);
        vec3 rd = rayDirection(45., uScreenSize.xy, gl_FragCoord.xy + rv);
        ray r = ray(eye, rd);
        col += color(r, spheres, sd);
        sd += 0.1;
    }
    col = col * ins;
    col = vec3(sqrt(col[0]), sqrt(col[1]), sqrt(col[2]));
    gl_FragColor = vec4(col, 1);
}