mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-28 20:16:52 -06:00
Refactor render for strict Clippy checks
This commit is contained in:
@@ -2,7 +2,9 @@ use super::frames::content_bounds;
|
||||
use super::fx_host::MAX_FX_DEPTH;
|
||||
use super::layers::StyledLayer;
|
||||
use super::opts::LayerTiming;
|
||||
use super::*;
|
||||
use super::{
|
||||
Arc, AutoPick, Buf, EffectCtx, EvalCtx, Image, ItemId, ItemKind, Layer, LayerSource, Mat3, MatteKind, Renderer, Tick, cache, color, masks, three_d, vec2,
|
||||
};
|
||||
|
||||
/// Which compositor renders a frame (Project Settings ▸ Video Rendering and Effects).
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
|
||||
@@ -283,7 +285,7 @@ impl<'a> Renderer<'a> {
|
||||
/// layers).
|
||||
pub fn run_effects_on(&self, ctx: &EvalCtx, layer: &Layer, adjustment: bool, target: &mut dyn FxTarget) {
|
||||
// `target` is used as it is: the comp below for adjustment layers (the callers).
|
||||
self.run_effect_stack(ctx, layer, adjustment, usize::MAX, None, [0.0; 2], target);
|
||||
self.run_effect_stack(ctx, layer, crate::pipeline::EffectStack { adjustment, limit: usize::MAX, origin: [0.0; 2] }, None, target);
|
||||
}
|
||||
|
||||
/// [`Renderer::run_effects_on`] for a (non-adjustment) layer's own pixels: `target` holds
|
||||
@@ -291,7 +293,7 @@ impl<'a> Renderer<'a> {
|
||||
/// there. Returns the effect-space origin in layer coordinates (rebase by its negation).
|
||||
pub fn run_layer_effects_on(&self, ctx: &EvalCtx, layer: &Layer, target: &mut dyn FxTarget) -> [f64; 2] {
|
||||
let origin = ctx.effect_bounds(layer).1;
|
||||
self.run_effect_stack(ctx, layer, false, usize::MAX, None, origin, target);
|
||||
self.run_effect_stack(ctx, layer, crate::pipeline::EffectStack { adjustment: false, limit: usize::MAX, origin }, None, target);
|
||||
origin
|
||||
}
|
||||
|
||||
|
||||
+11
-15
@@ -25,7 +25,7 @@ const MAX_DEPTH: usize = 16;
|
||||
/// interleaved (L R L R …).
|
||||
pub fn mix_comp(project: &Project, footage: &dyn FootageSource, expr: Option<&dyn ExprHost>, comp: ItemId, start: Tick, frames: usize, rate: u32) -> Vec<f32> {
|
||||
let mut out = vec![0.0f32; frames * 2];
|
||||
mix_into(project, footage, expr, comp, start, rate, &mut out, 1.0, 1.0, 0);
|
||||
mix_into(&AudioSources { project, footage, expr, rate }, comp, start, &mut out, 0);
|
||||
out
|
||||
}
|
||||
|
||||
@@ -151,7 +151,7 @@ impl MixEnv<'_> {
|
||||
return out;
|
||||
}
|
||||
let mut sub = vec![0.0f32; src_n * 2];
|
||||
mix_into(self.project, self.footage, self.expr, *item, lt, rate, &mut sub, 1.0, 1.0, self.depth + 1);
|
||||
mix_into(&AudioSources { project: self.project, footage: self.footage, expr: self.expr, rate }, *item, lt, &mut sub, self.depth + 1);
|
||||
sub
|
||||
}
|
||||
_ => return out,
|
||||
@@ -206,19 +206,16 @@ impl MixEnv<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn mix_into(
|
||||
project: &Project,
|
||||
footage: &dyn FootageSource,
|
||||
expr: Option<&dyn ExprHost>,
|
||||
comp_id: ItemId,
|
||||
start: Tick,
|
||||
/// Shared inputs for composition audio mixing, including nested compositions.
|
||||
struct AudioSources<'a> {
|
||||
project: &'a Project,
|
||||
footage: &'a dyn FootageSource,
|
||||
expr: Option<&'a dyn ExprHost>,
|
||||
rate: u32,
|
||||
out: &mut [f32],
|
||||
gl: f32,
|
||||
gr: f32,
|
||||
depth: usize,
|
||||
) {
|
||||
}
|
||||
|
||||
fn mix_into(sources: &AudioSources<'_>, comp_id: ItemId, start: Tick, out: &mut [f32], depth: usize) {
|
||||
let AudioSources { project, footage, expr, rate } = *sources;
|
||||
let Some(comp) = project.comp(comp_id) else { return };
|
||||
if depth >= MAX_DEPTH || rate == 0 {
|
||||
return;
|
||||
@@ -239,7 +236,6 @@ fn mix_into(
|
||||
}
|
||||
let t_a = sample_tick(s0 + a as i64, rate);
|
||||
let (ll, lr) = levels(&env, l, t_a);
|
||||
let (ll, lr) = (ll * gl, lr * gr);
|
||||
if ll == 0.0 && lr == 0.0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -580,7 +580,6 @@ fn key_with(ctx: &EvalCtx, layer: &Layer, scale: f64, draft: bool, blur: bool, f
|
||||
|
||||
/// `blur`: the layer is motion blurred in this render (`Renderer::mb_on`). `effects`: key only
|
||||
/// the first that many effects (a layer's input, see [`input_key`]).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn key_any(ctx: &EvalCtx, layer: &Layer, scale: f64, draft: bool, blur: bool, footage: bool, effects: Option<usize>) -> Option<u64> {
|
||||
let mut h = KeyHasher(0xcbf2_9ce4_8422_2325);
|
||||
ctx.comp.frame_rate.as_f64().to_bits().hash(&mut h);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::*;
|
||||
use super::{Arc, AtomicBool, Footage, Image, ItemId, Ordering, Tick};
|
||||
|
||||
/// How a [`FootageSource`] hands over float footage (OpenEXR, float TIFF), whose values are
|
||||
/// linear light: encoded with a curve, as the compositor takes footage, or as stored. Other
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::footage::NoFootage;
|
||||
use super::opts::RenderOpts;
|
||||
use super::*;
|
||||
use super::{EvalCtx, Footage, FrameBlend, FrameRate, Image, ItemId, Layer, LayerSource, Project, Renderer, TICKS_PER_SECOND, Tick, shapes, source_size, text};
|
||||
|
||||
/// The layer's frame blending mode, if the comp's Enable Frame Blending switch is on.
|
||||
/// One field of an interlaced frame as a full frame: the field's lines (`parity` 0 = even /
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::*;
|
||||
use super::{Arc, Buf, EffectHost, EvalCtx, ItemKind, Layer, LayerPixels, LayerSource, Params, Renderer, Tick, three_d};
|
||||
|
||||
/// Layer parameters and audio for effects (see [`effectcraft_effects::EffectHost`]). What it
|
||||
/// hands out is in effect space (see [`effect_bounds`]): the other layer's for its pixels, the
|
||||
|
||||
+15
-14
@@ -1,6 +1,16 @@
|
||||
use super::frames::adaptive_samples;
|
||||
use super::opts::{LayerTiming, Through};
|
||||
use super::*;
|
||||
use super::{
|
||||
Arc, BlendMode, Buf, Comp, EvalCtx, Image, IndexedParallelIterator, IntoParallelRefIterator, IntoParallelRefMutIterator, Layer, LayerSource, Mat3,
|
||||
MatteKind, ParallelIterator, Quality, Region, Renderer, Sampling, Tick, WarpOpts, cache, color, composite_warp_changed, masks, source_size, styles, vec2,
|
||||
};
|
||||
|
||||
/// Blend policy for placing a layer, including optional output-depth quantization.
|
||||
struct PlacementBlend {
|
||||
mode: BlendMode,
|
||||
opacity: f32,
|
||||
quantize: Option<f32>,
|
||||
}
|
||||
|
||||
/// A motion-blur shutter: the comp's, or for a collapsed precomp's layers the outermost
|
||||
/// containing comp's (they are drawn into its frames).
|
||||
@@ -342,22 +352,13 @@ impl<'a> Renderer<'a> {
|
||||
|
||||
/// Draw a processed layer into `target` (transform, motion blur) with `mode`/`opacity`.
|
||||
pub(crate) fn place(&self, ctx: &EvalCtx, layer: &Layer, buf: &Buf, target: &mut Image, mode: BlendMode, opacity: f32) {
|
||||
self.place_changed(ctx, layer, buf, target, mode, opacity, None);
|
||||
self.place_changed(ctx, layer, buf, target, PlacementBlend { mode, opacity, quantize: None });
|
||||
}
|
||||
|
||||
/// [`Self::place`], returning the target pixels it changed when the warp tracked them (then
|
||||
/// also quantised to `quantize` levels; see [`composite_warp_changed`]).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn place_changed(
|
||||
&self,
|
||||
ctx: &EvalCtx,
|
||||
layer: &Layer,
|
||||
buf: &Buf,
|
||||
target: &mut Image,
|
||||
mode: BlendMode,
|
||||
opacity: f32,
|
||||
quantize: Option<f32>,
|
||||
) -> Option<[u32; 4]> {
|
||||
fn place_changed(&self, ctx: &EvalCtx, layer: &Layer, buf: &Buf, target: &mut Image, blend: PlacementBlend) -> Option<[u32; 4]> {
|
||||
let PlacementBlend { mode, opacity, quantize } = blend;
|
||||
let pl = self.placement(ctx, layer, buf);
|
||||
let opts = WarpOpts { sampling: pl.sampling, opacity, mode, seed: pl.seed, clip: None };
|
||||
if let [m] = pl.matrices.as_slice() {
|
||||
@@ -459,7 +460,7 @@ impl<'a> Renderer<'a> {
|
||||
fn composite_layer(&self, ctx: &EvalCtx, layer: &Layer, buf: &Buf, canvas: &mut Image, opacity: f32, quantize: Option<f32>) -> Option<[u32; 4]> {
|
||||
let matte = layer.track_matte.is_some_and(|tm| tm.layer != layer.id && ctx.comp.layer(tm.layer).is_some());
|
||||
if !matte && !layer.preserve_transparency {
|
||||
return self.place_changed(ctx, layer, buf, canvas, layer.blend_mode, opacity, quantize);
|
||||
return self.place_changed(ctx, layer, buf, canvas, PlacementBlend { mode: layer.blend_mode, opacity, quantize });
|
||||
}
|
||||
// Render in isolation, then matte / preserve transparency, then blend.
|
||||
let mut iso = Image::new(canvas.width, canvas.height);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::accel::Backend;
|
||||
use super::*;
|
||||
use super::{Comp, EvalCtx, ItemId, Layer, Mat4, Tick, three_d};
|
||||
|
||||
#[derive(Clone, Copy, Debug, serde::Serialize, serde::Deserialize)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
|
||||
@@ -3,7 +3,18 @@ use super::footage::{FootageSource, is_vector_footage};
|
||||
use super::frames::{blend_frames, content_bounds, field_frame, frame_blend_mode, frame_position, frame_size, interpret_pixels, roi_inside};
|
||||
use super::fx_host::{FxHost, essential_overrides};
|
||||
use super::opts::{Collapse3d, LayerTiming, MAX_THROUGH, RenderOpts, Through};
|
||||
use super::*;
|
||||
use super::{
|
||||
Arc, AutoKey, AutoPick, BlendMode, Buf, Comp, EffectCtx, EffectEnv, EvalCtx, Footage, FootageKind, FrameBlend, FrameRate, GroupKind, Image, ItemId,
|
||||
ItemKind, Layer, LayerCache, LayerSource, Params, Project, Quality, Region, Renderer, Tick, cache, color, masks, shapes, styles, text, three_d, vec2,
|
||||
};
|
||||
|
||||
/// Coordinate space and stack range shared by CPU and accelerated effect evaluation.
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct EffectStack {
|
||||
pub adjustment: bool,
|
||||
pub limit: usize,
|
||||
pub origin: [f64; 2],
|
||||
}
|
||||
|
||||
impl<'a> Renderer<'a> {
|
||||
pub fn new(project: &'a Project, footage: &'a dyn FootageSource, opts: RenderOpts) -> Renderer<'a> {
|
||||
@@ -423,26 +434,16 @@ impl<'a> Renderer<'a> {
|
||||
// The comp below an adjustment layer is not in its layer space: it stays as it is.
|
||||
let origin = if adjustment { [0.0; 2] } else { ctx.effect_bounds(layer).1 };
|
||||
buf.rebase(origin);
|
||||
let mut out = self.effect_space_stack(ctx, layer, buf, adjustment, limit, timing, origin);
|
||||
let mut out = self.effect_space_stack(ctx, layer, buf, EffectStack { adjustment, limit, origin }, timing);
|
||||
out.rebase([-origin[0], -origin[1]]);
|
||||
out
|
||||
}
|
||||
|
||||
/// [`Renderer::apply_effects_timed`] on a buffer already in effect space (measured from
|
||||
/// `origin`, in layer coordinates); the result stays in effect space.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn effect_space_stack(
|
||||
&self,
|
||||
ctx: &EvalCtx,
|
||||
layer: &Layer,
|
||||
buf: Buf,
|
||||
adjustment: bool,
|
||||
limit: usize,
|
||||
timing: Option<&mut Vec<(String, f64)>>,
|
||||
origin: [f64; 2],
|
||||
) -> Buf {
|
||||
fn effect_space_stack(&self, ctx: &EvalCtx, layer: &Layer, buf: Buf, stack: EffectStack, timing: Option<&mut Vec<(String, f64)>>) -> Buf {
|
||||
let mut t = CpuFx { accel: self.active_accel(), levels: self.pipe.levels, buf: Some(buf) };
|
||||
self.run_effect_stack(ctx, layer, adjustment, limit, timing, origin, &mut t);
|
||||
self.run_effect_stack(ctx, layer, stack, timing, &mut t);
|
||||
t.buf.unwrap_or_else(|| Buf { img: Image::new(1, 1), offset: [0.0; 2], scale: self.opts.scale })
|
||||
}
|
||||
|
||||
@@ -450,17 +451,15 @@ impl<'a> Renderer<'a> {
|
||||
/// in layer coordinates): runs of GPU-capable effects go to [`FxTarget::gpu`] (when an
|
||||
/// accelerator is active and supports them), the rest to [`FxTarget::cpu`] one by one
|
||||
/// (quantised to the bit depth after each).
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub(crate) fn run_effect_stack(
|
||||
&self,
|
||||
ctx: &EvalCtx,
|
||||
layer: &Layer,
|
||||
adjustment: bool,
|
||||
limit: usize,
|
||||
stack: EffectStack,
|
||||
mut timing: Option<&mut Vec<(String, f64)>>,
|
||||
origin: [f64; 2],
|
||||
target: &mut dyn FxTarget,
|
||||
) {
|
||||
let EffectStack { adjustment, limit, origin } = stack;
|
||||
if !layer.switches.effects {
|
||||
return;
|
||||
}
|
||||
@@ -873,7 +872,7 @@ impl<'a> Renderer<'a> {
|
||||
let origin = ctx.effect_bounds(layer).1;
|
||||
buf.rebase(origin);
|
||||
if effects > 0 {
|
||||
buf = self.effect_space_stack(ctx, layer, buf, false, effects, None, origin);
|
||||
buf = self.effect_space_stack(ctx, layer, buf, EffectStack { adjustment: false, limit: effects, origin }, None);
|
||||
}
|
||||
let buf = Arc::new(buf);
|
||||
// The buffer the cache keeps (another worker's, if it kept one first).
|
||||
|
||||
@@ -211,16 +211,16 @@ fn collect(ctx: &EvalCtx, layer: &Layer, contents: &PropGroup, arena: &mut Vec<B
|
||||
}
|
||||
"star" => {
|
||||
let star = ctx.e(layer, g, "type") == 0;
|
||||
let p = effectcraft_path::polystar(
|
||||
let p = effectcraft_path::polystar(effectcraft_path::Polystar {
|
||||
star,
|
||||
ctx.f(layer, g, "points", 5.0),
|
||||
ctx.v2(layer, g, "position", [0.0; 2]),
|
||||
ctx.f(layer, g, "rotation", 0.0),
|
||||
ctx.f(layer, g, "innerRadius", 50.0),
|
||||
ctx.f(layer, g, "outerRadius", 100.0),
|
||||
ctx.f(layer, g, "innerRoundness", 0.0),
|
||||
ctx.f(layer, g, "outerRoundness", 0.0),
|
||||
);
|
||||
points: ctx.f(layer, g, "points", 5.0),
|
||||
pos: ctx.v2(layer, g, "position", [0.0; 2]),
|
||||
rotation: ctx.f(layer, g, "rotation", 0.0),
|
||||
inner: ctx.f(layer, g, "innerRadius", 50.0),
|
||||
outer: ctx.f(layer, g, "outerRadius", 100.0),
|
||||
inner_round: ctx.f(layer, g, "innerRoundness", 0.0),
|
||||
outer_round: ctx.f(layer, g, "outerRoundness", 0.0),
|
||||
});
|
||||
push_shape(ctx, layer, g, p, arena, &mut live);
|
||||
}
|
||||
"path" => {
|
||||
|
||||
+53
-31
@@ -287,11 +287,13 @@ pub fn char_transforms(ctx: &EvalCtx, layer: &Layer, text: &PropGroup, lay: &Tex
|
||||
let pos = get("position").map(|v| c3(v, 0.0));
|
||||
let anchor = get("anchor").map(|v| c3(v, 0.0));
|
||||
let scale = get("scale").map(|v| c3(v, 100.0));
|
||||
let rot = get1("rotation");
|
||||
let rx = get1("rotationX");
|
||||
let ry = get1("rotationY");
|
||||
let skew = get1("skew");
|
||||
let skew_axis = get1("skewAxis");
|
||||
let angles = CharacterAngles {
|
||||
rotation: get1("rotation"),
|
||||
rotation_x: get1("rotationX"),
|
||||
rotation_y: get1("rotationY"),
|
||||
skew: get1("skew"),
|
||||
skew_axis: get1("skewAxis"),
|
||||
};
|
||||
let op = get1("opacity");
|
||||
let fill = props.get("fillColor").map(|p| ctx.value(layer, p).as_color());
|
||||
let stroke = props.get("strokeColor").map(|p| ctx.value(layer, p).as_color());
|
||||
@@ -338,21 +340,7 @@ pub fn char_transforms(ctx: &EvalCtx, layer: &Layer, text: &PropGroup, lay: &Tex
|
||||
c.scale[d] *= 1.0 + (s[d] / 100.0 - 1.0) * k3[d];
|
||||
}
|
||||
}
|
||||
if let Some(r) = rot {
|
||||
c.rotation += r * k;
|
||||
}
|
||||
if let Some(r) = rx {
|
||||
c.rotation_x += r * k;
|
||||
}
|
||||
if let Some(r) = ry {
|
||||
c.rotation_y += r * k;
|
||||
}
|
||||
if let Some(s) = skew {
|
||||
c.skew += s * k;
|
||||
}
|
||||
if let Some(a) = skew_axis {
|
||||
c.skew_axis = a;
|
||||
}
|
||||
angles.apply(c, k);
|
||||
if let Some(o) = op {
|
||||
c.opacity *= 1.0 + (o / 100.0 - 1.0) * k;
|
||||
}
|
||||
@@ -381,17 +369,7 @@ pub fn char_transforms(ctx: &EvalCtx, layer: &Layer, text: &PropGroup, lay: &Tex
|
||||
if let Some(w) = sw {
|
||||
c.stroke_width += w * k;
|
||||
}
|
||||
if let Some(t) = tracking {
|
||||
let t = t * k;
|
||||
match tracking_type {
|
||||
1 => c.track_before += t,
|
||||
2 => c.track_after += t,
|
||||
_ => {
|
||||
c.track_before += t * 0.5;
|
||||
c.track_after += t * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
apply_tracking(c, tracking, tracking_type, k);
|
||||
if let Some(a) = line_anchor
|
||||
&& k.abs() >= c.line_anchor_k
|
||||
{
|
||||
@@ -432,6 +410,50 @@ pub fn char_transforms(ctx: &EvalCtx, layer: &Layer, text: &PropGroup, lay: &Tex
|
||||
out
|
||||
}
|
||||
|
||||
/// Optional animator orientation properties; an omitted property leaves the glyph unchanged.
|
||||
struct CharacterAngles {
|
||||
rotation: Option<f64>,
|
||||
rotation_x: Option<f64>,
|
||||
rotation_y: Option<f64>,
|
||||
skew: Option<f64>,
|
||||
skew_axis: Option<f64>,
|
||||
}
|
||||
|
||||
impl CharacterAngles {
|
||||
fn apply(&self, c: &mut CharXf, k: f64) {
|
||||
if let Some(r) = self.rotation {
|
||||
c.rotation += r * k;
|
||||
}
|
||||
if let Some(r) = self.rotation_x {
|
||||
c.rotation_x += r * k;
|
||||
}
|
||||
if let Some(r) = self.rotation_y {
|
||||
c.rotation_y += r * k;
|
||||
}
|
||||
if let Some(s) = self.skew {
|
||||
c.skew += s * k;
|
||||
}
|
||||
if let Some(a) = self.skew_axis {
|
||||
c.skew_axis = a;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Distribute selected tracking before and/or after a glyph according to Tracking Type.
|
||||
fn apply_tracking(c: &mut CharXf, tracking: Option<f64>, tracking_type: u32, k: f64) {
|
||||
if let Some(t) = tracking {
|
||||
let t = t * k;
|
||||
match tracking_type {
|
||||
1 => c.track_before += t,
|
||||
2 => c.track_after += t,
|
||||
_ => {
|
||||
c.track_before += t * 0.5;
|
||||
c.track_after += t * 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Character Offset / Character Value substitution (None = unchanged).
|
||||
pub fn substitute(ch: char, x: &CharXf) -> Option<char> {
|
||||
if ch.is_whitespace() {
|
||||
|
||||
@@ -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