diff --git a/README.md b/README.md index 0b4d019..eed8180 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#build--distribution) [![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/) [![Downloads](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=downloads)](https://github.com/CaYatur/SoundVisualizer/releases) -[![Tests](https://img.shields.io/badge/tests-2181%20passing-2ea043.svg)](#tests) +[![Tests](https://img.shields.io/badge/tests-2184%20passing-2ea043.svg)](#tests) [![cayadev.com](https://img.shields.io/badge/cayadev.com-e11d2a.svg)](https://cayadev.com) @@ -255,6 +255,12 @@ that asserts the bar profile has no step in it. flipping. A preset that leaves out its decay or gamma gets MilkDrop's 0.98 and 2.0 instead of 0, and so do most other values a preset leaves out; a rotation centre of 0 stays in the corner. Rendered through the engine, every case matches MilkDrop's formula within 2/255. +- **Composite shaders get the hue colour MilkDrop gives them.** MilkDrop passes every composite + shader four slowly drifting corner colours as `hue_shader`, whatever the preset's `fShader` + says; `fShader` only scales them on the fixed pipeline. The engine applied `fShader` to shaders + too, so about 950 presets of the corpus drew without their colour or with part of it, and the + corners were mirrored top to bottom. On the fixed pipeline the colour now goes through MilkDrop's + own draw passes, the wraparound of out-of-range colours included. - **Textured shapes, motion vectors, rotation matrices, mesh density, internal resolution scale, mouse input and preset transitions** are all implemented, and each sampler is read with the filtering and wrapping its name asks for (`sampler_pw_main` is point-sampled, `sampler_fc_main` is @@ -1299,7 +1305,7 @@ npm test npm start -- --smoke ``` -**2181 unit tests, all passing.** They are written to check answers, not to exercise lines: +**2184 unit tests, all passing.** They are written to check answers, not to exercise lines: - **Formulas** are checked against values derived by hand from their definitions — Viviani's curve staying on its sphere, the torus tube radius, Chladni's m↔n antisymmetry, every attractor diff --git a/README.tr.md b/README.tr.md index 771e520..9d41ef1 100644 --- a/README.tr.md +++ b/README.tr.md @@ -12,7 +12,7 @@ [![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-111997.svg)](#paketleme--dağıtım) [![Electron](https://img.shields.io/badge/Electron-43-47848F.svg)](https://www.electronjs.org/) [![İndirme](https://img.shields.io/github/downloads/CaYatur/SoundVisualizer/total?label=indirme)](https://github.com/CaYatur/SoundVisualizer/releases) -[![Test](https://img.shields.io/badge/test-2181%20geçiyor-2ea043.svg)](#testler) +[![Test](https://img.shields.io/badge/test-2184%20geçiyor-2ea043.svg)](#testler) [![cayadev.com](https://img.shields.io/badge/cayadev.com-e11d2a.svg)](https://cayadev.com) @@ -248,6 +248,12 @@ profilinde basamak olmadığını doğruluyor. yeniden beliriyor. Sönmesini ya da gamasını yazmayan bir preset 0 yerine MilkDrop'un 0,98 ve 2,0'ını alıyor, yazmadığı öteki değerlerin çoğu da öyle; dönme merkezi 0 ise köşede kalıyor. Motordan geçirilerek çizildi: her durum MilkDrop'un formülüyle 2/255 içinde aynı. +- **Birleştirme shader'ları MilkDrop'un verdiği ton rengini alıyor.** MilkDrop her birleştirme + shader'ına `hue_shader` olarak dört köşede yavaşça gezinen renkler veriyor, presetin `fShader`ı + ne derse desin; `fShader` onları yalnız sabit yolda ölçekliyor. Motor `fShader`ı shader'lara da + uyguluyordu: korpustaki yaklaşık 950 preset renksiz ya da rengin bir kısmıyla çiziliyordu, + köşeler de dikeyde aynalanmıştı. Sabit yolda renk artık MilkDrop'un kendi çizim geçişlerinden, + aralık dışı renklerin sarması dahil, geçiyor. - **Dokulu şekiller, hareket vektörleri, dönme matrisleri, ağ sıklığı, iç çözünürlük, fare girdisi ve preset geçişleri** uygulandı; her sampler adının istediği süzme ve sarma ile okunuyor (`sampler_pw_main` noktasal, `sampler_fc_main` süzülmüş+kenetli — presetlerin %22,7'si aynı @@ -1297,7 +1303,7 @@ npm test npm start -- --smoke ``` -**2181 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar: +**2184 birim testi, hepsi geçiyor.** Satır çalıştırmak için değil, cevap denetlemek için yazıldılar: - **Formüller**, tanımlarından elle türetilmiş değerlerle sınanıyor — Viviani eğrisinin küre üzerinde kalması, simidin boru yarıçapı, Chladni'nin m↔n antisimetrisi, her çekicinin sınırlı diff --git a/ROADMAP.md b/ROADMAP.md index 0936204..65e7b6a 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -129,9 +129,9 @@ npm test npm start -- --smoke ``` -- **2181 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0; +- **2184 unit tests, all passing** on `main`. 703 of those shipped in v3.1.0; 105 came with v3.1.1; 163 came with v3.1.2; 157 came with v3.1.3 — 1128 at - that tag — 469 more with v3.1.4, most of them from the MilkDrop work, and 584 + that tag — 469 more with v3.1.4, most of them from the MilkDrop work, and 587 on `main` since. Formulas are checked against values derived by hand from their definitions — Viviani's curve staying on its sphere, the @@ -949,23 +949,24 @@ MilkDrop (#560): preset changes class, and rendered alone in both trees it is identical - that flip is the picture it inherits from the preset before it in the run, not this change. The legacy run is identical byte for byte. -- **`hue_shader` only colours what the preset asked to colour** · done on - `main`. MilkDrop mixes the four corner colours towards white by the - preset's `fShader` amount, and skips them entirely below 0.001, leaving - white (milkdropfs.cpp:3857-3876); the default is zero (state.cpp:548). We - handed every preset the full colour. Of the 1,239 presets (12.0%) whose - composite shader reads `hue_shader`, 914 leave `fShader` at zero - their - picture was being tinted by a colour MilkDrop never sends - and 36 ask for - a partial amount. Six of those presets rendered alone in both trees all - differ, five of them by a lot (means of 3.6 to 26.9 of 255, up to 89% of - pixels past 8), and all six are identical with the fidelity switch off; on - the 900-preset sample no preset changes class. The same four colours then - reached the other half of the picture: MilkDrop draws its fixed composite - quad with them as vertex colours (milkdropfs.cpp:3940-3946) and we applied - none, so the 631 presets that have no composite shader and ask for a - non-zero `fShader` were missing the tint entirely. Six of them rendered - alone all differ (means of 0.3 to 13.0 of 255, up to 60% of pixels past 8) - and stay identical with the switch off. +- **`hue_shader` gets the colour MilkDrop sends** · done on `main`, + corrected in #580. MilkDrop builds four corner colours every frame and uses + them in two ways. Every composite shader gets the full colour as + `hue_shader`, whatever the preset's `fShader` says - the D3D11 and the D3D9 + code both note "since we don't know if shader uses it or not" + (milkdropfs.cpp:4122) - and `fShader` scales it only in the shader MilkDrop + writes for a preset without one. The fixed composite mixes the colours + towards white by `fShader`, skips them below 0.001 (milkdropfs.cpp: + 3857-3884; the default is zero, state.cpp:548) and draws its quad with them + as vertex colours (3940-3946). The first version of this change (16 + September) took the fixed composite's rule for the shaders too: with + fidelity on, the 914 presets whose composite shader reads `hue_shader` and + leaves `fShader` at zero lost their colour, and 36 got part of it. #580 gave + the full colour back and found the shader corners mirrored top to bottom as + well. The fixed composite applied no colour at all before this change; the + 631 presets without a composite shader that ask for a non-zero `fShader` + got the tint here and kept it, and #580 takes its amount from the file + instead of per-frame values and draws it as MilkDrop does. - **Motion vectors sit where MilkDrop puts them and point where it points** · done on `main`. Four differences in one field (milkdropfs.cpp:1172-1320): the count is truncated with its fraction widening the grid spacing, and the @@ -2030,9 +2031,9 @@ rest after. No version number yet. was equivalent, and the branch it changed was removed. Putting back the old ring nouns, the " · " in mash-up names or removing the module's script tag each fails a test. -- **Fidelity follow-ups (#580)** · the fixed composite, the stage rule and - MilkDrop's defaults are done; the other audits and the file-reading - differences are next. +- **Fidelity follow-ups (#580)** · the fixed composite, the stage rule, + MilkDrop's defaults and the hue colour are done; the remaining defaults, + the other audits and the file-reading differences are next. - **Checked against the source.** Nullsoft's own code (jecassis/foo_vis_milk2 5b44cea) and, for the fixed pipeline's blend passes, the D3D9 code the D3D11 fork was ported from (BeatDrop @@ -2101,25 +2102,59 @@ rest after. No version number yet. preset with `PSVERSION_WARP=0` was written as version 2 and drawn — so the mash-up rule version went from 1 to 2: a mash-up saved under the old rule keeps its file, and saving the recipe again writes a new one. - - **Found while finishing this, fixed next.** MilkDrop hands every - composite shader the full hue colour ("since we don't know if shader - uses it or not", in both the D3D11 and the D3D9 code); `fShader` scales - it only on the fixed path and in the shader MilkDrop writes for a - preset without one. The engine has applied `fShader` to preset shaders - too since the `hue_shader` change of 16 September, so with fidelity on - the 914 corpus presets that read `hue_shader` and leave `fShader` at 0 - lost their colour, and 36 got part of it. In the shader written for a - composite stage with a version and no text, a partial `fShader` now - lands twice, in the text and in the colour; the one corpus preset with - such a stage has `fShader` at 0. The engine also takes the - amount from per-frame values: 16 presets write `fshader` in code, - which MilkDrop never sees, all of them with a composite shader. Some - keys fall back to something else when a file leaves them out. MilkDrop - reads a missing `wave_r`, `wave_g`, `wave_b`, `wave_x` or `wave_y` as - `rot`'s value at that point, which is 0, where the engine has 1, 1, 1, - 0.5 and 0.5. Missing wave smoothing is 0.75, and the volume fade runs - from 0.75 to 0.95. Custom shapes default to red inside and green - outside. No corpus preset leaves any of these out, and neither do ours. + - **The hue colour, as MilkDrop gives it** (found while finishing the + first change). MilkDrop hands every composite shader the full hue + colour, and the engine had applied `fShader` there as well (see the + corrected #560 entry). With fidelity on, preset shaders get the full + colour again. Their corners were mirrored top to bottom too: MilkDrop + weighs them by the composite grid's screen position, with y at 1 on top + (plugin.cpp:1475-1490), and our translation flipped y. On the fixed + composite the amount is the file's `fShader`, blended linearly through a + transition like MilkDrop's other file values; 16 presets write `fshader` + in per-frame code, which MilkDrop never sees. It is no longer clamped to + 1, and each draw's corner colour goes through `COLOR_NORM`, so the 16 + presets with `fShader` 10 wrap into other colours as in MilkDrop. The + quad is interpolated as MilkDrop's two triangles, not bilinearly, and a + generated composite stage no longer gets a partial `fShader` twice. Echo + fading during a blend now looks at the new side's blended echo alpha, as + MilkDrop does, so the old echo fades out even when the new preset has + none. + - **Measured.** Four pure colours read back from the screen: corner 0 + sits top right in shaders and top left on the fixed quad, as in + MilkDrop, and the quad's centre is the mean of corners 1 and 2. The + fixed composite through the engine: 72 of 72 cases within 2/255, + including gamma 3.7 with echo 0.5, which is eight draws; a simulation + that rounds after every draw, as MilkDrop's 8-bit buffer does, differs + from our single rounding by at most 0.6/255 in those cases. The last + ten of 60 frames against `main`, up to 120 presets a group: + - shader presets that do not read `hue_shader`: 120 of 120 identical; + - `fShader` at 0: 95 of 120 changed by more than 1%, and their median + saturation went from 0.006 to 0.150; + - `fShader` 1 or more, where only the corners move: 90 of 120 changed, + median 4.3%; + - partial `fShader`: 28 of 36 changed; + - fixed composite with `fShader` above 0.001: 30 of 120 changed, + median 0.5%, and 12 of the 16 at 10; + - fixed composite without it: rounding only, the largest mean change + 0.36%. + + On the 900-preset sample one preset moves from blown to clean (882 → + 883 clean): it scales its picture by `hue_shader`, which was white on + `main`; rendered alone its brightness goes from 0.555 to 0.507 and its + share of pixels at full from 45.8% to 41.7%. + With fidelity off the fixed composite and the control group stay + byte-identical; presets that read `hue_shader` differ in at most 0.1% + of pixels by one level, the same four equal corner colours summed in + another order. + - **Next: keys that fall back to other values.** MilkDrop reads a + missing `wave_r`, `wave_g`, `wave_b`, `wave_x` or `wave_y` as `rot`'s + value at that point, which is 0, where the engine has 1, 1, 1, 0.5 and + 0.5. Missing wave smoothing is 0.75, and the volume fade runs from 0.75 + to 0.95. Custom shapes default to red inside and green outside. MilkDrop + also blends its other file-only values linearly through a transition + (wave scale and smoothing, the fade range, warp scale), where the engine + reads them from per-frame values. No corpus preset leaves any of these + keys out, and neither do ours. - **Not done yet:** the fixed warp path, the blur chain, borders and centre darkening, and the rest of the blend snap points; the file-reading differences found while building the mash-ups (a @@ -2127,8 +2162,12 @@ rest after. No version number yet. the last; numbered code that MilkDrop stops reading at the first missing number; `\\` comments; integer keys holding fractions; text after a number; key case; indented lines — about 40 corpus presets in - all); the reference comparison with an external renderer, which needs - one installed and waits for the user's approval. + all); whether `uv` in preset shaders runs the way MilkDrop's does — + read back from the screen, our `uv.y` is 1 at the top, where MilkDrop's + texture coordinate is 0; sampling agrees, but a shader doing arithmetic + on `uv.y` may come out mirrored, which needs its own check; the + reference comparison with an external renderer, which needs one + installed and waits for the user's approval. - **Tests.** 14 new: the version rule and the stage choice, the two generated shaders (float rounding, samplers, echo, hue, flag order, and that they translate), echo orientation and gamma cases, the @@ -2144,7 +2183,14 @@ rest after. No version number yet. check follow the new key; the mash-up tests check the part test against the engine's stage choice and expect `md_mix2_` ids. 36 of 36 mutations are caught, one of them putting the mash-up rule version - back to 1. + back to 1. The hue change adds three tests — the fixed quad's two + triangles run through the shader's own formula, the fixed amount taken + from the file and not from per-frame values, and its linear blend — and + reworks the hue tests: preset shaders at full colour for every + `fShader`; the fixed amount's threshold, partial mix and no clamp; the + draw weights (pass counts, the 1.0001 cut, the edge below −0.999, + wraparound, per-corner order); the echo fade with no echo on the new + side; and `hueAt` not flipping y. 18 of 18 mutations are caught. ## v3.1.6 — Comprehensive video export diff --git a/src/shared/milkdrop-shader.js b/src/shared/milkdrop-shader.js index 6d16c8e..676bc3c 100644 --- a/src/shared/milkdrop-shader.js +++ b/src/shared/milkdrop-shader.js @@ -503,10 +503,16 @@ tamamı hacim gürültüsünü veriyor; iki boyutlu aşırı yükleme yine de duruyor, çünkü korpus dışında bir preset 2B sampler geçirirse eskiden derlenen shader birden derlenmez olurdu. */ - /* Dort kose renginin cift dogrusal karisimi. MilkDrop'un tarama sirasi - ekranin USTUNDEN baslıyor, bizim `uv.y` ise altta sifir: bu yuzden y - ters cevriliyor. Ters cevrilmezse renk gecisi dikeyde aynalanirdi. */ - 'vec3 hueAt(vec2 p){ float x = p.x; float y = 1.0 - p.y;' + + /* Dört köşe renginin çift doğrusal karışımı, MilkDrop'un ağırlıklarıyla + (milkdropfs.cpp:4146-4160; BeatDrop'un D3D9 hâli aynı): köşe 0 x·y, + 1 (1−x)·y, 2 x·(1−y), 3 (1−x)·(1−y). x ve y birleştirme ağının + EKRAN konumundan geliyor ve y ekranın ÜSTÜNDE 1 — ağ `sy`yi üst + satırda +1 kuruyor (plugin.cpp:1475-1490) — yani 0 üst-sağ, 1 üst-sol, + 2 alt-sağ, 3 alt-sol. Bizim `uv.y` de ekranın üstünde 1: ağın ilk + satırı altta, ve ekrandan geri okunarak doğrulandı. Burada + `1.0 - p.y` vardı ("MilkDrop'un taraması üstten başlıyor" diye) ve + renk geçişini dikeyde aynalıyordu (#580). */ + 'vec3 hueAt(vec2 p){ float x = p.x; float y = p.y;' + ' return hue_corner[0] * x * y + hue_corner[1] * (1.0 - x) * y' + ' + hue_corner[2] * x * (1.0 - y) + hue_corner[3] * (1.0 - x) * (1.0 - y); }', 'vec3 tex3D(sampler3D s, vec3 uv2){ return texture(s, uv2).xyz; }', diff --git a/src/shared/milkdrop.js b/src/shared/milkdrop.js index 02ec8d2..97b2555 100644 --- a/src/shared/milkdrop.js +++ b/src/shared/milkdrop.js @@ -1642,20 +1642,70 @@ return (o % 2 !== 0 ? 1 : 0) | (o >= 2 ? 2 : 0); } - /* Sabit yolun gaması (milkdropfs.cpp:3955-4010): görüntü tam katları - kadar üst üste TOPLANARAK çiziliyor, son kat kesirli. Yankı açıkken - ilk çizim zaten tam; ek çizim ancak gama 1'i aştığında var, yani - 1'in altındaki gama yankıyla birlikte HİÇ uygulanmıyor. Yankı yokken - tek geçiş gamanın kendisiyle çiziliyor. */ - function fixedGammaGain(g, echoOn) { - const gamma = Math.fround(Number(g) || 0); - if (echoOn) return gamma > 0.001 && Math.trunc(gamma - 0.0001) >= 1 ? gamma : 1; - return gamma > 0 ? gamma : 0; + /* SABİT BİRLEŞTİRMENİN KÖŞE AĞIRLIKLARI (#580; milkdropfs.cpp:3907-4003, + BeatDrop'un D3D9 hâli 4090-4180 aynı). + + MilkDrop sabit yolda görüntüyü dokulu bir dörtgenle BİRKAÇ KEZ + çiziyor: ilki yazıyor, gerisi üstüne ekliyor. Her çizimin köşe rengi + (o çizimin gaması) × (katmanın payı) × (ton rengi) ve tepe rengi + yolundan, yani COLOR_NORM'dan geçiyor: bayta kırpılıyor, 1'i aşan ya + da eksiye düşen değer SARIYOR. Ekrandaki sonuç + + ana doku × Σ ana çizimlerin rengi + yankı dokusu × Σ yankı çizimlerinin rengi + + ve bu işlev iki toplamı köşe başına veriyor. Ton [0,1] içindeyken + toplam gama × pay × tonun bayta kırpılmışı; `fShader` 1'in üstündeyse + ton eksiye iniyor ve sarma onu başka bir renge çeviriyor (korpusta + sabit yolda 16 preset `fShader=10` yazıyor). + + - Yankı açık (saydamlık > 0,001): iki katman, ana `1 − a`, yankı `a` + payıyla. Her biri bir kez çiziliyor; gama 0,001'in üstündeyse + `(int)(gama − 0,0001)` kez daha, son tekrarın gaması kesirli kısım. + Yani 1'in altındaki gama yankıyla HİÇ uygulanmıyor. + - Yankı kapalı: `(int)(gama − 0,001) + 1` geçiş, sonuncunun gaması + kalan. Gama −0,999'un altındaysa hiç geçiş yok, dörtgen çizilmiyor + (bizde siyah). + + MilkDrop 8 bitlik tamponda her çizimden sonra yuvarlıyor; burada + toplam bir kez yuvarlanıyor. Hesap float32, MilkDrop'taki gibi. + `shade`: 12 sayı, köşe sırası üst-sol, üst-sağ, alt-sol, alt-sağ + (MilkDrop'un dörtgeni v3[0..3]). `out` verilirse dizileri yeniden + kullanılıyor. */ + function fixedCompWeights(gamma, echoAlpha, shade, out) { + const f = Math.fround; + const g = f(Number(gamma) || 0); + const a = f(Number(echoAlpha) || 0); + const main = out && out.main ? out.main.fill(0) : new Float32Array(12); + const echo = out && out.echo ? out.echo.fill(0) : new Float32Array(12); + // COLOR_NORM float32'de: (int)(x * 255) & 0xFF + const cn = (x) => (Math.trunc(f(x * 255)) & 0xFF) / 255; + const draw = (dst, k) => { for (let i = 0; i < 12; i++) dst[i] += cn(f(k * shade[i])); }; + /* Tam katlar 256'da kesiliyor: sıfır olmayan her çizim en az 1/255 + ekliyor, yani 255 kattan sonra kanal zaten doymuş. */ + const MAX_DRAWS = 256; + const echoOn = a > 0.001; + if (echoOn) { + for (let layer = 0; layer < 2; layer++) { + const mix = layer === 1 ? a : f(1 - a); + const dst = layer === 1 ? echo : main; + draw(dst, mix); + if (g > 0.001) { + const n = Math.trunc(f(g - f(0.0001))); + for (let r = Math.max(0, n - MAX_DRAWS); r < n; r++) { + draw(dst, f((r === n - 1 ? f(g - n) : 1) * mix)); + } + } + } + } else { + const n = Math.trunc(f(g - f(0.001))) + 1; + for (let p = Math.max(0, n - MAX_DRAWS); p < n; p++) draw(main, p === n - 1 ? f(g - p) : 1); + } + return { main, echo, echoOn }; } const api = { tokenize, parse, compile, Pool, FUNCS, parseMilk, Preset, clampColor, colorNorm, md2Versions, stagePlan, genWarpText, genCompText, - echoFlipBits, fixedGammaGain }; + echoFlipBits, fixedCompWeights }; if (typeof module !== 'undefined' && module.exports) module.exports = api; if (typeof window !== 'undefined') window.SVMilkdrop = api; })(); diff --git a/src/visualizer/modes/milkdrop.js b/src/visualizer/modes/milkdrop.js index 1370dd8..03cc166 100644 --- a/src/visualizer/modes/milkdrop.js +++ b/src/visualizer/modes/milkdrop.js @@ -254,27 +254,45 @@ in vec2 vUV; in float vBlend; out vec4 outColor; uniform sampler2D uSrc; -uniform float uGamma; -uniform float uEchoAlpha; +uniform float uGamma; // eski biçim +uniform float uEchoAlpha; // eski biçimde yankının payı; MilkDrop biçiminde yalnız "yankı var" uniform float uEchoZoom; uniform int uEchoOrient; uniform vec4 uFx; // brighten, darken, solarize, invert uniform float uFxMd2; // 1: MilkDrop 2'nin sabit yolunun biçimleri (#580) -uniform vec3 uHue[4]; // dört köşe rengi: üst-sol, üst-sağ, alt-sol, alt-sağ +/* MilkDrop biçimi: iki katmanın köşe ağırlıkları, köşe sırası üst-sol, + üst-sağ, alt-sol, alt-sağ. Her biri o katmanın çizimlerinin köşe + renklerinin toplamı — gama, pay ve ton rengi içinde (M.fixedCompWeights). */ +uniform vec3 uWMain[4]; +uniform vec3 uWEcho[4]; +/* MilkDrop'un dörtgeni İKİ ÜÇGEN (şerit v0 v1 v2 v3): ortak kenar + üst-sağdan alt-sola. Köşe rengi her üçgenin içinde doğrusal, çift + doğrusal değil — ortada dört köşenin değil 1 ile 2'nin ortalaması. */ +vec3 quad(vec3 w[4]) { + float x = vUV.x, y = vUV.y; + if (y >= x) return (y - x) * w[0] + x * w[1] + (1.0 - y) * w[2]; + return y * w[1] + (1.0 - x) * w[2] + (x - y) * w[3]; +} +vec2 echoUV() { + vec2 e = (vUV - 0.5) / max(0.001, uEchoZoom) + 0.5; + if (uEchoOrient == 1 || uEchoOrient == 3) e.x = 1.0 - e.x; + if (uEchoOrient == 2 || uEchoOrient == 3) e.y = 1.0 - e.y; + return e; +} void main(){ - vec3 c = texture(uSrc, vUV).rgb; - if (uEchoAlpha > 0.001) { - vec2 e = (vUV - 0.5) / max(0.001, uEchoZoom) + 0.5; - if (uEchoOrient == 1 || uEchoOrient == 3) e.x = 1.0 - e.x; - if (uEchoOrient == 2 || uEchoOrient == 3) e.y = 1.0 - e.y; - c = mix(c, texture(uSrc, e).rgb, uEchoAlpha); + vec3 c; + if (uFxMd2 > 0.5) { + /* MilkDrop 2: görüntü köşe rengiyle çarpılarak birkaç kez çiziliyor ve + toplanıyor (milkdropfs.cpp:3907-4003). Gama, yankının payı ve dört + köşe rengi (fShader) ağırlıkların içinde, çizim başına bayta + sarılmış olarak. */ + c = texture(uSrc, vUV).rgb * quad(uWMain); + if (uEchoAlpha > 0.001) c += texture(uSrc, echoUV()).rgb * quad(uWEcho); + } else { + c = texture(uSrc, vUV).rgb; + if (uEchoAlpha > 0.001) c = mix(c, texture(uSrc, echoUV()).rgb, uEchoAlpha); + c *= uGamma; } - /* MilkDrop dort kose rengini SABIT yolda da uyguluyor: tam ekran dortgeni - bu renklerle ciziliyor ve donanim aralarini dolduruyor - (milkdropfs.cpp:3857-3884, 3940-3946). Shader'li yolda ayni renkler - hue_shader olarak gidiyordu; sabit yolda hic uygulanmiyordu. */ - c *= mix(mix(uHue[2], uHue[3], vUV.x), mix(uHue[0], uHue[1], vUV.x), vUV.y); - c *= uGamma; c = clamp(c, 0.0, 1.0); /* MilkDrop'un MD1 donemi sabit efektleri. Bunlar shader'dan onceki surumlerden kalma ama eski presetlerin cogu hala kullaniyor; yoklugunda @@ -1147,7 +1165,8 @@ void main(){ uEchoOrient: gl.getUniformLocation(this.compFixed, 'uEchoOrient'), uFx: gl.getUniformLocation(this.compFixed, 'uFx'), uFxMd2: gl.getUniformLocation(this.compFixed, 'uFxMd2'), - uHue: gl.getUniformLocation(this.compFixed, 'uHue[0]'), + uWMain: gl.getUniformLocation(this.compFixed, 'uWMain[0]'), + uWEcho: gl.getUniformLocation(this.compFixed, 'uWEcho[0]'), }; this.locBlur = { uSrc: gl.getUniformLocation(this.blurProg, 'uSrc'), @@ -2326,13 +2345,19 @@ void main(){ preset yarı parlaklıkta çıkıyordu (varsayılan artık kare başı tabanında, shared/milkdrop.js MD2_PF_DEFAULTS); - yön `(int)echo_orient % 4` (shared/milkdrop.js `echoFlipBits`); - - gama görüntüyü üst üste toplayarak çiziyor ve yankıyla birlikte - 1'in altında HİÇ uygulanmıyor (`fixedGammaGain`); - - GEÇİŞTE iki presetin de dosyasında yankı varsa (saydamlık 0,01'in - üstünde) ve yönleri farklıysa, yankı atlama noktasına kadar ESKİ - yönle sönüyor, sonra yeni yönle yeniden beliriyor — yön bir anda + - gama ham dönüyor: görüntüyü üst üste toplayarak çizen geçişler ve + yankıyla birlikte 1'in altında uygulanmaması köşe ağırlıklarında + (shared/milkdrop.js `fixedCompWeights`); + - GEÇİŞTE iki tarafta da yankı varsa (saydamlık 0,01'in üstünde) ve + yönleri farklıysa, yankı atlama noktasına kadar ESKİ yönle + sönüyor, sonra yeni yönle yeniden beliriyor — yön bir anda dönmüyor. Karşılaştırılan DOSYADAKİ değerler; MilkDrop da onlara - bakıyor, denklemlerin yazdığına değil. */ + bakıyor, denklemlerin yazdığına değil. Yeni tarafın saydamlığı + geçiş boyunca KARIŞAN dosya değeri (`m_fVideoEchoAlpha.eval`), + eski tarafınki geçiş başındaki değer: yeni presette yankı kapalı + olsa da karışan değer 0,01'i aşıyor ve eski yankı yine sönerek + gidiyor (#580'in ilk hâlinde yeni presetin ham değerine + bakılıyordu ve yankı bir anda dönüyordu). */ _fixedCompInputs(Pp) { const M = window.SVMilkdrop; /* Gama ve yakınlaşma havuzdan: dosyanın yazmadığı adda MilkDrop'un @@ -2347,7 +2372,7 @@ void main(){ const v = P && P.file && P.file.params ? P.file.params[k] : undefined; return typeof v === 'number' && isFinite(v) ? v : d; }; - const na = fp(Pp, 'fvideoechoalpha', 0); + const na = this._fileVal('fvideoechoalpha', 0); const oa = fp(O, 'fvideoechoalpha', 0); const no = Math.trunc(fp(Pp, 'nvideoechoorientation', 0)); const oo = Math.trunc(fp(O, 'nvideoechoorientation', 0)); @@ -2362,7 +2387,26 @@ void main(){ } } } - return { alpha, zoom, orient, gain: M.fixedGammaGain(Pp.get('gamma'), alpha > 0.001) }; + return { alpha, zoom, orient, gamma: Pp.get('gamma') }; + } + + /* DOSYADAN OKUNAN, KARE DEĞİŞKENİ OLMAYAN ayarlar — `fShader` gibi. + MilkDrop bunları denklemlere açmıyor: kodun aynı adla yazdığı değer + bir kullanıcı değişkeni olarak kalıyor. Geçişte eski ve yeni presetin + dosyadaki değeri arasında DOĞRUSAL, ham ilerlemeyle karışıyorlar + (`CBlendableFloat::eval`, state.cpp:1933-1953) — kosinüs eğrisi yok. + `key` dosyanın küçük harfli anahtarı; yazılmamışsa `dflt`, yani + MilkDrop'un varsayılanı. */ + _fileVal(key, dflt) { + const get = (P) => { + const v = P && P.file && P.file.params ? P.file.params[key] : undefined; + return typeof v === 'number' && isFinite(v) ? v : dflt; + }; + const cur = get(this.preset); + const O = this.oldPreset; + if (!O || !(this.blendProg < 1)) return cur; + const p = Math.max(0, Math.min(1, this.blendProg)); + return get(O) * (1 - p) + cur * p; } /* MilkDrop'un dönme matrisleri: rot_s/d/f/vf/uf/rand 1..4. @@ -2472,47 +2516,51 @@ void main(){ return L; } - /* DÖRT KÖŞE RENGİ (`hue_shader`). Hem shader'lı yol (uniform) hem de - sabit birleştirme yolu (tepe rengi) bunu kullanıyor; MilkDrop ikisini - de aynı `shade[4][3]` dizisinden besliyor (milkdropfs.cpp:3857-3884). - Miktar presetin `fShader`ı: 0,001'in altındaysa dört köşe de beyaz. */ - _hueCorners(P, t, rand) { + /* DÖRT KÖŞE RENGİ. MilkDrop her kare dört köşeye ayrı fazlarla bir renk + hesaplıyor — sabit yolda (milkdropfs.cpp:3857-3884) ve shader'lı + yolda (4122-4140) aynı formül — ve rengi beyaza doğru `amt` oranıyla + karıştırıyor. Oranı ÇAĞIRAN veriyor, çünkü iki yol ayrı: + - shader'lı yol HER ZAMAN 1: MilkDrop "shader kullanıyor mu + bilmiyoruz" diye `hue_shader`a tam rengi veriyor (4122, BeatDrop + 4318); `fShader` yalnız kendi yazdığı birleştirme metninde çarpan. + 52c7391 (#560) oranı buraya da uyguluyordu ve `fShader`ı 0 olan + 914 preset rengini kaybediyordu (#580); + - sabit yol: presetin DOSYADAKİ `fShader`ı, geçişte doğrusal karışan + (`_fileVal`); 0,001'in altındaysa renk hiç hesaplanmıyor, köşeler + beyaz kalıyor. + Oran kenetlenmiyor: 1'in üstünde renk 0,5'in altına, eksiye iniyor ve + sabit yolda COLOR_NORM onu sarıyor (M.fixedCompWeights). + Dizideki sıra yalnız köşenin NUMARASI; ekrandaki yerini iki yol + kendisi veriyor ve MilkDrop'ta da ayrı: sabit yolun dörtgeninde 0 + üst-sol, shader'ın `hueAt`inde 0 üst-sağ. Uyum kapalıyken motorun + eski tek rengi. */ + _hueCorners(amt, t, rand) { const accurate = this._wantAcc !== false; const rs = rand || this.randPreset || [0, 0, 0, 0]; const hc = this._hueBuf || (this._hueBuf = new Float32Array(12)); - /* RENGİN MİKTARI presetin `fShader`ından. MilkDrop dört köşe rengini - hesapladıktan sonra beyaza doğru bu oranla karıştırıyor ve oran - 0,001'in altındaysa hiç hesaplamıyor: dört köşe de (1,1,1) - kalıyor (milkdropfs.cpp:3857-3876). Varsayılan 0 - (state.cpp:548). Motor rengi HER presete veriyordu: korpusta - `hue_shader` okuyan 1.239 presetin 914'ü `fShader`ı sıfır - bırakıyor, yani MilkDrop'ta hiç renk almayan bir görüntüyü - renklendiriyorduk; 36'sı da ara bir oran yazıyor. */ - const amt = accurate - ? Math.max(0, Math.min(1, +(P && P.get('fshader')) || 0)) : 1; - for (let i = 0; i < 4; i++) { - let r, g, b; - if (accurate && amt <= 0.001) { - r = 1; g = 1; b = 1; - } else if (accurate) { - const k = i; - r = 0.6 + 0.3 * Math.sin(t * 30 * 0.0143 + 3 + k * 21 + rs[3]); - g = 0.6 + 0.3 * Math.sin(t * 30 * 0.0107 + 1 + k * 13 + rs[1]); - b = 0.6 + 0.3 * Math.sin(t * 30 * 0.0129 + 6 + k * 9 + rs[2]); - const mx = Math.max(r, g, b) || 1; - r = 0.5 + 0.5 * (r / mx); - g = 0.5 + 0.5 * (g / mx); - b = 0.5 + 0.5 * (b / mx); - r = r * amt + (1 - amt); - g = g * amt + (1 - amt); - b = b * amt + (1 - amt); - } else { - r = 0.5 + 0.5 * Math.sin(t * 0.31); - g = 0.5 + 0.5 * Math.sin(t * 0.31 + 2.09); - b = 0.5 + 0.5 * Math.sin(t * 0.31 + 4.19); - } - hc[i * 3] = r; hc[i * 3 + 1] = g; hc[i * 3 + 2] = b; + for (let i = 0; i < 4; i++) { + let r, g, b; + if (accurate && !(amt > 0.001)) { + r = 1; g = 1; b = 1; + } else if (accurate) { + const k = i; + r = 0.6 + 0.3 * Math.sin(t * 30 * 0.0143 + 3 + k * 21 + rs[3]); + g = 0.6 + 0.3 * Math.sin(t * 30 * 0.0107 + 1 + k * 13 + rs[1]); + b = 0.6 + 0.3 * Math.sin(t * 30 * 0.0129 + 6 + k * 9 + rs[2]); + const mx = Math.max(r, g, b) || 1; + r = 0.5 + 0.5 * (r / mx); + g = 0.5 + 0.5 * (g / mx); + b = 0.5 + 0.5 * (b / mx); + r = r * amt + (1 - amt); + g = g * amt + (1 - amt); + b = b * amt + (1 - amt); + } else { + r = 0.5 + 0.5 * Math.sin(t * 0.31); + g = 0.5 + 0.5 * Math.sin(t * 0.31 + 2.09); + b = 0.5 + 0.5 * Math.sin(t * 0.31 + 4.19); } + hc[i * 3] = r; hc[i * 3 + 1] = g; hc[i * 3 + 2] = b; + } return hc; } @@ -2640,8 +2688,12 @@ void main(){ Anahtar KAPALIYKEN dort koseye de AYNI renk gidiyor: yapi ayni kaliyor (yine dort kose, yine ayni shader), yalnizca degerler - motorun eski tek-renk davranisini veriyor. */ - if (L.hue_corner) gl.uniform3fv(L.hue_corner, this._hueCorners(ctx.P, t, rand)); + motorun eski tek-renk davranisini veriyor. + + Oran HER ZAMAN 1, presetin `fShader`ı ne olursa olsun: MilkDrop + shader'a tam rengi veriyor ve kullanıp kullanmamayı shader'a + bırakıyor (milkdropfs.cpp:4122). Geçişte de hesaplanıyor. */ + if (L.hue_corner) gl.uniform3fv(L.hue_corner, this._hueCorners(1, t, rand)); /* Presetin kendisi bu uniform'ları okuyabiliyor (`b1n`/`b1x` olarak yazıp shader'da `blur1_min` diye geri okuyor; korpusta altı preset @@ -3455,8 +3507,18 @@ void main(){ const acc = this._wantAcc !== false; if (acc) { const f = this._fixedCompInputs(Pp); - gl.uniform1f(this.locComp.uGamma, f.gain); - gl.uniform1f(this.locComp.uEchoAlpha, f.alpha); + /* Dört köşe rengi de burada: MilkDrop tam ekran dörtgenini bu + renklerle çiziyor (milkdropfs.cpp:3857-3884, 3940-3946). + Korpusta comp shader'ı olmayan 2.128 presetin 631'i sıfırdan + büyük bir `fShader` yazıyor. Oran DOSYADAN, geçişte doğrusal + karışarak; renk dizisi paylaşılan tampon, hemen ağırlıklara + dönüşüyor (çizim başına COLOR_NORM, M.fixedCompWeights). */ + const shade = this._hueCorners(this._fileVal('fshader', 0), this.time, this.randPreset); + const w = window.SVMilkdrop.fixedCompWeights(f.gamma, f.alpha, shade, + this._compW || (this._compW = { main: new Float32Array(12), echo: new Float32Array(12) })); + gl.uniform3fv(this.locComp.uWMain, w.main); + gl.uniform3fv(this.locComp.uWEcho, w.echo); + gl.uniform1f(this.locComp.uEchoAlpha, w.echoOn ? 1 : 0); gl.uniform1f(this.locComp.uEchoZoom, f.zoom); gl.uniform1i(this.locComp.uEchoOrient, f.orient); } else { @@ -3470,17 +3532,6 @@ void main(){ Pp.get('brighten') ? 1 : 0, Pp.get('darken') ? 1 : 0, Pp.get('solarize') ? 1 : 0, Pp.get('invert') ? 1 : 0); if (this.locComp.uFxMd2) gl.uniform1f(this.locComp.uFxMd2, acc ? 1 : 0); - /* Dört köşe rengi burada da: MilkDrop tam ekran dörtgenini bu - renklerle çiziyor (milkdropfs.cpp:3940-3946), shader'lı yolla - aynı `shade` dizisinden. Korpusta comp shader'ı olmayan 2.129 - presetin 631'i sıfırdan büyük bir `fShader` yazıyor; onlarda - ekran boyunca gezen bu ton hiç çizilmiyordu. Uyum kapalıyken - beyaz: eski sabit yol rengi hiç uygulamıyordu. */ - if (this.locComp.uHue) { - gl.uniform3fv(this.locComp.uHue, this._wantAcc !== false - ? this._hueCorners(Pp, this.time, this.randPreset) - : (this._hueWhite || (this._hueWhite = new Float32Array(12).fill(1)))); - } } gl.bindVertexArray(this.vao); gl.drawElements(gl.TRIANGLES, this.indexCount, gl.UNSIGNED_INT, 0); diff --git a/tests/milkdrop-fidelity-comp.test.js b/tests/milkdrop-fidelity-comp.test.js index eaf9974..e6a2788 100644 --- a/tests/milkdrop-fidelity-comp.test.js +++ b/tests/milkdrop-fidelity-comp.test.js @@ -99,18 +99,43 @@ test('sabit yol yankı yönü: (int)x % 4, C işaret kuralıyla', () => { for (const [v, want] of cases) assert.strictEqual(M.echoFlipBits(v), want, String(v)); }); -test('sabit yol gaması: yankıyla birlikte 1\'in altı uygulanmıyor', () => { - const on = (g) => M.fixedGammaGain(g, true); - const off = (g) => M.fixedGammaGain(g, false); - assert.strictEqual(on(0.5), 1); - assert.strictEqual(on(0), 1); - assert.strictEqual(on(1), 1); - assert.strictEqual(on(1.00005), 1, 'kırpma sınırı 1,0001'); - assert.strictEqual(on(1.5), 1.5); - assert.strictEqual(on(2), 2); - assert.strictEqual(off(0.5), 0.5); - assert.strictEqual(off(0), 0, 'yankısız gama 0 siyah'); - assert.strictEqual(off(2), 2); +/* Sabit yolun gaması ve yankısı MilkDrop'un ÇİZİMLERİ olarak: her çizimin + köşe rengi gama × pay × ton ve COLOR_NORM'dan geçiyor (bayta kırpma, + taşanı sarma); ekrandaki çarpan çizimlerin toplamı + (milkdropfs.cpp:3907-4003). */ +test('sabit yol gaması ve yankısı: MilkDrop\'un çizimleri, çizim başına COLOR_NORM', () => { + const W = (g, a, s) => M.fixedCompWeights(g, a, new Array(12).fill(s === undefined ? 1 : s)); + const b = (x) => Math.trunc(Math.fround(x * 255)) / 255; // [0,1] içinde bayta kırpma + const near = (v, want, msg) => assert.ok(Math.abs(v - want) < 1e-6, msg + ': ' + v + ' ≠ ' + want); + // Yankı yok: (int)(g − 0,001) + 1 geçiş, sonuncusunun gaması kalan + near(W(2, 0).main[0], 2, 'gama 2 iki tam çizim'); + near(W(1.5, 0).main[0], 1 + b(0.5), 'gama 1,5: 1 + bayta kırpılmış 0,5'); + near(W(0.5, 0).main[0], b(0.5), 'gama 0,5 tek geçiş'); + near(W(0, 0).main[0], 0, 'yankısız gama 0 siyah'); + near(W(-1, 0).main[0], 0, 'gama −0,999 altında hiç çizim yok'); + near(W(-0.9995, 0).main[0], 0, 'sınır (int)(g − 0,001): −0,9995 de çizilmiyor'); + near(W(-0.5, 0).main[0], 129 / 255, 'eksi gama bayta SARIYOR'); + assert.strictEqual(W(2, 0).echoOn, false); + assert.ok(W(2, 0).echo.every((v) => v === 0), 'yankısız yankı katmanı boş'); + // Yankı açık: iki katman bir kez paylarıyla; ek çizim (int)(g − 0,0001) kez + let w = W(0.5, 0.5); + assert.strictEqual(w.echoOn, true); + near(w.main[0], b(0.5), '1\'in altındaki gama yankıyla uygulanmıyor (ana)'); + near(w.echo[0], b(0.5), '1\'in altındaki gama yankıyla uygulanmıyor (yankı)'); + near(W(1.00005, 0.5).main[0], b(0.5), 'kırpma sınırı 1,0001'); + w = W(2.5, 0.3); + near(w.main[0], 2 * b(0.7) + b(0.35), 'ana: pay, 1 × pay, 0,5 × pay'); + near(w.echo[0], 2 * b(0.3) + b(0.15), 'yankı: aynı çizimler'); + near(W(1, 0.0005).main[0], 1, 'saydamlık 0,001 ve altı yankı sayılmıyor'); + // Ton eksiye inerse (fShader 1'in üstünde) sarma başka bir renk veriyor + near(W(1, 0, -0.5).main[0], 129 / 255, 'ton −0,5'); + near(W(1, 0, -2.3).main[0], (Math.trunc(Math.fround(-2.3 * 255)) & 0xFF) / 255, 'ton −2,3'); + // Çok büyük gama doyuruyor; döngü sınırlı + assert.ok(W(1e6, 0).main[0] >= 1); + // Köşeler ayrı ayrı, dizideki sırasıyla + const s = [0.5, 0.6, 0.7, 0.8, 0.9, 1, 0.55, 0.65, 0.75, 0.85, 0.95, 0.52]; + const m2 = M.fixedCompWeights(1, 0, s).main; + s.forEach((v, i) => near(m2[i], b(v), 'köşe bileşeni ' + i)); }); // ------------------------------------------------------- varsayılanlar @@ -311,6 +336,15 @@ test('motor: geçişte yönü farklı iki yankı sönüp yeniden beliriyor', () f = m._fixedCompInputs(m.preset); assert.strictEqual(f.alpha, 0.5); assert.strictEqual(f.orient, 2); + /* Yeni presette yankı kapalı: yeni tarafın saydamlığı geçişte KARIŞAN + dosya değeri (m_fVideoEchoAlpha.eval), o 0,01'in üstünde kaldıkça eski + yankı yine eski yönle sönerek gidiyor */ + m.preset = mk(0, 0); + m.oldPreset = mk(1, 0.5); + m.blendProg = 0.25; + assert.strictEqual(m._fixedCompInputs(m.preset).orient, 1, 'yeni tarafta yankı yok: yine eski yön'); + m.blendProg = 0.999; + assert.strictEqual(m._fixedCompInputs(m.preset).orient, 0, 'karışan değer 0,0005: sönüm yok'); // Geçiş yokken yön (int) % 4 m.oldPreset = null; m.preset = mk(5, 0.5); @@ -327,5 +361,35 @@ test('sabit birleştirme shader\'ı: MilkDrop 2 biçimleri anahtarın arkasında // Sabit geçiş uyum açıkken girdilerini MilkDrop'un kuralından alıyor assert.match(code, /const acc = this\._wantAcc !== false;\s*if \(acc\) \{\s*const f = this\._fixedCompInputs\(Pp\);/); assert.match(code, /gl\.uniform1i\(this\.locComp\.uEchoOrient, f\.orient\);/); - assert.match(code, /gl\.uniform1f\(this\.locComp\.uGamma, f\.gain\);/); + // Gama, yankının payı ve ton köşe ağırlıklarında; tonun oranı dosyadan + assert.match(code, /const shade = this\._hueCorners\(this\._fileVal\('fshader', 0\), this\.time, this\.randPreset\);/); + assert.match(code, /window\.SVMilkdrop\.fixedCompWeights\(f\.gamma, f\.alpha, shade,/); + assert.match(code, /gl\.uniform3fv\(this\.locComp\.uWMain, w\.main\);/); + assert.match(code, /gl\.uniform3fv\(this\.locComp\.uWEcho, w\.echo\);/); + // MilkDrop biçiminde uEchoAlpha yalnız "yankı dalı var" bayrağı + assert.match(code, /gl\.uniform1f\(this\.locComp\.uEchoAlpha, w\.echoOn \? 1 : 0\);/); + assert.match(code, /c = texture\(uSrc, vUV\)\.rgb \* quad\(uWMain\);\s*if \(uEchoAlpha > 0\.001\) c \+= texture\(uSrc, echoUV\(\)\)\.rgb \* quad\(uWEcho\);/); + // Eski biçim kendi dalında, değişmeden + assert.match(code, /c = texture\(uSrc, vUV\)\.rgb;\s*if \(uEchoAlpha > 0\.001\) c = mix\(c, texture\(uSrc, echoUV\(\)\)\.rgb, uEchoAlpha\);\s*c \*= uGamma;/); +}); + +/* MilkDrop'un sabit dörtgeni iki üçgen (şerit v0 v1 v2 v3; ortak kenar + üst-sağdan alt-sola): köşe rengi her üçgenin içinde DOĞRUSAL. Shader'daki + formül burada sayılarla koşturuluyor — köşelerde köşenin kendisi, ortada + 1 ile 2'nin ortalaması (çift doğrusalda dördünün ortalaması olurdu). */ +test('sabit dörtgen: köşe rengi iki üçgende doğrusal, ortak kenar üst-sağ → alt-sol', () => { + const code = read('src/visualizer/modes/milkdrop.js'); + const q = /vec3 quad\(vec3 w\[4\]\) \{\s*float x = vUV\.x, y = vUV\.y;\s*if \(y >= x\) return ([^;]+);\s*return ([^;]+);\s*\}/.exec(code); + assert.ok(q, 'quad() bulunamadı'); + const fn = new Function('w', 'x', 'y', 'return y >= x ? (' + q[1] + ') : (' + q[2] + ');'); + const w = [10, 20, 30, 40]; // 0 üst-sol, 1 üst-sağ, 2 alt-sol, 3 alt-sağ + assert.strictEqual(fn(w, 0, 1), 10, 'üst-sol'); + assert.strictEqual(fn(w, 1, 1), 20, 'üst-sağ'); + assert.strictEqual(fn(w, 0, 0), 30, 'alt-sol'); + assert.strictEqual(fn(w, 1, 0), 40, 'alt-sağ'); + assert.strictEqual(fn(w, 0.5, 0.5), 25, 'orta: 1 ile 2\'nin ortalaması'); + // Ortak kenarın iki yanı birleşiyor + for (const t of [0.1, 0.3, 0.7]) { + assert.ok(Math.abs(fn(w, t, t + 1e-9) - fn(w, t + 1e-9, t)) < 1e-6, 'kenarda süreksiz: ' + t); + } }); diff --git a/tests/milkdrop-fidelity.test.js b/tests/milkdrop-fidelity.test.js index f1fead6..4a6fc37 100644 --- a/tests/milkdrop-fidelity.test.js +++ b/tests/milkdrop-fidelity.test.js @@ -99,12 +99,15 @@ test('çeviri: hueAt dört köşeyi çift doğrusal karıştırıyor', () => { assert.match(r.glsl, /hue_corner\[3\] \* \(1\.0 - x\) \* \(1\.0 - y\)/); }); -/* MilkDrop'un tarama sırası ekranın ÜSTÜNDEN başlıyor, bizim `uv.y` ise - altta sıfır. Ters çevrilmezse renk geçişi dikeyde aynalanır — hata - vermez, yalnız yanlış görünür. */ -test('çeviri: hueAt dikeyde ters çeviriyor', () => { +/* MilkDrop'un ağırlığındaki y birleştirme ağının EKRAN konumu ve ekranın + üstünde 1 (plugin.cpp:1475-1490, `sy` üst satırda +1); bizim `uv.y` de + üstte 1. Burada eskiden `1.0 - p.y` vardı ve renk geçişi dikeyde + aynalanıyordu (#580) — hata vermez, yalnız yanlış görünür. Ekrandaki + yerleşim bu testin sınayamadığı şey; o ölçümle doğrulandı. */ +test('çeviri: hueAt y\'yi çevirmiyor (MilkDrop\'ta y ekranın üstünde 1)', () => { const r = T.translate('shader_body { ret = hue_shader; }'); - assert.match(r.glsl, /float y = 1\.0 - p\.y;/); + assert.match(r.glsl, /float y = p\.y;/); + assert.doesNotMatch(r.glsl, /float y = 1\.0 - p\.y;/); }); test('çeviri: warp aşaması da hue_shader alıyor', () => { @@ -114,10 +117,11 @@ test('çeviri: warp aşaması da hue_shader alıyor', () => { test('motor: köşe renkleri tek seferde yükleniyor', () => { assert.match(CODE, /L\.hue_corner = u\('hue_corner\[0\]'\)/); - /* Renkler ayrı bir yöntemde: shader'lı yol uniform olarak, sabit - birleştirme yolu da tepe rengi olarak aynı diziyi kullanıyor. */ - assert.match(CODE, /gl\.uniform3fv\(L\.hue_corner, this\._hueCorners\(ctx\.P, t, rand\)\)/); - assert.match(CODE, /gl\.uniform3fv\(this\.locComp\.uHue, this\._wantAcc !== false/); + /* Renkler ayrı bir yöntemde: shader'lı yol uniform olarak tam renkle + (oran 1), sabit birleştirme yolu dosyadaki fShader oranıyla ve köşe + ağırlıklarına çevirerek aynı yöntemi kullanıyor (#580). */ + assert.match(CODE, /gl\.uniform3fv\(L\.hue_corner, this\._hueCorners\(1, t, rand\)\)/); + assert.match(CODE, /this\._hueCorners\(this\._fileVal\('fshader', 0\), this\.time, this\.randPreset\)/); // Dört köşe x üç bileşen assert.match(CODE, /new Float32Array\(12\)/); }); @@ -140,7 +144,7 @@ test('motor: köşe rengi en büyük bileşene bölünüp yeniden haritalanıyor /* Anahtar kapalıyken YAPI aynı kalıyor: yine dört köşe, yine aynı shader, yine aynı uniform. Yalnız dördüne de aynı renk gidiyor. */ test('motor: anahtar kapalıyken dört köşe de aynı rengi alıyor', () => { - const blk = /_hueCorners\(P, t, rand\) \{[\s\S]*?return hc;/.exec(CODE); + const blk = /_hueCorners\(amt, t, rand\) \{[\s\S]*?return hc;/.exec(CODE); assert.ok(blk, 'köşe rengi yöntemi bulunamadı'); assert.match(blk[0], /r = 0\.5 \+ 0\.5 \* Math\.sin\(t \* 0\.31\)/); assert.match(blk[0], /g = 0\.5 \+ 0\.5 \* Math\.sin\(t \* 0\.31 \+ 2\.09\)/); diff --git a/tests/milkdrop-hue-shader.test.js b/tests/milkdrop-hue-shader.test.js index 0501d3c..3cbcc1b 100644 --- a/tests/milkdrop-hue-shader.test.js +++ b/tests/milkdrop-hue-shader.test.js @@ -1,13 +1,21 @@ 'use strict'; -/* HUE_SHADER'IN MİKTARI: presetin `fShader` ayarı (#560). +/* DÖRT KÖŞE RENGİ VE `fShader` (#560, düzeltme #580). * - * MilkDrop dört köşe rengini hesapladıktan sonra beyaza doğru `fShader` - * oranıyla karıştırıyor, ve oran 0,001'in altındaysa rengi hiç - * hesaplamıyor — dört köşe de (1,1,1) kalıyor (milkdropfs.cpp:3857-3876). - * Varsayılan 0 (state.cpp:548). Motor rengi HER presete veriyordu. + * MilkDrop her kare dört köşe için ayrı fazlarla bir renk hesaplıyor ve iki + * yolda iki ayrı şekilde kullanıyor: + * - SHADER'LI YOL: `hue_shader`a HER ZAMAN tam renk gidiyor, `fShader` ne + * olursa olsun ("shader kullanıyor mu bilmiyoruz", milkdropfs.cpp:4122; + * BeatDrop'un D3D9 hâli 4318 aynı). `fShader` yalnız MilkDrop'un kendi + * yazdığı birleştirme metninde çarpan. + * - SABİT YOL: renk beyaza doğru presetin `fShader` oranıyla karışıyor ve + * oran 0,001'in altındaysa hiç hesaplanmıyor — köşeler beyaz + * (milkdropfs.cpp:3857-3884). Oran dosyadaki değer, geçişte doğrusal + * karışıyor (CBlendableFloat); kenetlenmiyor. * - * Korpusta comp shader'ında `hue_shader` okuyan 1.239 preset var (%12,0); - * bunların 914'ü `fShader`ı sıfır bırakıyor, 36'sı ara bir oran yazıyor. + * 52c7391 (#560) sabit yolun kuralını shader'lı yola da uyguluyordu: + * korpusta `hue_shader` okuyan 1.239 presetin 914'ü `fShader`ı sıfır + * bırakıyor ve rengini kaybediyordu, 36'sı rengin yalnız bir kısmını + * alıyordu. */ const test = require('node:test'); const assert = require('node:assert'); @@ -25,6 +33,10 @@ function method(sig) { } const body = (src) => src.slice(src.indexOf('{') + 1, src.lastIndexOf('}')); +// Köşe rengi yöntemi kaynaktan +const hueCorners = new Function('amt', 't', 'rand', body(method('_hueCorners(amt, t, rand)'))); +const fileVal = new Function('key', 'dflt', body(method('_fileVal(key, dflt)'))); + /* Motorun kendi uniform yükleyicisini koşturur ve `hue_corner`a yazılan on iki sayıyı döndürür. Yalnız o uniform'un konumu verildiği için ötekilerin hepsi `if (L[n])` denetiminden geçemiyor ve atlanıyor. */ @@ -49,13 +61,13 @@ function corners(opts) { _texSizeFor: () => [1, 1, 1, 1], _rotRows: () => new Float32Array(12), _blurScaleBias: () => [1, 0], - // köşe renkleri ayrı bir yöntemde; o da kaynaktan geliyor - _hueCorners: new Function('P', 't', 'rand', body(method('_hueCorners(P, t, rand)'))), + _hueCorners: hueCorners, blur: [], preset: null, }; const L = { hue_corner: 'HC', _plan: [], _texSize: [], _rot: [] }; - const P = { get: (k) => (k === 'fshader' ? opts.fshader : 0) }; + // Presetin hem dosyası hem havuzu fShader'ı taşıyor; shader yolu ikisine de bakmamalı + const P = { get: (k) => (k === 'fshader' ? opts.fshader : 0), file: { params: { fshader: opts.fshader } } }; const ctx = { w: 960, h: 720, aspectx: 1, aspecty: 0.75, aspX: 1, aspY: 0.75, time: opts.time === undefined ? 12.5 : opts.time, @@ -68,26 +80,18 @@ function corners(opts) { return got; } -test('fShader sıfırken dört köşe de beyaz', () => { - for (const v of [0, 0.0005, undefined, -1]) { - const hc = corners({ fshader: v }); - assert.deepStrictEqual(hc, new Array(12).fill(1), 'fShader = ' + v); - } -}); +// ------------------------------------------------------------ shader yolu -test('fShader 1 iken tam renk, ara değerde beyaza karışıyor', () => { +test('shader yolu: fShader ne olursa olsun tam renk', () => { const full = corners({ fshader: 1 }); - const half = corners({ fshader: 0.5 }); - assert.ok(full.some((v) => v < 0.999), 'tam oranda renk yok'); - for (let i = 0; i < 12; i++) { - // yarı oran: tam rengin beyazla ortası - assert.ok(Math.abs(half[i] - (full[i] * 0.5 + 0.5)) < 1e-6, 'köşe ' + i); - assert.ok(half[i] >= full[i] - 1e-9, 'yarı oran daha koyu çıktı'); + assert.ok(full.some((v) => v < 0.999), 'renk yok'); + for (const v of [0, 0.0005, undefined, -1, 0.5, 10]) { + assert.deepStrictEqual(corners({ fshader: v }), full, 'fShader = ' + v); } }); -test('köşeler birbirinden farklı ve en parlak bileşen 1 ile ölçekli', () => { - const hc = corners({ fshader: 1 }); +test('shader yolu: köşeler birbirinden farklı ve en parlak bileşen 1 ile ölçekli', () => { + const hc = corners({ fshader: 0 }); const c = [0, 1, 2, 3].map((i) => hc.slice(i * 3, i * 3 + 3)); assert.ok(c.some((v, i) => i > 0 && v.some((x, k) => Math.abs(x - c[0][k]) > 1e-6)), 'dört köşe aynı renk'); @@ -110,20 +114,59 @@ test('uyum kapalıyken eski tek renk duruyor', () => { } }); -/* SABİT birleştirme yolu da aynı renkleri uyguluyor: MilkDrop tam ekran - dörtgenini bu dört renkle çiziyor (milkdropfs.cpp:3940-3946). Motorda o - yol rengi hiç uygulamıyordu; comp shader'ı olmayan 2.129 presetin 631'i - sıfırdan büyük bir fShader yazıyor. */ -test('sabit birleştirme yolu köşe renklerini ekran boyunca uyguluyor', () => { +// -------------------------------------------------------------- sabit yol + +const hue = (amt) => Array.from(hueCorners.call({ _wantAcc: true, randPreset: [0.1, 0.2, 0.3, 0.4] }, + amt, 12.5, [0.1, 0.2, 0.3, 0.4])); + +test('sabit yolun oranı: 0,001 ve altı beyaz, ara değer beyaza karışıyor, 1\'in üstü kenetlenmiyor', () => { + for (const v of [0, 0.0005, 0.001, -1, NaN, undefined]) { + assert.deepStrictEqual(hue(v), new Array(12).fill(1), 'oran ' + v); + } + const full = hue(1); + const half = hue(0.5); + const ten = hue(10); + for (let i = 0; i < 12; i++) { + assert.ok(Math.abs(half[i] - (full[i] * 0.5 + 0.5)) < 1e-6, 'yarı oran, bileşen ' + i); + assert.ok(Math.abs(ten[i] - (full[i] * 10 - 9)) < 1e-4, 'oran 10, bileşen ' + i); + } + // Oran 10'da renk 0,5'in altına, eksiye iniyor — COLOR_NORM onu sarıyor + assert.ok(ten.some((v) => v < 0), 'oran 10 kenetlenmiş'); +}); + +test('sabit yolun oranı dosyadan: denklemin yazdığı fshader MilkDrop\'a ulaşmıyor', () => { + // Havuz 1 diyor (denklem yazmış), dosya 0,4 + const P = { get: () => 1, file: { params: { fshader: 0.4 } } }; + assert.strictEqual(fileVal.call({ preset: P, oldPreset: null, blendProg: 1 }, 'fshader', 0), 0.4); + const bos = { get: () => 1, file: { params: {} } }; + assert.strictEqual(fileVal.call({ preset: bos, oldPreset: null, blendProg: 1 }, 'fshader', 0), 0, + 'yazılmamışsa MilkDrop\'un varsayılanı'); +}); + +test('sabit yolun oranı geçişte eski ve yeni dosya değeri arasında doğrusal', () => { + const mk = (v) => ({ file: { params: { fshader: v } } }); + const self = { preset: mk(0), oldPreset: mk(1), blendProg: 0.25 }; + assert.ok(Math.abs(fileVal.call(self, 'fshader', 0) - 0.75) < 1e-12, 'kosinüs değil, ham ilerleme'); + self.blendProg = 1; + assert.strictEqual(fileVal.call(self, 'fshader', 0), 0, 'geçiş bitti'); + self.blendProg = 0.5; + self.oldPreset = { file: { params: {} } }; + assert.ok(Math.abs(fileVal.call(Object.assign({}, self, { preset: mk(1) }), 'fshader', 0) - 0.5) < 1e-12, + 'eski tarafta yazılmamış: varsayılandan'); +}); + +/* Sabit birleştirme yolu: ton rengi dosyadaki orandan, köşe ağırlıklarına + çevrilerek (çizim başına COLOR_NORM). Uyum kapalıyken renk hiç + hesaplanmıyor — eski sabit yol rengi uygulamıyordu. */ +test('sabit birleştirme yolu köşe rengini ağırlıklarla dörtgene uyguluyor', () => { const frag = /const COMP_FIXED_FRAG = `([\s\S]*?)`;/.exec(SRC)[1]; - assert.match(frag, /uniform vec3 uHue\[4\];/); - // Alt satır 2-3, üst satır 0-1: dörtgenin köşe sırası - assert.match(frag, /c \*= mix\(mix\(uHue\[2\], uHue\[3\], vUV\.x\), mix\(uHue\[0\], uHue\[1\], vUV\.x\), vUV\.y\);/); - // Gama çarpanından ÖNCE: MilkDrop rengi katmana uygulayıp sonra gama için - // katmanı yeniden çiziyor - assert.ok(frag.indexOf('uHue[2]') < frag.indexOf('c *= uGamma;'), 'renk gamadan sonra uygulanıyor'); + assert.match(frag, /uniform vec3 uWMain\[4\];/); + assert.match(frag, /uniform vec3 uWEcho\[4\];/); + assert.doesNotMatch(frag, /uHue/, 'eski tek çarpanlı renk kalmamalı'); const pass = method('_drawCompPass(gl, dst, prog, ctx)'); - assert.match(pass, /this\._hueCorners\(Pp, this\.time, this\.randPreset\)/); - assert.match(pass, /new Float32Array\(12\)\.fill\(1\)/); // uyum kapalıyken beyaz + const acc = /if \(acc\) \{([\s\S]*?)\} else \{([\s\S]*?)\}\s*gl\.uniform4f/.exec(pass); + assert.ok(acc, 'uyum dalları bulunamadı'); + assert.match(acc[1], /this\._hueCorners\(this\._fileVal\('fshader', 0\), this\.time, this\.randPreset\)/); + assert.doesNotMatch(acc[2], /_hueCorners/, 'uyum kapalıyken renk yok'); });