// The material half of the OpenPBR shader library's preamble.
//
// The lobe code reads its parameters as bare globals -- `base_metalness`,
// `coat_roughness` and the rest -- because the reference GLSL it was ported
// from reads uniforms of those names, and keeping the names made the port
// checkable line by line. So the shading pass copies one material out of the
// table into `mat`, calls `unpack_material()`, and from there every lobe finds
// what it needs without being handed it.
//
// This file is ours; everything after it in the program is the library as it
// arrived. Nothing here refers to a camera, a light, or a scene: what the
// library needs is a material and the energy tables, and that is all.
struct Material {
base_color_weight: vec4<f32>,
specular_color_weight: vec4<f32>,
transmission_color_depth: vec4<f32>,
transmission_scatter_aniso: vec4<f32>,
subsurface_color_weight: vec4<f32>,
subsurface_radius_scale_radius: vec4<f32>,
coat_color_weight: vec4<f32>,
fuzz_color_weight: vec4<f32>,
emission_color_luminance: vec4<f32>,
base_diffuse_roughness: f32,
base_metalness: f32,
specular_roughness: f32,
specular_anisotropy: f32,
specular_ior: f32,
transmission_weight: f32,
transmission_dispersion_abbe_number: f32,
transmission_dispersion_scale: f32,
subsurface_anisotropy: f32,
coat_roughness: f32,
coat_anisotropy: f32,
coat_ior: f32,
coat_darkening: f32,
fuzz_roughness: f32,
thin_film_weight: f32,
thin_film_thickness: f32,
thin_film_ior: f32,
geometry_opacity: f32,
geometry_thin_walled: u32,
normal_map_y_along_v: u32,
base_color_layer: f32,
roughness_layer: f32,
metalness_layer: f32,
normal_layer: f32,
};
// The material being shaded, copied out of the table at entry.
var<private> mat: Material;
var<private> base_weight: f32;
var<private> base_color: vec3<f32>;
var<private> base_diffuse_roughness: f32;
var<private> base_metalness: f32;
var<private> specular_weight: f32;
var<private> specular_color: vec3<f32>;
var<private> specular_roughness: f32;
var<private> specular_anisotropy: f32;
var<private> specular_ior: f32;
var<private> transmission_weight: f32;
var<private> transmission_color: vec3<f32>;
var<private> transmission_depth: f32;
var<private> transmission_scatter: vec3<f32>;
var<private> transmission_scatter_anisotropy: f32;
var<private> transmission_dispersion_abbe_number: f32;
var<private> transmission_dispersion_scale: f32;
var<private> subsurface_weight: f32;
var<private> subsurface_color: vec3<f32>;
var<private> subsurface_radius: f32;
var<private> subsurface_radius_scale: vec3<f32>;
var<private> subsurface_anisotropy: f32;
var<private> coat_weight: f32;
var<private> coat_color: vec3<f32>;
var<private> coat_roughness: f32;
var<private> coat_anisotropy: f32;
var<private> coat_ior: f32;
var<private> coat_darkening: f32;
var<private> fuzz_weight: f32;
var<private> fuzz_color: vec3<f32>;
var<private> fuzz_roughness: f32;
var<private> thin_film_weight: f32;
var<private> thin_film_thickness: f32;
var<private> thin_film_ior: f32;
var<private> emission_luminance: f32;
var<private> emission_color: vec3<f32>;
var<private> geometry_opacity: f32;
var<private> geometry_thin_walled: bool;
// The only two the library wants that are not the material's own. Both are
// switches the path tracer exposed; here they are constants, because a
// forward pass has no wireframe mode and always wants its energy back.
var<private> energy_compensation: bool = true;
var<private> wireframe: bool = false;
fn unpack_material() {
base_color = mat.base_color_weight.xyz;
base_weight = mat.base_color_weight.w;
specular_color = mat.specular_color_weight.xyz;
specular_weight = mat.specular_color_weight.w;
transmission_color = mat.transmission_color_depth.xyz;
transmission_depth = mat.transmission_color_depth.w;
transmission_scatter = mat.transmission_scatter_aniso.xyz;
transmission_scatter_anisotropy = mat.transmission_scatter_aniso.w;
subsurface_color = mat.subsurface_color_weight.xyz;
subsurface_weight = mat.subsurface_color_weight.w;
subsurface_radius_scale = mat.subsurface_radius_scale_radius.xyz;
subsurface_radius = mat.subsurface_radius_scale_radius.w;
coat_color = mat.coat_color_weight.xyz;
coat_weight = mat.coat_color_weight.w;
fuzz_color = mat.fuzz_color_weight.xyz;
fuzz_weight = mat.fuzz_color_weight.w;
emission_color = mat.emission_color_luminance.xyz;
emission_luminance = mat.emission_color_luminance.w;
base_diffuse_roughness = mat.base_diffuse_roughness;
base_metalness = mat.base_metalness;
specular_roughness = mat.specular_roughness;
specular_anisotropy = mat.specular_anisotropy;
specular_ior = mat.specular_ior;
transmission_weight = mat.transmission_weight;
transmission_dispersion_abbe_number = mat.transmission_dispersion_abbe_number;
transmission_dispersion_scale = mat.transmission_dispersion_scale;
subsurface_anisotropy = mat.subsurface_anisotropy;
coat_roughness = mat.coat_roughness;
coat_anisotropy = mat.coat_anisotropy;
coat_ior = mat.coat_ior;
coat_darkening = mat.coat_darkening;
fuzz_roughness = mat.fuzz_roughness;
thin_film_weight = mat.thin_film_weight;
thin_film_thickness = mat.thin_film_thickness;
thin_film_ior = mat.thin_film_ior;
geometry_opacity = mat.geometry_opacity;
geometry_thin_walled = mat.geometry_thin_walled != 0u;
}
// ---------------------------------------------------------------------------
// The energy compensation tables: eight curves of `ENERGY_BINS` samples for
// every material, in the order `EnergyTables` writes them. See `energy.rs`.
// ---------------------------------------------------------------------------
const ENERGY_BINS: u32 = 32u;
const ENERGY_METAL: u32 = 0u;
const ENERGY_DIELECTRIC_RT_OUT: u32 = 1u;
const ENERGY_DIELECTRIC_RT_IN: u32 = 2u;
const ENERGY_DIELECTRIC_R_OUT: u32 = 3u;
const ENERGY_DIELECTRIC_R_IN: u32 = 4u;
const ENERGY_DIELECTRIC_T_OUT: u32 = 5u;
const ENERGY_DIELECTRIC_T_IN: u32 = 6u;
const ENERGY_COAT_R: u32 = 7u;
/// How many floats one material's tables occupy.
const ENERGY_STRIDE: u32 = 8u * ENERGY_BINS;
// Where this material's tables start, set alongside `mat`.
var<private> energy_base: u32;
// ---------------------------------------------------------------------------
// What the shading pass binds. The library reads only `energy_tables`; the
// other two are here because a binding declared next to the struct it carries
// is a binding that cannot drift from it.
// ---------------------------------------------------------------------------
@group(0) @binding(4) var<storage, read> materials: array<Material>;
@group(0) @binding(5) var<storage, read> energy_tables: array<f32>;
/// One local light: a place, a reach, a colour, and a cone if it has one.
/// Mirrors `GpuLight`.
struct LocalLight {
// xyz where it is, w how far it reaches.
position_range: vec4<f32>,
color: vec4<f32>,
// xyz the way a spot points, w the cosine of its outer half-angle, or
// `NOT_A_CONE` for a light that goes everywhere.
direction_outer: vec4<f32>,
// x the cosine of the inner half-angle.
cone: vec4<f32>,
};
const NOT_A_CONE: f32 = -2.0;
@group(0) @binding(6) var<storage, read> lights: array<LocalLight>;
// The cluster grid, filled by the culling pass: how many lights reached each
// cell, and which. See `clustered.rs`.
@group(0) @binding(7) var<storage, read> cluster_counts: array<u32>;
@group(0) @binding(8) var<storage, read> cluster_lights: array<u32>;
const CLUSTER_X: u32 = 16u;
const CLUSTER_Y: u32 = 9u;
const CLUSTER_Z: u32 = 24u;
const MAX_LIGHTS_PER_CLUSTER: u32 = 64u;
/// Take up material `index` and everything that goes with it.
///
/// The two globals set here that are not the material's own are the reason
/// this is a function rather than three lines at the top of a shading pass:
///
/// - `wavelength_nm` reaches `specular_ior_dispersive`, which divides by its
/// square. Left at zero -- which is what a `var<private>` starts as -- the
/// IOR comes back infinite, `eta_s` turns that into a NaN, and the NaN goes
/// through every lobe and out into the frame as a black surface. Nothing
/// reports it. 550 nm is the middle of the visible band, and is what the
/// path tracer uses when it is not tracing a spectral path.
/// - `rndSeed` is only read where a lobe is *sampled*, which a forward pass
/// never does, but a zero seed is a degenerate one and costs nothing to
/// avoid.
fn openpbr_begin(index: u32, seed: u32) {
mat = materials[index];
energy_base = index * ENERGY_STRIDE;
wavelength_nm = 550.0;
rndSeed = seed;
unpack_material();
}