mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-31 13:37:37 -06:00
Merge origin/main into fix/issue-2092-camera-focus-crash
Conflicts were main's new code next to this branch's additions; both kept: - crates/effects/src/lib.rs: main's Buf::pad_sides stays as main wrote it (Transform's transform_grow bounds that padding itself); Buf::pad keeps this branch's effective-pad clamp. - crates/raster/src/lib.rs: main's quantize_px/round_half_up and this branch's MAX_PADDED_SIDE (still equal to limits::MAX_SIZE on main).
This commit is contained in:
@@ -82,9 +82,20 @@ pub fn f_schlick(f0: [f32; 3], v_h: f32) -> [f32; 3] {
|
||||
[f0[0] + (1.0 - f0[0]) * k, f0[1] + (1.0 - f0[1]) * k, f0[2] + (1.0 - f0[2]) * k]
|
||||
}
|
||||
|
||||
/// Surface parameters used by the direct-light BRDF after texture sampling.
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct BrdfSurface {
|
||||
pub base: [f32; 3],
|
||||
pub metallic: f32,
|
||||
pub roughness: f32,
|
||||
pub specular: f32,
|
||||
pub diffuse: f32,
|
||||
}
|
||||
|
||||
/// Cook–Torrance BRDF × N·L × π for one light: (diffuse colour factor, specular colour).
|
||||
/// Returned as the reflected fraction of the light's irradiance.
|
||||
pub fn brdf(base: [f32; 3], metallic: f32, rough: f32, spec_k: f32, diff_k: f32, n: [f32; 3], v: [f32; 3], l: [f32; 3]) -> [f32; 3] {
|
||||
pub fn brdf(surface: BrdfSurface, n: [f32; 3], v: [f32; 3], l: [f32; 3]) -> [f32; 3] {
|
||||
let BrdfSurface { base, metallic, roughness: rough, specular: spec_k, diffuse: diff_k } = surface;
|
||||
let n_l = dot(n, l);
|
||||
if n_l <= 0.0 {
|
||||
return [0.0; 3];
|
||||
@@ -484,7 +495,7 @@ pub fn shade(s: &Scene, f: &Frag) -> Option<[f32; 4]> {
|
||||
continue;
|
||||
}
|
||||
let sh = if m.receives_shadows { light_shadow(s, l, f.pos, ng, n_l) } else { 1.0 };
|
||||
let b = brdf(rgb, metallic, rough, m.specular_k, m.diffuse_k, n, v, to_l);
|
||||
let b = brdf(BrdfSurface { base: rgb, metallic, roughness: rough, specular: m.specular_k, diffuse: m.diffuse_k }, n, v, to_l);
|
||||
for c in 0..3 {
|
||||
out[c] += b[c] * l.color[c] * k * sh;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ fn brdf_matches_cook_torrance_formula() {
|
||||
let v = shade::norm([0.3, 0.1, 1.0]);
|
||||
let l = shade::norm([-0.4, 0.2, 1.0]);
|
||||
let (base, metal, rough) = ([0.8, 0.4, 0.2], 0.3f32, 0.45f32);
|
||||
let got = shade::brdf(base, metal, rough, 1.0, 1.0, n, v, l);
|
||||
let got = shade::brdf(shade::BrdfSurface { base, metallic: metal, roughness: rough, specular: 1.0, diffuse: 1.0 }, n, v, l);
|
||||
let h = shade::norm([v[0] + l[0], v[1] + l[1], v[2] + l[2]]);
|
||||
let (nv, nl, nh, vh) = (shade::dot(n, v), shade::dot(n, l), shade::dot(n, h), shade::dot(v, h));
|
||||
let a2 = (rough * rough).powi(2);
|
||||
@@ -30,9 +30,9 @@ fn brdf_matches_cook_torrance_formula() {
|
||||
assert!((got[c] - expect).abs() < 1e-5, "channel {c}: {} vs {expect}", got[c]);
|
||||
}
|
||||
// No light from behind the surface.
|
||||
assert_eq!(shade::brdf(base, metal, rough, 1.0, 1.0, n, v, [0.0, 0.0, -1.0]), [0.0; 3]);
|
||||
assert_eq!(shade::brdf(shade::BrdfSurface { base, metallic: metal, roughness: rough, specular: 1.0, diffuse: 1.0 }, n, v, [0.0, 0.0, -1.0]), [0.0; 3]);
|
||||
// A white rough dielectric lit head-on reflects about its base colour (π folded in).
|
||||
let w = shade::brdf([1.0; 3], 0.0, 1.0, 1.0, 1.0, n, n, n);
|
||||
let w = shade::brdf(shade::BrdfSurface { base: [1.0; 3], metallic: 0.0, roughness: 1.0, specular: 1.0, diffuse: 1.0 }, n, n, n);
|
||||
assert!(w[0] > 0.95 && w[0] < 1.05, "{w:?}");
|
||||
}
|
||||
|
||||
|
||||
@@ -197,7 +197,7 @@ fn mb_samples(r: &Renderer, ctx: &EvalCtx, layer: &Layer) -> usize {
|
||||
/// Prepare a layer (buffer, geometry, material). `None` when it shows nothing.
|
||||
pub(crate) fn prepare<'a>(r: &Renderer, ctx: &EvalCtx<'a>, layer: &'a Layer, order: usize, out: (u32, u32), in_run: bool) -> Option<Item<'a>> {
|
||||
let buf = r.blend_layer_buf(ctx, layer)?;
|
||||
prepare_with(r, ctx, layer, order, out, in_run, buf, Mat4::IDENTITY)
|
||||
prepare_with(r, ctx, layer, order, out, in_run, PlaneInput { buf, local: Mat4::IDENTITY })
|
||||
}
|
||||
|
||||
/// Prepare a layer as planes: one per character for per-character 3D text, else one.
|
||||
@@ -205,24 +205,21 @@ pub(crate) fn prepare_all<'a>(r: &Renderer, ctx: &EvalCtx<'a>, layer: &'a Layer,
|
||||
if crate::text::per_char_3d(ctx, layer) {
|
||||
return crate::text::per_char_planes(ctx, layer, r.opts.scale)
|
||||
.into_iter()
|
||||
.filter_map(|(buf, m)| prepare_with(r, ctx, layer, order, out, in_run, r.to_blend(Arc::new(buf), None), m))
|
||||
.filter_map(|(buf, m)| prepare_with(r, ctx, layer, order, out, in_run, PlaneInput { buf: r.to_blend(Arc::new(buf), None), local: m }))
|
||||
.collect();
|
||||
}
|
||||
prepare(r, ctx, layer, order, out, in_run).into_iter().collect()
|
||||
}
|
||||
|
||||
/// Prepare a plane showing `buf`, whose space maps into the layer through `local`.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn prepare_with<'a>(
|
||||
r: &Renderer,
|
||||
ctx: &EvalCtx<'a>,
|
||||
layer: &'a Layer,
|
||||
order: usize,
|
||||
out: (u32, u32),
|
||||
in_run: bool,
|
||||
mut buf: Arc<Buf>,
|
||||
/// A processed plane and its mapping into the layer's coordinate system.
|
||||
struct PlaneInput {
|
||||
buf: Arc<Buf>,
|
||||
local: Mat4,
|
||||
) -> Option<Item<'a>> {
|
||||
}
|
||||
|
||||
/// Prepare the plane's geometry, motion samples and material.
|
||||
fn prepare_with<'a>(r: &Renderer, ctx: &EvalCtx<'a>, layer: &'a Layer, order: usize, out: (u32, u32), in_run: bool, plane: PlaneInput) -> Option<Item<'a>> {
|
||||
let PlaneInput { mut buf, local } = plane;
|
||||
if buf.img.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user