TL; DR: 2 * 1LSB трикутне-pdf відмирання в обрізних краях на 0 і 1 за рахунок затискання. Рішення полягає в тому, щоб лерпувати до 1-бітної рівномірної дистрибуції в цих крайових шарах.
Я додаю другу відповідь, бачачи, як це виявилося трохи складніше, ніж я спочатку думав. Здається, ця проблема була "TODO: потребує затискання?" у моєму коді, оскільки я перейшов з нормалізованого на трикутне забарвлення ... у 2012 році. Добре, нарешті, перегляньте його: Повний код для рішення / зображень, які використовуються у публікації: https://www.shadertoy.com/view/llXfzS
Перш за все, ось проблема, яку ми розглядаємо, під час кількісного визначення сигналу на 3 біти з тригранним двостороннім розширенням у форматі pdf:
- по суті те, що показала гаряча мультимедіа.
Зростаючий контраст, ефект, описаний у запитанні, стає очевидним: Вихід не має середнього до чорного / білого в крайових шафах (а насправді виходить за межі 0/1 перед цим).
Перегляд графіку дає трохи більше розуміння:
(сірі лінії позначають 0/1, також сірим кольором є сигнал, який ми намагаємося виводити, жовта лінія - це середній розмірний / квантований вихід, червоний - помилка (середній сигнал)).
Цікаво, що середня потужність не лише 0/1 в межах, вона також не є лінійною (ймовірно, через трикутного pdf шуму). Дивлячись на нижній кінець, має інтуїтивний сенс, чому вихід розходиться: Коли розрядний сигнал починає включати негативні значення, затискаючий-вихідний змінює значення нижніх зрізаних частин виходу (тобто негативних значень), тим самим збільшення значення середнього. Ілюстрація, здається, в порядку (рівномірна, симетрична нітра 2LSB, середня все ще жовта):
Тепер, якщо ми просто використовуємо нормалізовану дибуру на 1LSB, у крайових випадків проблем взагалі немає, але, звичайно, ми втрачаємо приємні властивості трикутного відшарування (див., Наприклад, цю презентацію ).
Тож (прагматичне, емпіричне) рішення (хак) - повернутись до [-0,5; 0,5 [рівномірне забарвлення для крайового шару:
float dithertri = (rnd.x + rnd.y - 1.0); //note: symmetric, triangular dither, [-1;1[
float dithernorm = rnd.x - 0.5; //note: symmetric, uniform dither [-0.5;0.5[
float sizt_lo = clamp( v/(0.5/7.0), 0.0, 1.0 );
float sizt_hi = 1.0 - clamp( (v-6.5/7.0)/(1.0-6.5/7.0), 0.0, 1.0 );
dither = lerp( dithernorm, dithertri, min(sizt_lo, sizt_hi) );
Який фіксує крайові шафи, зберігаючи трикутну обмацування недоторканою для решти діапазону:
Тож, щоб не відповісти на ваше запитання: я не знаю, чи є більш математично обґрунтоване рішення, і я однаково прагну знати, що зробили Masters of Past :) До того часу, принаймні, у нас є цей жахливий хак, щоб підтримувати наш код.
EDIT
Я, мабуть, повинен висвітлити попереджувальну пропозицію, подану в запитанні, щодо простого стиснення сигналу. Оскільки середнє значення не є лінійним у крайових шарах, просто стискання вхідного сигналу не дає ідеального результату - хоча це фіксує кінцеві точки:
Список літератури