Skip to content

Commit 0cdc9bc

Browse files
committed
fix(compositor): Left back to the v1.13.0 iso, Right its mirror
Left and Right had become steep roll-free angles drawn with the projective warp, [-23, +-25, 0]: nothing like the v1.13.0 iso. - Left is the v1.13.0 iso again, [-12, -18, -2]; Right is its mirror, [-12, 18, 2]. A stored iso still reads as Left. - Fixed angles use the bilinear warp again, projective only under a device frame, as in v1.13.0. - The inspector's camera tiles are redrawn from these poses. - The mode-8 motion blur added since v1.13.0 stays. Checked against the installed v1.13.0 with `openscreen export`: while the zoom holds, Left matches the old iso and follow-cursor matches the old orbit, cursor sprite aside. Right mirrors Left within 2 px.
1 parent cbf85dc commit 0cdc9bc

9 files changed

Lines changed: 99 additions & 129 deletions

File tree

‎crates/compositor/src/frame_geometry.rs‎

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1158,7 +1158,7 @@ pub fn cursor_sprite_cb(
11581158
src_prev: [br0, br1, bl0, bl1],
11591159
// Le clip vit ici et NON dans `fx` (mode 7) : `fx` porte les coins.
11601160
dst_prev: clip,
1161-
// `mb.x` : 1 = warp projectif (tout écran incliné, cf. `screen_tilt`).
1161+
// `mb.x` : 1 = warp projectif (caméra réelle ou appareil, cf. `screen_tilt`).
11621162
mb: [quad.warp_flag(), 0.0, 0.0, 0.0],
11631163
..Default::default()
11641164
}
@@ -1908,16 +1908,16 @@ impl FrameGeometry {
19081908
self.zoom_rotation,
19091909
self.zoom_rotation_dyn,
19101910
);
1911-
// Un angle fixe se dessine au warp PROJECTIF, comme la caméra réelle. Les quatre coins
1912-
// sont ceux de la projection exacte, donc l'homographie qu'ils définissent EST cette
1913-
// projection, partout et pas seulement aux coins. Le warp bilinéaire s'en écartait de
1914-
// ~8 % de la largeur au milieu des bords (mesuré par
1915-
// `the_device_screen_face_lands_on_the_footage_plane`), et surtout il PENCHAIT le
1916-
// contenu : la verticale du milieu de l'écran, droite sous une caméra sans roulis,
1917-
// tombait de 2,9° sous `iso` et de 0,8° sous `left`/`right`, et ce roulis-là entrait
1918-
// avec le zoom. Le mode 17 (appareil) en dépend aussi : il lance ses rayons dans la
1919-
// perspective exacte.
1920-
quad.projective = true;
1911+
// Un angle fixe se dessine au warp BILINÉAIRE de ses coins : le rendu des angles fixes
1912+
// de la v1.13.0, à l'octet. Sous un cadre d'APPAREIL seulement, l'écran passe au warp
1913+
// PROJECTIF : le mode 17 lance ses rayons dans la perspective exacte, et le warp
1914+
// bilinéaire s'en écarte de ~8 % de la largeur au milieu des bords (mesuré par
1915+
// `the_device_screen_face_lands_on_the_footage_plane`), soit une lunette à cent pixels
1916+
// du bord de l'image. Les quatre coins sont les mêmes des deux côtés, donc
1917+
// l'homographie qu'ils définissent EST cette projection exacte.
1918+
if self.window_frame.as_ref().is_some_and(|f| f.kind.is_device()) {
1919+
quad.projective = true;
1920+
}
19211921
quad
19221922
})
19231923
}
@@ -4663,10 +4663,10 @@ mod tests {
46634663
assert_eq!(cb.dst_prev[3], 1.0, "{name}: l'écran est resté au warp bilinéaire");
46644664
assert_eq!(cb.color, [0.0; 4], "{name}: la lampe de la caméra s'est allumée");
46654665
}
4666-
// Sans appareil, l'angle fixe a le même warp exact (`screen_tilt`) et toujours pas de
4667-
// lampe ; la caméra réelle, elle, a les deux.
4666+
// Sans appareil, l'angle fixe garde son warp bilinéaire ET son absence de lampe ; la
4667+
// caméra réelle, elle, garde les deux.
46684668
let plain = flat_cb(&framed_plan(&framed_scene("", r#""iso""#, 1.0, false)));
4669-
assert_eq!((plain.dst_prev[3], plain.color), (1.0, [0.0; 4]));
4669+
assert_eq!((plain.dst_prev[3], plain.color), (0.0, [0.0; 4]));
46704670
let orbit = flat_cb(&framed_plan(&framed_scene("", r#""follow-cursor""#, 1.0, false)));
46714671
assert_eq!(orbit.dst_prev[3], 1.0);
46724672
assert!(orbit.color[0] != 0.0 || orbit.color[1] != 0.0, "la caméra réelle n'éclaire plus");
@@ -5961,7 +5961,7 @@ mod tests {
59615961
/// reporté dans le slot d'un layout en bloc ; sans elle, rien de ce qu'il portait ne change.
59625962
#[test]
59635963
fn the_tilted_screen_cb_carries_its_trail_in_its_own_frame() {
5964-
let quad = crate::regions::rotated_quad_corners_px(800.0, 450.0, [-23.0, -25.0, 0.0], [0.0; 3]);
5964+
let quad = crate::regions::rotated_quad_corners_px(800.0, 450.0, [-12.0, -18.0, -2.0], [0.0; 3]);
59655965
let moved = TiltTrail { corners: quad.corners.map(|(x, y)| (x * 0.9 + 3.0, y * 0.9 - 2.0)), mb: [6.0, 0.35] };
59665966
let mask = ScreenMask { rect: [0.1, 0.1, 0.6, 0.6], radius_px: 8.0 };
59675967
let cb = |mask, trail| {
@@ -7011,8 +7011,8 @@ mod tests {
70117011
.expect("plan cursor")
70127012
}
70137013

7014-
const ISO: [f32; 3] = [-23.0, -25.0, 0.0];
7015-
const LEFT: [f32; 3] = [-23.0, -25.0, 0.0];
7014+
const LEFT: [f32; 3] = [-12.0, -18.0, -2.0];
7015+
const RIGHT: [f32; 3] = [-12.0, 18.0, 2.0];
70167016

70177017
/// Le `LayerCB` du sprite incliné est, octet pour octet, celui que chaque backend construisait
70187018
/// avant de le partager. La référence est le corps d'origine de `draw_cursor_sprite`.
@@ -7070,7 +7070,7 @@ mod tests {
70707070
let len = std::mem::size_of::<LayerCB>();
70717071
unsafe { std::slice::from_raw_parts(cb as *const LayerCB as *const u8, len) }.to_vec()
70727072
};
7073-
for rot in [ISO, LEFT, [-23.0, 25.0, 0.0]] {
7073+
for rot in [LEFT, RIGHT] {
70747074
let plan = plan_with(rot);
70757075
let clip = [0.1, 0.2, 0.7, 0.6];
70767076
let got = cursor_sprite_cb(
@@ -7327,7 +7327,7 @@ mod tests {
73277327
let scene = model_scene();
73287328
for key in MODEL_STATES {
73297329
let (sdf, shape) = sprite_model(key);
7330-
for rot in [[0.0; 3], [-23.0, -25.0, 0.0]] {
7330+
for rot in [[0.0; 3], LEFT] {
73317331
let track = still_track_as(Some(key), vec![0.5]);
73327332
for (t, want) in [(0.3, MODEL_HOVER), (0.5 + CONTACT_S, 0.0)] {
73337333
let plan = model_plan(rot, &scene, &track, t, true).expect("plan");
@@ -7451,9 +7451,6 @@ mod tests {
74517451
assert!(model_plan([0.0; 3], &bare, &track, 0.3, true).expect("plan").model.is_none());
74527452
}
74537453

7454-
const LEFT_ROT: [f32; 3] = [-23.0, -25.0, 0.0];
7455-
const ISO_ROT: [f32; 3] = [-23.0, -25.0, 0.0];
7456-
74577454
/// Les poses d'essai, pour chaque état de `MODEL_STATES` : au repos, posé, tourné.
74587455
fn model_cases() -> Vec<(String, CursorPlan, SpriteShape, crate::cursor_sdf::CursorSdf)> {
74597456
let scene = model_scene();
@@ -7465,7 +7462,7 @@ mod tests {
74657462
vec![],
74667463
vec![(0.0, key.to_string())],
74677464
);
7468-
for (name, rot) in [("flat", [0.0; 3]), ("iso", ISO_ROT), ("left", LEFT_ROT), ("right", [-23.0, 25.0, 0.0])] {
7465+
for (name, rot) in [("flat", [0.0; 3]), ("left", LEFT), ("right", RIGHT)] {
74697466
for (pose, track, t) in [("hover", &clicked, 0.3), ("touch", &clicked, 0.5 + CONTACT_S), ("yaw", &moving, 1.0)] {
74707467
let plan = model_plan(rot, &scene, track, t, true).expect("plan");
74717468
let (sdf, shape) = sprite_model(key);

‎crates/compositor/src/regions.rs‎

Lines changed: 33 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -602,25 +602,17 @@ fn ease_connected_pan(t: f32) -> f32 {
602602
/// alors que le plan était rendu en entier — mesuré au pixel sur un export 1920×1080 : bord droit
603603
/// à 1539, coin calculé à 1540, arrondis présents aux quatre coins.
604604
///
605-
/// Aucune arête ne s'approche donc d'un axe à moins de 2° (cf.
606-
/// `no_preset_has_an_axis_aligned_edge`), et AUCUN préset ne roule : Z vaut 0. Le tilt entre avec
607-
/// le zoom, et une caméra qui bouge ne roule jamais le métrage — le roulis de −2°/±1° qui tenait
608-
/// la règle jusqu'ici penchait l'image pendant chaque zoom, et balayait 2° entre left et right.
605+
/// Chaque préset a donc ses trois composantes, choisies pour qu'aucune arête ne s'approche d'un
606+
/// axe à moins de 2° (cf. `no_preset_has_an_axis_aligned_edge`).
609607
///
610-
/// Sans roulis, la règle ne laisse que deux bandes de tangage (X). Une rotation Y pure garde les
611-
/// verticales verticales, donc chaque préset tangue ; et entre 10° et 14° de tangage, le bord haut
612-
/// d'un écran tourné ressort à plat (la remontée du rotateX annulée par la perspective), à tout
613-
/// lacet. Les deux présets prennent donc la bande haute, le regard de l'ancien `iso` : tourné et
614-
/// vu d'en haut franchement, arêtes à 4,8° au plus près. Ils sortent de la bande des 2° pendant
615-
/// leur ease-in à 0,82 de force, avant que la parallaxe ne s'ouvre (`PARALLAX_GATE_START`), et
616-
/// laissent au budget dynamique ses ±1,9° / ±3° d'origine (`DYNAMIC_TILT_BUDGET`).
617-
///
618-
/// `iso` n'est plus proposé : c'était déjà ce regard-là, tourné à gauche. Un projet qui le porte
619-
/// encore se lit donc comme `left` (côté app : `readRotation3DPreset`, et ici par sécurité).
608+
/// `left` EST l'`iso` de la v1.13.0, à la valeur près : tourné vers la gauche et vu d'en haut, avec
609+
/// son léger roulis. `right` en est le miroir (lacet et roulis inversés). `iso` n'est plus proposé
610+
/// et se lit comme `left` (côté app : `readRotation3DPreset`, et ici par sécurité), donc un projet
611+
/// qui le porte rend exactement comme avant.
620612
fn rotation3d_for(rotation: &Option<String>) -> [f32; 3] {
621613
match rotation.as_deref() {
622-
Some("left" | "iso") => [-23.0, -25.0, 0.0],
623-
Some("right") => [-23.0, 25.0, 0.0],
614+
Some("left" | "iso") => [-12.0, -18.0, -2.0],
615+
Some("right") => [-12.0, 18.0, 2.0],
624616
_ => [0.0, 0.0, 0.0],
625617
}
626618
}
@@ -1149,11 +1141,11 @@ pub(crate) const PERSPECTIVE_FACTOR: f32 = 1.6;
11491141
/// profondeur rapporté à la distance de fuite (`|z − z_focus| / P`).
11501142
///
11511143
/// Réglé une fois, en unités de P : cet écart ne dépend ni de la résolution ni du zoom (la
1152-
/// perspective se déduit de la taille du plan lui-même). Sur iso en 16:9, focus au centre, le
1153-
/// coin lointain est à ~0.25 P, soit un flou de ~5.9 texels, au plafond du shader
1154-
/// (`DOF_MAX_LOD`, niveau 1.5 de la pyramide demi-résolution, soit ~5.7 texels) ; focus sur le
1155-
/// coin proche, l'écart double. Un flou exprimé en texels suit le CONTENU : la même zone est aussi
1156-
/// floue quel que soit le zoom qui l'affiche.
1144+
/// perspective se déduit de la taille du plan lui-même). Sur `left` en 16:9, focus au centre, le
1145+
/// coin lointain est à ~0.19 P, soit un flou de ~4.6 texels ; focus sur le coin proche, l'écart
1146+
/// double et le shader plafonne (`DOF_MAX_LOD`, niveau 1.5 de la pyramide demi-résolution, soit
1147+
/// ~5.7 texels). Un flou exprimé en texels suit le CONTENU : la même zone est aussi floue quel
1148+
/// que soit le zoom qui l'affiche.
11571149
pub const DOF_COC_PER_DEPTH: f32 = 24.0;
11581150

11591151
/// Les 4 coins d'un quad `width`×`height` réduit de `scale`, projetés. `None` si un coin part
@@ -1208,10 +1200,9 @@ pub struct TiltedQuad {
12081200
/// Où tombe le centre du plan à l'image, en px relatifs au centre du rect d'origine.
12091201
pub offset: [f32; 2],
12101202
/// `true` : le plan se dessine par l'homographie EXACTE de ses coins ; `false` : par le warp
1211-
/// bilinéaire, qui penche le contenu d'un plan tourné ET tangué (sa verticale du milieu
1212-
/// suit les milieux des bords, pas la projection). Tout écran incliné passe au projectif
1213-
/// (`FrameGeometry::screen_tilt`) ; `rotated_quad_corners_px` rend `false`, la géométrie
1214-
/// seule.
1203+
/// bilinéaire des angles fixes, le rendu de la v1.13.0. La caméra réelle l'exige, et un cadre
1204+
/// d'appareil aussi (le mode 17 lance ses rayons dans la perspective exacte,
1205+
/// `FrameGeometry::screen_tilt`).
12151206
pub projective: bool,
12161207
/// `true` : une lampe posée sur la caméra éclaire un peu plus le côté proche du plan
12171208
/// (`CAMERA_LIGHT_GAIN`, cf. `tilted_screen_cb`). Elle appartient à la caméra RÉELLE, pas au
@@ -1332,9 +1323,10 @@ fn contain_scale(
13321323
/// puis la somme est bornée par `clamp_dynamic_tilt`.
13331324
///
13341325
/// - Z : 0. Jamais de roulis : une caméra qui bouge ne roule pas le métrage.
1335-
/// - X : ±1,9°, Y : ±3°. Ensemble, à 1,15 fois ces valeurs, `left` sort d'une des deux règles
1336-
/// (arête à moins de 2° d'un axe, ou débordement de sa boîte) ; seul, Y y arrive vers 3,3°, X
1337-
/// laisse plus de marge (`the_budget_is_close_to_what_left_allows`).
1326+
/// - X : ±1,9°, Y : ±3°. Les valeurs de la v1.13.0, au ras de son `left` [−8, −16, −1] : le
1327+
/// `left` d'aujourd'hui, son `iso`, garde ainsi sa parallaxe et son impact à l'identique. Il ne
1328+
/// sortirait d'une des deux règles (arête à moins de 2° d'un axe, ou débordement de sa boîte)
1329+
/// qu'à 1,46 fois ce balayage.
13381330
///
13391331
/// Reproduits par `the_budget_sweep_keeps_every_edge_off_axis`,
13401332
/// `the_budget_sweep_stays_inside_the_original_rect` et
@@ -1353,10 +1345,10 @@ pub fn clamp_dynamic_tilt(rot: [f32; 3]) -> [f32; 3] {
13531345
}
13541346

13551347
/// Force du préset sous laquelle la part dynamique est nulle ; elle s'installe en smoothstep
1356-
/// jusqu'à 1. La spec disait 0,5 ; les maths de ce crate disent autrement : en montant,
1357-
/// `left`/`right` traversent le tangage où leur bord haut ressort à plat, et ne sortent de la
1358-
/// bande des 2° qu'à 0,82 de force : la porte doit s'ouvrir après, et raide. Cf.
1359-
/// `the_budget_sweep_keeps_every_edge_off_axis`.
1348+
/// jusqu'à 1. La spec disait 0,5 ; les maths de ce crate disent autrement : l'ease-in part de
1349+
/// l'écran droit, dont les arêtes longent les axes, et `left`/`right` ne sortent de la bande des
1350+
/// 2° qu'à 0,48 de force. 0,85 est la porte de la v1.13.0, réglée pour son `left` plus serré
1351+
/// (sorti de la bande à 0,635 seulement). Cf. `the_budget_sweep_keeps_every_edge_off_axis`.
13601352
const PARALLAX_GATE_START: f32 = 0.85;
13611353
/// Degrés de parallaxe par unité de vitesse (largeurs — ou hauteurs — de coupe par seconde),
13621354
/// avant saturation. Un balayage d'une demi-largeur par seconde penche d'environ 2° en Y, une
@@ -2232,13 +2224,14 @@ mod tilt_tests {
22322224
}
22332225
}
22342226

2235-
/// `iso` n'est plus proposé : il se lit comme `left`, qui a gardé son regard, et `right` en est
2236-
/// le miroir exact.
2227+
/// `left` est l'`iso` de la v1.13.0 à la valeur près, `iso` se lit comme lui, et `right` en est
2228+
/// le miroir exact : lacet et roulis inversés.
22372229
#[test]
22382230
fn iso_reads_as_left_and_right_mirrors_it() {
22392231
let left = rotation3d_for(&Some("left".into()));
2232+
assert_eq!(left, [-12.0, -18.0, -2.0], "l'iso de la v1.13.0");
22402233
assert_eq!(rotation3d_for(&Some("iso".into())), left);
2241-
assert_eq!(rotation3d_for(&Some("right".into())), [left[0], -left[1], 0.0]);
2234+
assert_eq!(rotation3d_for(&Some("right".into())), [left[0], -left[1], -left[2]]);
22422235
}
22432236

22442237
/// Aucune arête d'un préset ne doit longer un axe de l'image. C'est LE critère qui distingue
@@ -2374,7 +2367,7 @@ mod tilt_tests {
23742367
};
23752368
let centre = [0.5, 0.5];
23762369
assert!(coc(1920.0, 1080.0, centre, 0.5, 0.5) < 1e-4);
2377-
// Coin lointain (BL) : ~0.25 P, donc ~5.9 texels, bien au-dessus du seuil net (0.5).
2370+
// Coin lointain (BL) : ~0.19 P, donc ~4.6 texels, bien au-dessus du seuil net (0.5).
23782371
let far = coc(1920.0, 1080.0, centre, 0.0, 1.0);
23792372
assert!((3.5..6.0).contains(&far), "coin lointain {far}");
23802373
// Focus posé sur le coin proche (TR) : le lointain s'en éloigne encore.
@@ -2417,12 +2410,6 @@ mod tilt_tests {
24172410
.min(v(c[1], c[2]))
24182411
}
24192412

2420-
/// Débordement relatif du quad hors du rect d'origine (> 0 = déborde).
2421-
fn overflow(c: &[(f32, f32); 4], w: f32, h: f32) -> f32 {
2422-
let (mx, my) = projected_extents(c);
2423-
(mx / (w * 0.5)).max(my / (h * 0.5)) - 1.0
2424-
}
2425-
24262413
/// Toutes les parts dynamiques d'un budget réduit de `g`, sur une grille qui inclut les
24272414
/// coins de la boîte (le pire cas combine les deux axes).
24282415
fn budget_sweep(g: f32) -> impl Iterator<Item = [f32; 3]> {
@@ -2441,9 +2428,9 @@ mod tilt_tests {
24412428

24422429
/// La règle des 2° tient sur TOUT le balayage du budget, et pas qu'aux présets : à force 1
24432430
/// sur la boîte entière, et pendant l'ease-in, où la part dynamique ne doit jamais faire
2444-
/// passer une arête sous min(2°, ce que la base seule donne). Sous ~0,82 de force, `left`
2445-
/// et `right` sont DÉJÀ dans la bande (l'ease-in traverse 0°, puis le tangage où leur bord
2446-
/// haut ressort à plat) ; on ne leur demande alors que de ne pas empirer.
2431+
/// passer une arête sous min(2°, ce que la base seule donne). Sous ~0,48 de force, `left`
2432+
/// et `right` sont DÉJÀ dans la bande (l'ease-in traverse 0°) ; on ne leur demande alors que
2433+
/// de ne pas empirer.
24472434
#[test]
24482435
fn the_budget_sweep_keeps_every_edge_off_axis() {
24492436
let (w, h) = (1920.0f32, 1080.0f32);
@@ -2465,26 +2452,6 @@ mod tilt_tests {
24652452
}
24662453
}
24672454

2468-
/// Les chiffres du budget sont ceux des maths du crate, à peu près au ras : le balayage à 15 %
2469-
/// de plus sur X et Y ensemble fait casser à `left` une des deux règles, arête à moins de 2°
2470-
/// d'un axe (16:9) ou débordement de sa boîte (les trois formes), et Y seul aussi. X seul a
2471-
/// plus de marge : c'est avec Y qu'il la consomme. Z n'a pas de budget, c'est la règle produit.
2472-
#[test]
2473-
fn the_budget_is_close_to_what_left_allows() {
2474-
let base = preset("left");
2475-
let breaks = |d: [f32; 3]| {
2476-
[(1920.0f32, 1080.0f32), (1080.0, 1920.0), (800.0, 800.0)].into_iter().any(|(w, h)| {
2477-
let c = rotated_quad_corners_px(w, h, base, d).corners;
2478-
(w > h && min_edge_angle(&c) < 2.0) || overflow(&c, w, h) > 0.0
2479-
})
2480-
};
2481-
let b = DYNAMIC_TILT_BUDGET;
2482-
assert!(!budget_sweep(1.0).any(|d| breaks(d)), "le budget lui-même doit tenir");
2483-
assert!(budget_sweep(1.15).any(|d| breaks(d)), "X et Y");
2484-
assert!(breaks([0.0, b[1] * 1.15, 0.0]) || breaks([0.0, -b[1] * 1.15, 0.0]), "Y");
2485-
assert_eq!(b[2], 0.0, "Z n'a pas de budget");
2486-
}
2487-
24882455
/// Échelle gelée : la part dynamique ne fait que re-projeter les coins, et le quad ne
24892456
/// déborde toujours pas du rect d'origine — sur les trois formes de boîte de
24902457
/// `every_preset_stays_inside_the_original_rect`.
@@ -3359,7 +3326,7 @@ mod follow_camera_tests {
33593326
assert_eq!((blind.aim, blind.orbit), ([0.5, 0.5], [0.5, 0.5]));
33603327
// Un angle fixe garde exactement son état.
33613328
let left = zoom_state_in(&[region("left", 2.0, 8.0)], 5.0, Some(&tr), &f, &ScreenClock::default());
3362-
assert_eq!((left.rotation, left.camera, left.click_impact), ([-23.0, -25.0, 0.0], 0.0, 1.0));
3329+
assert_eq!((left.rotation, left.camera, left.click_impact), ([-12.0, -18.0, -2.0], 0.0, 1.0));
33633330
let unknown = zoom_state_in(&[region("orbit", 2.0, 8.0)], 5.0, Some(&tr), &f, &ScreenClock::default());
33643331
assert_eq!((unknown.rotation, unknown.tilt, unknown.camera), ([0.0; 3], 0.0, 0.0));
33653332
}

‎crates/compositor/src/shaders.hlsl‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ float3 quad_inverse_projective(float2 P, float2 c00, float2 c10, float2 c11, flo
253253
return float3(s, t, ok);
254254
}
255255

256-
// Le warp inverse d'un calque posé sur le plan : projectif (`projective` = 1, cf.
257-
// `TiltedQuad::warp_flag`, que tout écran incliné porte), bilinéaire sinon.
256+
// Le warp inverse d'un calque posé sur le plan : projectif sous la caméra réelle ou un appareil
257+
// (`projective` = 1, cf. `TiltedQuad::warp_flag`), bilinéaire sous un angle fixe, inchangé.
258258
float3 quad_inverse(float2 P, float2 c00, float2 c10, float2 c11, float2 c01, float projective)
259259
{
260260
if (projective > 0.5)
@@ -1455,8 +1455,8 @@ float4 ps_main(VSOut i) : SV_Target
14551455
// mode 8 : écran tilté en 3D (zoom regions "rotation" : iso/left/right) ou vu par la caméra
14561456
// réelle (`follow-cursor`). `dst`/`quad_px` couvrent la BOUNDING BOX des 4 coins projetés
14571457
// (`frame_geometry::tilted_screen_cb`) ; ce shader retrouve où tombe chaque pixel DANS le quad
1458-
// (warp inverse : projectif exact quand dst_prev.w = 1, ce que porte tout écran incliné,
1459-
// bilinéaire sinon) et échantillonne la vidéo à l'UV correspondant, sinon transparent.
1458+
// (warp inverse : bilinéaire sous un angle fixe, projectif exact sous la caméra réelle ou un
1459+
// appareil, dst_prev.w = 1) et échantillonne la vidéo à l'UV correspondant, sinon transparent.
14601460
// fx.xy/fx.zw = coins TL/TR (px locaux, 0..quad_px) ; src_prev.xy/.zw = coins BR/BL.
14611461
// color.xy (caméra réelle) : éclairage 1 + color.x·(s − 0.5) + color.y·(t − 0.5).
14621462
// mb = [gx, gy, z_focus, k] : profondeur du point r du plan = (r.x - 0.5)*gx + (r.y - 0.5)*gy

0 commit comments

Comments
 (0)