Skip to main content

BRDF

Constant BRDF 

Source
pub const BRDF: &str = "const FILM_SAMPLES: u32 = 24u;\nconst FILM_NU: f32 = 0.0013513514;\nconst FILM_STEP: f32 = 5.2728312e-05;\nconst FILM_WEIGHTS = array<vec3f, 24>(\n    vec3f(0.00049457996, 6.4814856e-05, 0.00010619715),\n    vec3f(0.002711246, -0.0001910701, -0.00037234355),\n    vec3f(0.01515967, 0.00028906547, 0.00012810956),\n    vec3f(0.06871473, -0.006184027, -0.00132862),\n    vec3f(0.19734336, -0.023679964, -0.0021775665),\n    vec3f(0.34738973, -0.02425651, -0.0060811765),\n    vec3f(0.3639468, 0.030559337, -0.014212847),\n    vec3f(0.23666404, 0.12368723, -0.022455476),\n    vec3f(0.08203017, 0.19909823, -0.027573477),\n    vec3f(-0.02977581, 0.22957213, -0.027457258),\n    vec3f(-0.091345824, 0.21851267, -0.020553917),\n    vec3f(-0.09862617, 0.16825487, -0.004000803),\n    vec3f(-0.071613036, 0.09727391, 0.021227753),\n    vec3f(-0.047891952, 0.05012592, 0.0553259),\n    vec3f(-0.024773184, 0.0206622, 0.112508),\n    vec3f(-0.01048964, -0.0007772626, 0.18015881),\n    vec3f(0.006274984, -0.01616651, 0.20803724),\n    vec3f(0.017522685, -0.023618592, 0.2038683),\n    vec3f(0.015057241, -0.021199455, 0.17673187),\n    vec3f(0.0156005435, -0.01456651, 0.10748986),\n    vec3f(0.0014033349, -0.00467564, 0.039753698),\n    vec3f(0.004500126, -0.0028086475, 0.014122494),\n    vec3f(-0.0012337026, 0.00018912496, 0.0036677718),\n    vec3f(0.00093538617, -0.00016521492, 0.0030877078),\n);\nconst FILM_EDGE: f32 = 0.46266436;\n\nstruct FilmStack {\n    lift: vec2f,\n    floor: vec2f,\n    cosine: vec2f,\n    sine: vec2f,\n}\n\nfn film_amplitudes(cos_i: f32, na: f32, nb: f32) -> vec3f {\n    let sin2 = (na / nb) * (na / nb) * (1.0 - cos_i * cos_i);\n    if (sin2 >= 1.0) {\n        return vec3f(1.0, 1.0, 0.0);\n    }\n    let cos_t = sqrt(1.0 - sin2);\n    let rs = (na * cos_i - nb * cos_t) / (na * cos_i + nb * cos_t);\n    let rp = (nb * cos_i - na * cos_t) / (nb * cos_i + na * cos_t);\n    return vec3f(rs, rp, cos_t);\n}\n\nfn film_stack(r12: vec2f, r23_re: vec2f, r23_im: vec2f) -> FilmStack {\n    let r23 = r23_re * r23_re + r23_im * r23_im;\n    let floor = vec2f(1.0) + r12 * r12 * r23;\n    return FilmStack(0.5 * (r12 * r12 + r23 - floor), floor, 2.0 * r12 * r23_re, -2.0 * r12 * r23_im);\n}\n\nfn film_pair(z: vec2f, stack: FilmStack) -> f32 {\n    return dot(stack.lift, vec2f(1.0) / (stack.floor + stack.cosine * z.x + stack.sine * z.y));\n}\n\nfn film_sum(path: f32, red: FilmStack, green: FilmStack, blue: FilmStack) -> vec3f {\n    let turn = vec2f(cos(path * FILM_STEP), sin(path * FILM_STEP));\n    var z = vec2f(cos(path * FILM_NU), sin(path * FILM_NU));\n    var weights = FILM_WEIGHTS;\n    var total = vec3f(1.0);\n    for (var k = 0u; k < FILM_SAMPLES; k++) {\n        total += weights[k] * vec3f(film_pair(z, red), film_pair(z, green), film_pair(z, blue));\n        z = vec2f(z.x * turn.x - z.y * turn.y, z.x * turn.y + z.y * turn.x);\n    }\n    return clamp(total, vec3f(0.0), vec3f(1.0));\n}\n\nfn film_sum_one(path: f32, stack: FilmStack) -> vec3f {\n    let turn = vec2f(cos(path * FILM_STEP), sin(path * FILM_STEP));\n    var z = vec2f(cos(path * FILM_NU), sin(path * FILM_NU));\n    var weights = FILM_WEIGHTS;\n    var total = vec3f(1.0);\n    for (var k = 0u; k < FILM_SAMPLES; k++) {\n        total += weights[k] * film_pair(z, stack);\n        z = vec2f(z.x * turn.x - z.y * turn.y, z.x * turn.y + z.y * turn.x);\n    }\n    return clamp(total, vec3f(0.0), vec3f(1.0));\n}\n\nfn film_dielectric(cosine: f32, thickness: f32, film_ior: f32, substrate_ior: f32) -> vec3f {\n    let nf = max(film_ior, 1.01);\n    let first = film_amplitudes(clamp(cosine, 0.0, 1.0), 1.0, nf);\n    let second = film_amplitudes(first.z, nf, max(substrate_ior, 1.0));\n    if (second.z <= 0.0) {\n        return vec3f(1.0);\n    }\n    let normal = cosine >= 1.0;\n    let stack = film_stack(select(first.xy, first.xx, normal), select(second.xy, second.xx, normal), vec2f(0.0));\n    return film_sum_one(12.566370614 * nf * thickness * first.z, stack);\n}\n\nfn film_conductor_ior(f0: f32) -> vec2f {\n    let r = clamp(f0, 0.0, 0.999);\n    let edge = mix(clamp(f0, 0.0, 1.0), 1.0, FILM_EDGE);\n    let root = sqrt(r);\n    let n = mix((1.0 + root) / (1.0 - root), (1.0 - r) / (1.0 + r), edge);\n    let k = sqrt(max((r * (n + 1.0) * (n + 1.0) - (n - 1.0) * (n - 1.0)) / (1.0 - r), 0.0));\n    return vec2f(n, k);\n}\n\nfn film_mul(a: vec2f, b: vec2f) -> vec2f {\n    return vec2f(a.x * b.x - a.y * b.y, a.x * b.y + a.y * b.x);\n}\n\nfn film_div(a: vec2f, b: vec2f) -> vec2f {\n    return vec2f(a.x * b.x + a.y * b.y, a.y * b.x - a.x * b.y) / dot(b, b);\n}\n\nfn film_root(a: vec2f) -> vec2f {\n    let size = length(a);\n    let re = sqrt(max(0.5 * (size + a.x), 0.0));\n    let im = sqrt(max(0.5 * (size - a.x), 0.0));\n    return vec2f(re, select(im, -im, a.y < 0.0));\n}\n\nfn film_metal_stack(r12: vec2f, cos_f: f32, nf: f32, f0: f32) -> FilmStack {\n    let metal = film_conductor_ior(f0);\n    let square = film_mul(metal, metal);\n    let u = film_root(square - vec2f(nf * nf * (1.0 - cos_f * cos_f), 0.0));\n    let across = vec2f(nf * cos_f, 0.0);\n    let rs = film_div(across - u, across + u);\n    if (cos_f >= 1.0) {\n        return film_stack(r12.xx, vec2f(rs.x), vec2f(rs.y));\n    }\n    let tilted = square * cos_f;\n    let rp = film_div(tilted - nf * u, tilted + nf * u);\n    return film_stack(r12, vec2f(rs.x, rp.x), vec2f(rs.y, rp.y));\n}\n\nfn film_conductor(cosine: f32, thickness: f32, film_ior: f32, f0: vec3f) -> vec3f {\n    let nf = max(film_ior, 1.01);\n    let first = film_amplitudes(clamp(cosine, 0.0, 1.0), 1.0, nf);\n    let red = film_metal_stack(first.xy, first.z, nf, f0.r);\n    let green = film_metal_stack(first.xy, first.z, nf, f0.g);\n    let blue = film_metal_stack(first.xy, first.z, nf, f0.b);\n    return film_sum(12.566370614 * nf * thickness * first.z, red, green, blue);\n}\n\nconst PI: f32 = 3.141592653589793;\nconst RGB_WAVELENGTHS_NM: vec3f = vec3f(640.0, 540.0, 455.0);\nconst GLASS_IOR: f32 = 1.56;\nconst GLASS_DISPERSION_RED: f32 = -0.016;\nconst GLASS_DISPERSION_BLUE: f32 = 0.022;\nconst WATER_IOR: f32 = 1.333;\noverride use_film: bool = true;\n\nstruct Shaded {\n    base: vec3f,\n    roughness: f32,\n    metalness: f32,\n    specular: f32,\n    clearcoat: f32,\n    clearcoat_roughness: f32,\n    sheen: f32,\n    transmission: f32,\n    ior: f32,\n    dispersion: f32,\n    thickness: f32,\n    subsurface: f32,\n    subsurface_tint: vec3f,\n    absorption: f32,\n    thin_film: f32,\n    thin_film_ior: f32,\n    thin_film_amount: f32,\n    emission: vec3f,\n    fresnel_power: f32,\n    normal: vec3f,\n}\n\nfn dielectric_f0(ior: f32) -> f32 {\n    let r = (ior - 1.0) / (ior + 1.0);\n    return r * r;\n}\n\nfn dispersed_ior(ior: f32, dispersion: f32) -> vec3f {\n    return vec3f(ior + GLASS_DISPERSION_RED * dispersion, ior, ior + GLASS_DISPERSION_BLUE * dispersion);\n}\n\nfn schlick(f0: vec3f, cos_theta: f32, power: f32) -> vec3f {\n    let k = pow(1.0 - clamp(cos_theta, 0.0, 1.0), power);\n    return mix(f0, vec3f(1.0), k);\n}\n\nfn fresnel_dielectric(cos_theta: f32, eta_in: f32) -> f32 {\n    let c = clamp(cos_theta, 0.0, 1.0);\n    var eta = eta_in;\n    if (abs(eta) < 1e-4) {\n        eta = 1.0;\n    }\n    let s2 = 1.0 - c * c;\n    let t2 = 1.0 - s2 / (eta * eta);\n    if (t2 <= 0.0) {\n        return 1.0;\n    }\n    let ct = sqrt(t2);\n    let rs = (c - eta * ct) / (c + eta * ct);\n    let rp = (eta * c - ct) / (eta * c + ct);\n    return 0.5 * (rs * rs + rp * rp);\n}\n\nfn fresnel_conductor(cos_theta: f32, eta: f32, k: f32) -> f32 {\n    let c = clamp(cos_theta, 0.0, 1.0);\n    if (c <= 1e-6) {\n        return 1.0;\n    }\n    let cos2 = c * c;\n    let sin2 = 1.0 - cos2;\n    let eta2 = eta * eta;\n    let k2 = k * k;\n    let t0 = eta2 - k2 - sin2;\n    let a2b2 = sqrt(t0 * t0 + 4.0 * eta2 * k2);\n    let t1 = a2b2 + cos2;\n    let a = sqrt(max(0.5 * (a2b2 + t0), 0.0));\n    let t2 = 2.0 * a * c;\n    let rs = (t1 - t2) / (t1 + t2);\n    let t3 = cos2 * a2b2 + sin2 * sin2;\n    let t4 = t2 * sin2;\n    let denom = t3 + t4;\n    var rp = rs;\n    if (abs(denom) > 1e-12) {\n        rp = rs * (t3 - t4) / denom;\n    }\n    return clamp(0.5 * (rs + rp), 0.0, 1.0);\n}\n\nfn ggx_d(nh: f32, a2: f32) -> f32 {\n    let n = max(nh, 0.0);\n    let k = n * n * (a2 - 1.0) + 1.0;\n    return a2 / (PI * k * k);\n}\n\nfn smith_g1(ndot: f32, a2: f32) -> f32 {\n    let n = max(ndot, 0.0);\n    return 2.0 * n / (n + sqrt(a2 + (1.0 - a2) * n * n));\n}\n\nfn smith_g(ndv: f32, ndl: f32, a2: f32) -> f32 {\n    return smith_g1(ndv, a2) * smith_g1(ndl, a2);\n}\n\nfn smith_correlated(ndv: f32, ndl: f32, alpha: f32) -> f32 {\n    let a2 = alpha * alpha;\n    let gv = ndl * sqrt(ndv * ndv * (1.0 - a2) + a2);\n    let gl = ndv * sqrt(ndl * ndl * (1.0 - a2) + a2);\n    return 0.5 / max(gv + gl, 1e-6);\n}\n\nfn alpha_rough(roughness: f32) -> f32 {\n    let rough = clamp(roughness, 0.02, 1.0);\n    return rough * rough;\n}\n\nfn lambert(albedo: vec3f) -> vec3f {\n    return albedo / PI;\n}\n\nfn beer(distance: f32, tint: vec3f, absorption: f32) -> vec3f {\n    if (distance <= 0.0 || absorption <= 0.0) {\n        return vec3f(1.0);\n    }\n    return vec3f(\n        exp(-distance * absorption * max(1.0 - tint.r, 0.0)),\n        exp(-distance * absorption * max(1.0 - tint.g, 0.0)),\n        exp(-distance * absorption * max(1.0 - tint.b, 0.0)),\n    );\n}\n\nfn film_phase(cosine: f32, thickness: f32, n_film: f32) -> vec4f {\n    let sine = sqrt(max(1.0 - cosine * cosine, 0.0));\n    let nf = max(n_film, 1.01);\n    let inner = sqrt(max(1.0 - (sine / nf) * (sine / nf), 0.0));\n    let r12 = (cosine - nf * inner) / (cosine + nf * inner);\n    return vec4f(r12, cos(6.28318530718 * 2.0 * nf * thickness * inner / RGB_WAVELENGTHS_NM));\n}\n\nfn airy(r12: f32, r23: f32, c: f32) -> f32 {\n    let top = r12 * r12 + r23 * r23 + 2.0 * r12 * r23 * c;\n    let bottom = 1.0 + r12 * r12 * r23 * r23 + 2.0 * r12 * r23 * c;\n    if (abs(bottom) <= 1e-12) {\n        return 0.0;\n    }\n    return clamp(top / bottom, 0.0, 1.0);\n}\n\nfn film_rgb(cosine_in: f32, thickness: f32, n_film: f32, n_substrate: f32) -> vec3f {\n    let cosine = clamp(cosine_in, 0.0, 1.0);\n    if (thickness <= 0.0) {\n        let f = fresnel_dielectric(cosine, max(n_substrate, 1.01));\n        return vec3f(f);\n    }\n    let phase = film_phase(cosine, thickness, n_film);\n    let r23 = (max(n_film, 1.01) - max(n_substrate, 1.01)) / (max(n_film, 1.01) + max(n_substrate, 1.01));\n    return vec3f(airy(phase.x, r23, phase.y), airy(phase.x, r23, phase.z), airy(phase.x, r23, phase.w));\n}\n\nfn metal_film(cosine_in: f32, thickness: f32, n_film: f32, f0: vec3f) -> vec3f {\n    let cosine = clamp(cosine_in, 0.0, 1.0);\n    if (thickness <= 0.0) {\n        return f0;\n    }\n    let phase = film_phase(cosine, thickness, n_film);\n    let r23 = -sqrt(clamp(f0, vec3f(0.0), vec3f(0.999)));\n    return vec3f(airy(phase.x, r23.r, phase.y), airy(phase.x, r23.g, phase.z), airy(phase.x, r23.b, phase.w));\n}\n\nfn liquid_dye(amount: f32, dye: vec3f) -> vec3f {\n    return mix(vec3f(1.0), dye, clamp(amount, 0.0, 1.0));\n}\n\nfn refract_dir(incident: vec3f, normal: vec3f, eta: f32) -> vec4f {\n    let cos_i = dot(normal, incident);\n    let k = 1.0 - eta * eta * (1.0 - cos_i * cos_i);\n    if (k < 0.0) {\n        return vec4f(0.0);\n    }\n    let t = eta * cos_i + sqrt(k);\n    return vec4f(incident * eta - normal * t, 1.0);\n}\n\nfn filmed(s: Shaded) -> bool {\n    return use_film && s.thin_film > 0.0 && s.thin_film_amount > 0.0;\n}\n\nfn film_colour(s: Shaded, cos_theta: f32) -> vec3f {\n    if (s.metalness >= 0.5) {\n        let f0 = mix(vec3f(s.specular), s.base, clamp(s.metalness, 0.0, 1.0));\n        return film_conductor(cos_theta, s.thin_film, s.thin_film_ior, f0);\n    }\n    return film_dielectric(cos_theta, s.thin_film, s.thin_film_ior, s.ior);\n}\n\nfn reflection_colour(s: Shaded, cos_theta: f32, ndh_cos: f32) -> vec3f {\n    var film = vec3f(0.0);\n    if (filmed(s)) {\n        film = film_colour(s, cos_theta);\n    }\n    return reflection_filmed(s, ndh_cos, film);\n}\n\nfn reflection_filmed(s: Shaded, ndh_cos: f32, film: vec3f) -> vec3f {\n    var power = s.fresnel_power;\n    if (power <= 0.0) {\n        power = 5.0;\n    }\n    let f0 = mix(vec3f(s.specular), s.base, clamp(s.metalness, 0.0, 1.0));\n    var plain: vec3f;\n    if (s.transmission > 0.0 || s.dispersion != 0.0) {\n        let iors = dispersed_ior(s.ior, s.dispersion);\n        let metal = schlick(s.base, ndh_cos, power);\n        let diel = vec3f(\n            fresnel_dielectric(ndh_cos, iors.r),\n            fresnel_dielectric(ndh_cos, iors.g),\n            fresnel_dielectric(ndh_cos, iors.b),\n        );\n        plain = mix(diel, metal, clamp(s.metalness, 0.0, 1.0));\n    } else if (f0.r <= 0.0 && f0.g <= 0.0 && f0.b <= 0.0) {\n        plain = vec3f(0.0);\n    } else {\n        plain = schlick(f0, ndh_cos, power);\n    }\n    if (filmed(s)) {\n        plain = mix(plain, film, clamp(s.thin_film_amount, 0.0, 1.0));\n    }\n    return plain;\n}\n\nfn ggx_spec(ndv: f32, ndl: f32, ndh: f32, a2: f32, fresnel: vec3f) -> vec3f {\n    return fresnel * ggx_d(ndh, a2) * smith_g(ndv, ndl, a2) / (4.0 * ndv * ndl);\n}\n\nfn eval(s: Shaded, wo: vec3f, wi: vec3f) -> vec3f {\n    var film = vec3f(0.0);\n    if (filmed(s)) {\n        film = film_colour(s, clamp(wo.z, 0.0, 1.0));\n    }\n    return eval_filmed(s, wo, wi, film);\n}\n\nfn eval_filmed(s: Shaded, wo: vec3f, wi: vec3f, film: vec3f) -> vec3f {\n    let ndv = wo.z;\n    if (ndv <= 1e-5) {\n        return vec3f(0.0);\n    }\n    let ndl = wi.z;\n    if (abs(ndl) <= 1e-8) {\n        return vec3f(0.0);\n    }\n    let metal = clamp(s.metalness, 0.0, 1.0);\n    let trans = clamp(s.transmission, 0.0, 1.0);\n    let sss = clamp(s.subsurface, 0.0, 1.0) * (1.0 - trans);\n    let sheen = clamp(s.sheen, 0.0, 1.0);\n    let sheen_cover = sheen * pow(1.0 - ndv, 4.0);\n    let view = clamp(ndv, 0.0, 1.0);\n    if (ndl < 0.0) {\n        let fresnel = reflection_filmed(s, view, film);\n        let transmitted = vec3f(1.0) - fresnel;\n        let back = (1.0 - metal) * sss + trans * (1.0 - metal);\n        let tint = beer(s.thickness, s.subsurface_tint, s.absorption);\n        return s.base * tint * transmitted * back / PI;\n    }\n    let h = normalize(wo + wi);\n    let ndh = max(h.z, 0.0);\n    let vdh = max(dot(wo, h), 0.0);\n    let fresnel = reflection_filmed(s, vdh, film);\n    let a = alpha_rough(s.roughness);\n    let spec = ggx_spec(ndv, ndl, ndh, a * a, fresnel);\n    let transmitted = vec3f(1.0) - fresnel;\n    let body = (1.0 - metal) * (1.0 - trans) * (1.0 - sss);\n    let diffuse_w = body * (1.0 - sheen_cover);\n    let sheen_w = body * sheen_cover * pow(1.0 - ndh, 5.0);\n    var colour = spec + s.base * transmitted * (diffuse_w + sheen_w) / PI;\n    let coat = clamp(s.clearcoat, 0.0, 1.0);\n    if (coat > 0.0) {\n        let coat_a = alpha_rough(s.clearcoat_roughness);\n        let coat_f = schlick(vec3f(0.04), vdh, 5.0);\n        let coat_spec = ggx_spec(ndv, ndl, ndh, coat_a * coat_a, coat_f);\n        let fc = schlick(vec3f(0.04), view, 5.0).r;\n        colour = coat_spec * coat + colour * (1.0 - coat * fc);\n    }\n    return colour;\n}\n";