Constant MAPS_WGSL
Source pub const MAPS_WGSL: &str = r#"
struct MapMaterial {
indices: vec4i,
factors: vec4f,
age_a: vec4f,
age_b: vec4f,
seeds: vec4u,
layer0: vec4f,
layer1: vec4f,
layer2: vec4f,
layer3: vec4f,
content: vec4f,
content_extra: vec4f,
packed: vec4f,
atlas: vec4f,
params: array<vec4f, 16>,
}
struct MapResult {
base: vec3f,
roughness: f32,
metalness: f32,
normal: vec3f,
coat_roughness: f32,
thin_film: f32,
}
@group(3) @binding(0) var map_base: texture_2d_array<f32>;
@group(3) @binding(1) var map_normal: texture_2d_array<f32>;
@group(3) @binding(2) var map_roughness: texture_2d_array<f32>;
@group(3) @binding(3) var map_metal: texture_2d_array<f32>;
@group(3) @binding(4) var map_sampler: sampler;
@group(3) @binding(5) var<storage, read> map_materials: array<MapMaterial>;
override use_maps: bool = true;
override use_detail: bool = true;
override use_ageing: bool = true;
override use_fine_noise: bool = false;
override use_uv1: bool = true;
override use_lacquer: bool = true;
override use_procedural_lacquer: bool = false;
override use_scratch: bool = true;
override use_grime: bool = true;
override use_wood: bool = true;
override use_wall: bool = true;
override use_fibre: bool = true;
override use_crinkle: bool = true;
override use_procedural_detail: bool = false;
var<private> map_ink_base: vec4f;
var<private> map_pixel: f32;
var<private> map_dx: vec3f;
var<private> map_dy: vec3f;
var<private> map_planes: vec3f = vec3f(0.0, 0.0, 1.0);
var<private> map_gain: f32 = 1.0;
const tile_cells: f32 = 64.0;
const lacquer_broad_range: f32 = 2.5;
const lacquer_fine_range: f32 = 1.5;
const lacquer_blend: f32 = 0.5;
const map_ring_mean: f32 = 0.25;
const map_line_mean: f32 = 0.0897;
const map_wear_mean: f32 = 0.3814;
const map_speck_mean: f32 = 0.0015;
fn map_footprint(position: vec3f) {
let dx = dpdx(position);
let dy = dpdy(position);
map_pixel = max(length(dx), length(dy));
if ((use_lacquer && !use_procedural_lacquer) || map_tiled()) {
map_dx = dx;
map_dy = dy;
let face = cross(dx, dy);
let size = length(face);
var w = vec3f(0.0, 0.0, 1.0);
if (size > 0.0) {
w = max(abs(face) / size - vec3f(lacquer_blend), vec3f(0.0));
w = w / (w.x + w.y + w.z);
}
map_planes = w;
map_gain = inverseSqrt(dot(w, w));
}
}
fn map_tiled() -> bool {
return !use_procedural_detail && (use_scratch || use_wall || use_fibre || use_crinkle);
}
fn lacquer_plane(at: vec2f, dx: vec2f, dy: vec2f, layer: i32, fine_scale: f32) -> vec2f {
let broad = textureSampleGrad(map_metal, map_sampler, at, layer, dx, dy).rg;
let fine = textureSampleGrad(map_metal, map_sampler, at * fine_scale, layer, dx * fine_scale, dy * fine_scale).ba;
return (broad * 2.0 - 1.0) * lacquer_broad_range + (fine * 2.0 - 1.0) * lacquer_fine_range;
}
fn map_lacquer(p: vec3f, amp: f32, tile: f32, a: vec4f, b: vec4f, d: vec4f) -> vec3f {
if (use_procedural_lacquer || tile < 0.0 || d.w == 0.0) {
return value_noise3_gradient(p, a.x) * amp + value_noise3_gradient(p, a.y) * (amp * a.w) + value_noise3_gradient(p, a.z) * (amp * b.x);
}
let layer = i32(tile);
let w = map_planes;
var g = vec3f(0.0);
if (w.z > 0.0) {
g += vec3f(lacquer_plane(p.xy, map_dx.xy, map_dy.xy, layer, d.z), 0.0) * w.z;
}
if (w.x > 0.0) {
g += vec3f(0.0, lacquer_plane(p.yz + vec2f(0.31, 0.67), map_dx.yz, map_dy.yz, layer, d.z)) * w.x;
}
if (w.y > 0.0) {
let t = lacquer_plane(p.zx + vec2f(0.53, 0.19), map_dx.zx, map_dy.zx, layer, d.z);
g += vec3f(t.y, 0.0, t.x) * w.y;
}
return g * (amp * map_gain);
}
fn map_weight(rate: f32) -> f32 {
if (use_fine_noise) {
return 1.0;
}
return octave_weight(map_pixel * rate);
}
fn map_faded(mean: f32, value: f32, weight: f32) -> f32 {
return select(mix(mean, value, weight), value, weight >= 1.0);
}
fn map_noise(q: vec3f, rate: f32) -> f32 {
let weight = map_weight(rate);
if (weight <= 0.0) {
return 0.5;
}
return map_faded(0.5, value_noise3(q, false), weight);
}
fn tile_pair(at: vec2f, dx: vec2f, dy: vec2f, layer: i32, high: bool) -> vec2f {
let s = 1.0 / tile_cells;
let t = textureSampleGrad(map_metal, map_sampler, at * s, layer, dx * s, dy * s);
return select(t.xy, t.zw, high);
}
fn tile_slice(pair: vec2f, depth: f32) -> f32 {
let k = floor(depth);
let w = fade_cubic(depth - k);
let odd = k - 2.0 * floor(k * 0.5) > 0.5;
return select(mix(pair.x, pair.y, w), mix(pair.y, pair.x, w), odd);
}
fn tile_flat(at: vec2f, dx: vec2f, dy: vec2f, depth: f32, tile: f32, octave: u32) -> f32 {
return tile_slice(tile_pair(at, dx, dy, i32(tile) + 1 + i32(octave >> 1u), (octave & 1u) == 1u), depth);
}
fn tile_noise(q: vec3f, rate: vec3f, tile: f32, octave: u32) -> f32 {
if (map_weight(max(rate.x, max(rate.y, rate.z))) <= 0.0) {
return 0.5;
}
let dx = map_dx * rate;
let dy = map_dy * rate;
let w = map_planes;
var sum = 0.0;
if (w.z > 0.0) {
sum += (tile_flat(q.xy, dx.xy, dy.xy, q.z, tile, octave) - 0.5) * w.z;
}
if (w.x > 0.0) {
sum += (tile_flat(q.yz + vec2f(17.0, 41.0), dx.yz, dy.yz, q.x, tile, octave) - 0.5) * w.x;
}
if (w.y > 0.0) {
sum += (tile_flat(q.zx + vec2f(29.0, 7.0), dx.zx, dy.zx, q.y, tile, octave) - 0.5) * w.y;
}
return 0.5 + sum * map_gain;
}
fn tile_gradient(at: vec2f, dx: vec2f, dy: vec2f, layer: i32) -> vec2f {
let s = 1.0 / tile_cells;
let t = textureSampleGrad(map_metal, map_sampler, at * s, layer, dx * s, dy * s).ba;
return (t * 2.0 - 1.0) * lacquer_fine_range;
}
fn tile_wobble(p: vec3f, scale: f32, shift: vec2f, tile: f32) -> vec3f {
if (map_weight(scale) <= 0.0) {
return vec3f(0.0);
}
let q = p * scale;
let dx = map_dx * scale;
let dy = map_dy * scale;
let layer = i32(tile);
let w = map_planes;
var g = vec3f(0.0);
if (w.z > 0.0) {
g += vec3f(tile_gradient(q.xy + shift, dx.xy, dy.xy, layer), 0.0) * w.z;
}
if (w.x > 0.0) {
g += vec3f(0.0, tile_gradient(q.yz + shift.yx, dx.yz, dy.yz, layer)) * w.x;
}
if (w.y > 0.0) {
let t = tile_gradient(q.zx + shift * 1.7, dx.zx, dy.zx, layer);
g += vec3f(t.y, 0.0, t.x) * w.y;
}
return g * map_gain;
}
fn ink_sample(at: vec2f) -> vec4f {
return map_ink_base;
}
fn map_perturb(n: vec3f, g: vec3f) -> vec3f {
return normalize(n - (g - n * dot(g, n)));
}
fn map_scratch_coat(base: vec3f, roughness: f32, amount: f32, position: vec3f, seed: u32) -> vec4f {
if (amount <= 0.0) {
return vec4f(base, roughness);
}
let q = position * 900.0;
let n = value_noise3(vec3f(q.x * 0.02 + f32(seed), q.y, q.z * 0.02), false);
let line = 1.0 - smoothstep(0.0, 0.045, abs(n - 0.5));
let smudge = smoothstep(0.52, 0.78, value_noise3(position * 22.0, false) * 0.65 + value_noise3(position * 61.0, false) * 0.35);
let cell = vec3i(floor(position * 2600.0));
let speck = select(0.0, 1.0, hash3_xor(vec3i(cell.x, cell.y, cell.z ^ i32(seed))) > 0.9985);
let cover = min(amount, 1.0);
let breakup = clamp(smudge * 0.55 + line * 0.35 + speck, 0.0, 1.0) * cover;
let rough2 = roughness * roughness;
let mixed = mix(rough2, min(rough2 * 4.0 + 0.02, 0.35), breakup);
let albedo = mix(base, base * 0.9 + vec3f(0.06), speck * cover);
return vec4f(albedo, sqrt(mixed));
}
fn map_scratch(base: vec3f, roughness: f32, amount: f32, position: vec3f, seed: u32, coated: bool, tile: f32, scales: vec4f, stretch: f32) -> vec4f {
if (amount <= 0.0) {
return vec4f(base, roughness);
}
let tiled = !use_procedural_detail && tile >= 0.0;
var line = map_line_mean;
let line_weight = map_weight(scales.x);
if (line_weight > 0.0) {
let at = vec3f(position.x * stretch + f32(seed), position.y * scales.x, position.z * stretch);
var n: f32;
if (tiled) {
n = tile_noise(at, vec3f(stretch, scales.x, stretch), tile, 2u);
} else {
n = value_noise3(at, false);
}
line = map_faded(map_line_mean, 1.0 - smoothstep(0.0, 0.045, abs(n - 0.5)), line_weight);
}
var smudge: f32;
if (tiled) {
smudge = smoothstep(0.52, 0.78, tile_noise(position * scales.y, vec3f(scales.y), tile, 3u) * 0.65 + tile_noise(position * scales.z, vec3f(scales.z), tile, 1u) * 0.35);
} else {
smudge = smoothstep(0.52, 0.78, map_noise(position * scales.y, scales.y) * 0.65 + map_noise(position * scales.z, scales.z) * 0.35);
}
var speck = map_speck_mean;
let speck_weight = map_weight(scales.w);
if (speck_weight > 0.0) {
let cell = vec3i(floor(position * scales.w));
speck = map_faded(map_speck_mean, select(0.0, 1.0, hash3_xor(vec3i(cell.x, cell.y, cell.z ^ i32(seed))) > 0.9985), speck_weight);
}
let cover = min(amount, 1.0);
let breakup = clamp(smudge * 0.55 + line * 0.35 + speck, 0.0, 1.0) * cover;
let rough2 = roughness * roughness;
var mixed = mix(rough2, min(rough2 * 3.0 + 0.03, 0.5), breakup);
if (coated) {
mixed = mix(rough2, min(rough2 * 4.0 + 0.02, 0.35), breakup);
}
let albedo = mix(base, base * 0.9 + vec3f(0.06), speck * cover);
return vec4f(albedo, sqrt(mixed));
}
fn map_layer(result_in: MapResult, layer: vec4f, pa: vec4f, pb: vec4f, pc: vec4f, pd: vec4f, uv: vec2f, position: vec3f, time: f32, coated: bool, tile: f32) -> MapResult {
var result = result_in;
let kind = u32(round(layer.x));
let amp = layer.z;
if (kind == 0u || amp == 0.0) {
return result;
}
let seed = bitcast<u32>(layer.w);
let p = position + vec3f(f32(seed) * 0.001, f32(seed) * 0.001, 0.0);
let tiled = !use_procedural_detail && tile >= 0.0;
if (use_fibre && kind == 1u) {
var fibre: f32;
var mottle: f32;
var g: vec3f;
let streak = vec3f(pa.x, pa.y, pa.x);
if (tiled) {
fibre = tile_noise(p * streak, streak, tile, 0u) * 0.6 + tile_noise(p * pa.z, vec3f(pa.z), tile, 1u) * 0.4;
mottle = tile_noise(p * pa.w, vec3f(pa.w), tile, 2u);
g = tile_wobble(p, pb.x, vec2f(3.0, 5.0), tile) * (pc.x * amp) + tile_wobble(p, pb.y, vec2f(11.0, 23.0), tile) * (pc.y * amp);
} else {
fibre = map_noise(p * streak, max(pa.x, pa.y)) * 0.6 + map_noise(p * pa.z, pa.z) * 0.4;
mottle = map_noise(p * pa.w, pa.w);
g = value_noise3_gradient(p, pb.x) * (pc.x * amp) + value_noise3_gradient(p, pb.y) * (pc.y * amp);
}
result.base *= 1.0 + ((fibre - 0.5) * pb.z + (mottle - 0.5) * pb.w) * amp;
result.normal = map_perturb(result.normal, g);
} else if (use_crinkle && kind == 2u) {
var g: vec3f;
if (tiled) {
g = tile_wobble(p, pa.x, vec2f(7.0, 19.0), tile) * (pa.z * amp) + tile_wobble(p, pa.y, vec2f(31.0, 2.0), tile) * (pa.w * amp);
} else {
g = value_noise3_gradient(p, pa.x) * (pa.z * amp) + value_noise3_gradient(p, pa.y) * (pa.w * amp);
}
result.normal = map_perturb(result.normal, g);
} else if (use_wood && kind == 3u) {
let plank = floor(p.z * pd.x);
let tone = hash3_xor(vec3i(i32(plank), 7, 3));
let across = fract(p.z * pd.x);
let seam = smoothstep(0.0, 0.006, across) + smoothstep(0.0, 0.006, 1.0 - across) - 1.0;
var grain = map_ring_mean;
let ring_weight = map_weight(pd.z);
if (ring_weight > 0.0) {
let warp = map_noise(vec3f(p.x * 1.4, plank * 3.7, p.z * 6.0), 6.0) * 0.9 + map_noise(vec3f(p.x * 6.0, plank, p.z * 20.0), 20.0) * 0.25;
let rings = fract(fma(fma(warp, pa.z, p.z), pa.y, map_noise(vec3f(p.x * 0.8, plank, 0.0), 0.8) * pc.y));
grain = map_faded(map_ring_mean, smoothstep(0.55, 0.95, rings), ring_weight);
}
let figure = grain * 0.55 + map_noise(vec3f(p.x * pa.w, p.z * pb.x, plank), pb.x) * 0.25;
let factor = (0.78 + tone * pb.y) * (1.08 - figure * pb.z) * (pb.w + clamp(seam, 0.0, 1.0) * pd.y);
result.base *= mix(1.0, factor, clamp(amp, 0.0, 1.0));
let alpha = result.roughness * result.roughness;
result.roughness = mix(result.roughness, sqrt(min(alpha * (0.85 + figure * pc.x), 1.0)), clamp(amp, 0.0, 1.0));
} else if (use_wall && kind == 4u) {
let broad = map_noise(p * pa.x, pa.x) * 0.6 + map_noise(p * pa.y, pa.y) * 0.4;
var fine: f32;
var g: vec3f;
if (tiled) {
fine = tile_noise(p * pa.z, vec3f(pa.z), tile, 2u) * 0.6 + tile_noise(p * pa.w, vec3f(pa.w), tile, 3u) * 0.4;
g = tile_wobble(p, pb.x, vec2f(5.0, 13.0), tile) * (pc.x * amp) + tile_wobble(p, pb.y, vec2f(29.0, 3.0), tile) * (pc.y * amp);
} else {
fine = map_noise(p * pa.z, pa.z) * 0.6 + map_noise(p * pa.w, pa.w) * 0.4;
g = value_noise3_gradient(p, pb.x) * (pc.x * amp) + value_noise3_gradient(p, pb.y) * (pc.y * amp);
}
result.base *= mix(1.0, 1.0 + (broad - 0.5) * pb.z + (fine - 0.5) * pb.w, clamp(amp, 0.0, 1.0));
result.normal = map_perturb(result.normal, g);
} else if (use_grime && kind == 5u) {
var wear = map_wear_mean;
let grime_weight = map_weight(pa.x);
if (grime_weight > 0.0) {
let grime = value_noise3(p * pa.x, false) * 0.6 + map_noise(p * pa.y, pa.y) * 0.4;
wear = map_faded(map_wear_mean, smoothstep(0.35, 0.75, grime), grime_weight);
}
let tint = mix(vec3f(pa.z, pa.w, pb.x), vec3f(pb.y, pb.z, pb.w), wear);
result.base *= mix(vec3f(1.0), tint, clamp(amp, 0.0, 1.0));
let alpha = result.roughness * result.roughness;
result.roughness = mix(result.roughness, sqrt(min(alpha * mix(pc.x, pc.y, wear), 1.0)), clamp(amp, 0.0, 1.0));
} else if (kind == 7u) {
let spots = fbm3(vec3f(uv * 12.0, 2.0), 4, true);
result.base *= mix(1.0, 0.7 + 0.6 * spots, clamp(amp, 0.0, 1.0));
} else if (kind == 8u) {
let value = fbm3(p * 300.0, 4, true);
result.base *= mix(1.0, 0.75 + 0.5 * value, clamp(amp, 0.0, 1.0));
} else if (use_scratch && kind == 17u) {
if (coated) {
let scratched = map_scratch(result.base, result.coat_roughness, amp, p, seed, true, tile, pa, pd.x);
result.base = scratched.xyz;
result.coat_roughness = scratched.w;
} else {
let scratched = map_scratch(result.base, result.roughness, amp, p, seed, false, tile, pa, pd.x);
result.base = scratched.xyz;
result.roughness = scratched.w;
}
} else if (use_lacquer && kind == 18u) {
result.normal = map_perturb(result.normal, map_lacquer(p, amp, tile, pa, pb, pd));
} else if (kind == 14u) {
let value = flow(uv * layer.y, time);
result.thin_film = max(result.thin_film + amp * (value - 0.5) * 2.0, 0.0);
} else if (kind == 15u) {
let value = map_noise(p * layer.y, abs(layer.y));
result.base *= mix(1.0, 1.0 + (value - 0.5) * 2.0, clamp(amp, 0.0, 1.0));
} else if (kind == 16u) {
let value = fbm3(p * layer.y, 4, true);
result.base *= mix(1.0, 1.0 + (value - 0.5) * 2.0, clamp(amp, 0.0, 1.0));
}
return result;
}
fn map_evaluate(base: vec3f, roughness: f32, metalness: f32, coat_roughness: f32, thin_film: f32, normal: vec3f, tangent: vec4f, uv: vec2f, object_position: vec3f, edge: f32, age_in: f32, time: f32, material_index: u32) -> MapResult {
return map_evaluate_with(base, roughness, metalness, coat_roughness, thin_film, normal, tangent, uv, uv, object_position, edge, age_in, time, material_index);
}
fn map_evaluate_with(base: vec3f, roughness: f32, metalness: f32, coat_roughness: f32, thin_film: f32, normal: vec3f, tangent: vec4f, uv: vec2f, uv1: vec2f, object_position: vec3f, edge: f32, age_in: f32, time: f32, material_index: u32) -> MapResult {
let material = map_materials[material_index];
let baked = material.seeds.w != 0u;
var atlas_uv = uv;
if (use_uv1 && material.packed.y != 0.0) {
atlas_uv = uv1;
}
let tiled_uv = select(uv * select(max(material.factors.x, 0.000001), 1.0, baked), atlas_uv * material.atlas.xy + material.atlas.zw, material.seeds.w >= 2u);
var result = MapResult(base, roughness, metalness, normalize(normal), coat_roughness, thin_film);
let tile_dx = dpdx(tiled_uv) * material.content_extra.z;
let tile_dy = dpdy(tiled_uv) * material.content_extra.z;
if (use_maps && material.indices.x >= 0) {
let sample = textureSampleGrad(map_base, map_sampler, tiled_uv, material.indices.x, tile_dx, tile_dy);
if (baked) {
result.base = sample.rgb;
} else {
result.base *= mix(vec3f(1.0), sample.rgb, clamp(material.factors.y, 0.0, 1.0));
}
}
if (use_maps && material.indices.z >= 0) {
let sampled_roughness = textureSampleGrad(map_roughness, map_sampler, tiled_uv, material.indices.z, tile_dx, tile_dy).r;
if (baked) {
result.roughness = sampled_roughness;
} else {
result.roughness *= sampled_roughness;
}
}
if (use_maps && material.indices.w >= 0) {
result.metalness *= textureSampleGrad(map_metal, map_sampler, tiled_uv, material.indices.w, tile_dx, tile_dy).r;
}
if (use_maps && material.indices.y >= 0) {
var mapped = textureSampleGrad(map_normal, map_sampler, tiled_uv, material.indices.y, tile_dx, tile_dy).xyz * 2.0 - vec3f(1.0);
if (material.seeds.w == 3u) {
mapped.z = sqrt(max(0.0, 1.0 - dot(mapped.xy, mapped.xy)));
}
let t = normalize(tangent.xyz - result.normal * dot(tangent.xyz, result.normal));
let b = cross(result.normal, t) * tangent.w;
let transformed = normalize(t * mapped.x + b * mapped.y + result.normal * mapped.z);
result.normal = normalize(mix(result.normal, transformed, clamp(material.factors.z, 0.0, 1.0)));
}
let age = clamp(age_in, 0.0, 1.0);
let seed = material.seeds.x;
if (use_ageing && age > 0.0) {
result.base = fade_colour(result.base, age * material.age_a.x, object_position, seed);
result.base = yellow(result.base, age * material.age_a.y, uv, seed);
if (material.age_a.z > 0.0) {
map_ink_base = vec4f(result.base, 1.0);
result.base = mix(result.base, ink_age(object_position.xy, 1.0, seed).xyz, clamp(age * material.age_a.z, 0.0, 1.0));
}
if (material.seeds.y != 0u) {
let scratched = map_scratch_coat(result.base, result.coat_roughness, age * material.age_a.w, object_position, seed);
result.base = scratched.xyz;
result.coat_roughness = scratched.w;
} else {
let scratched = scratch(result.base, result.roughness, age * material.age_a.w, object_position, seed);
result.base = scratched.xyz;
result.roughness = scratched.w;
}
let worn = worn_edge(result.base, result.roughness, age * material.age_b.x, edge);
result.base = worn.xyz;
result.roughness = worn.w;
let dusty = dust(result.base, result.roughness, age * material.age_b.y, object_position, seed);
result.base = dusty.xyz;
result.roughness = dusty.w;
let patinated = patina(result.base, result.roughness, result.metalness, age * material.age_b.z, object_position, seed);
result.base = patinated.xyz;
result.roughness = patinated.w;
if (material.age_b.z > 0.0) {
let p = object_position + vec3f(f32(seed) * 0.001);
let green = smoothstep(0.62, 0.9, fbm3(p * 1500.0 + vec3f(9.0), 3, true)) * clamp(age * material.age_b.z, 0.0, 1.0) * 0.45;
result.metalness = mix(result.metalness, 0.5, green);
}
}
if (use_detail && material.seeds.w == 0u) {
result = map_layer(result, material.layer0, material.params[0], material.params[1], material.params[2], material.params[3], uv, object_position, time, material.seeds.y != 0u, material.packed.z);
result = map_layer(result, material.layer1, material.params[4], material.params[5], material.params[6], material.params[7], uv, object_position, time, material.seeds.y != 0u, material.packed.z);
result = map_layer(result, material.layer2, material.params[8], material.params[9], material.params[10], material.params[11], uv, object_position, time, material.seeds.y != 0u, material.packed.z);
result = map_layer(result, material.layer3, material.params[12], material.params[13], material.params[14], material.params[15], uv, object_position, time, material.seeds.y != 0u, material.packed.z);
}
return result;
}
"#;