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";