TL; DR: phối màu 2 * 1LSB phá vỡ hình tam giác-pdf trong edgecase ở 0 và 1 do kẹp. Một giải pháp là lerp để hòa sắc 1bit trong các edgecase đó.
Tôi đang thêm một câu trả lời thứ hai, vì điều này hóa ra hơi phức tạp hơn tôi nghĩ ban đầu. Có vẻ như vấn đề này đã là "TODO: cần kẹp?" trong mã của tôi kể từ khi tôi chuyển từ chuẩn hóa sang phối màu tam giác ... vào năm 2012. Cảm thấy tốt khi cuối cùng nhìn vào nó :) Mã đầy đủ cho giải pháp / hình ảnh được sử dụng trong suốt bài đăng: https://www.shadertoy.com/view/llXfzS
Trước hết, đây là vấn đề chúng tôi đang xem xét, khi định lượng tín hiệu thành 3 bit với phối màu tam giác 2 * 1LSB:
- về cơ bản những gì hotmultidia đã cho thấy.
Tăng độ tương phản, hiệu ứng được mô tả trong câu hỏi trở nên rõ ràng: Đầu ra không trung bình thành đen / trắng trong các edgecase (và thực sự kéo dài quá 0/1 trước khi thực hiện).
Nhìn vào biểu đồ cung cấp một cái nhìn sâu sắc hơn một chút:
(vạch màu xám đánh dấu 0/1, còn màu xám là tín hiệu chúng ta đang cố gắng phát ra, đường màu vàng là trung bình của đầu ra được phối màu / lượng tử hóa, màu đỏ là lỗi (trung bình tín hiệu)).
Thật thú vị, không chỉ là đầu ra trung bình không 0/1 ở các giới hạn, nó cũng không phải là tuyến tính (có thể là do pdf tam giác của tiếng ồn). Nhìn ở đầu dưới, nó có ý nghĩa trực quan tại sao đầu ra phân kỳ: Khi tín hiệu hòa sắc bắt đầu bao gồm các giá trị âm, đầu ra kẹp thay đổi giá trị của các phần được phối màu thấp hơn của đầu ra (nghĩa là các giá trị âm), do đó tăng giá trị trung bình. Một hình minh họa dường như theo thứ tự (hoà sắc, hòa sắc 2LSB đối xứng, trung bình vẫn có màu vàng):
Bây giờ, nếu chúng ta chỉ sử dụng một hoà sắc chuẩn hóa 1LSB, thì không có vấn đề gì ở các trường hợp cạnh, nhưng tất nhiên chúng ta sẽ mất các tính chất đẹp của phối màu tam giác (xem ví dụ: bản trình bày này ).
Sau đó, một giải pháp (thực dụng, theo kinh nghiệm) (hack) là hoàn nguyên về [-0,5; 0,5 [phối màu đồng nhất cho edgecase:
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) );
Cái nào sửa các edgecase trong khi vẫn giữ nguyên độ hoà sắc tam giác cho phạm vi còn lại:
Vì vậy, để không trả lời câu hỏi của bạn: Tôi không biết liệu có một giải pháp vững chắc hơn về mặt toán học hay không, và cũng rất muốn biết Masters of Past đã làm gì :) Cho đến lúc đó, ít nhất chúng ta có bản hack khủng khiếp này để giữ cho mã của chúng ta hoạt động.
EDIT
Có lẽ tôi nên đề xuất cách giải quyết được đưa ra trong Câu hỏi, chỉ đơn giản là nén tín hiệu. Bởi vì trung bình không phải là tuyến tính trong các edgecase, chỉ cần nén tín hiệu đầu vào không tạo ra kết quả hoàn hảo - mặc dù nó đã sửa các điểm cuối:
Người giới thiệu