Есть у алгоритма penumbra wedges один существенный недостаток, бороться с которым очень сложно и в общем случае - невозможно. Недостаток заключается в том, что алгоритм неверно определяет coverage источника света в случае, если два или более ребра несколько раз перекрывают одну и ту же область - т. н.
occluder fusion. Проблема вытекает из того, что пиксельный шейдер работает отдельно с каждым ребром, которое проецируется на источник света, а не со всеми сразу, и расчёт для каждого ребра никак не учитывают влияние "соседей". Как компромисс авторы в далёком 2003 предлагали использовать т. н. probabilistic approach, при котором силуэты от двух разных кастеров рисуются в отдельные RT и потом они смешиваются по формуле
c = max(Ca, Cb) + 1/2 * min(Ca, Cb)
что в результате даёт значение между двумя экстремумами. Также есть пейпр, где сабж пытались побороть с помощью penumbra wedges blending (Vincent Forest et al., Realistic Soft Shadows by Penumbra-Wedges Blending), но техника выглядит очень заумной и нежизнеспособной в real-time и не стоит подробного рассмотрения.
Хотя single point silhouette approximation представляется мне более сложной и важной проблемой, серьёзно влияющей на визуальное качество теней, occluder fusion также не давал мне покоя и я потихоньку думал, как её можно обойти на DX 11 железе. Ясно, что проблема лежит в аналитическом методе расчёта пенумбры, из-за которого, собственно, мягкие тени и выглядят как настоящие тени, а не как их жалкое подобие. Правильное решение заключается в интегрировании площади источника света с помощью множества сэмплов на его поверхности, как делается, например, в методе depth complexity sampling. Но применённый алгоритм получается медленным и не годится для real-time.
Быстрое "решение" пришло в голову, когда я обдумывал преимущества использования
coverage lookup table для упрощения пиксельного шейдера. Итак, у нас есть предрассчитанный coverage для ребра с произвольными координатами двух вершин. А что, если разбить источник света на N сэмплов, и вместо coverage предрассчитывать маску охвата, как это делают GPU при мультисэмплинге? Получается маска вида 0000101100100010, её можно сохранить в integer текстуре. В Direct3D 11 доступен формат DXGI_FORMAT_R32G32B32A32_UINT, так что можно разбить площадь и. с. на 128 сэмплов при последующей одной выборке в пиксельном шейдере.
Теперь, делая такую выборку для каждого отдельного ребра, мы может просто ORить маски охвата (SM 4.0 железо и выше умеет работать с отдельными битами) и в конце получить результирующую маску, которая и описывает правильный coverage. Теперь достаточно подсчитать количество битов, выставленных в 1 (в SM 5.0 для этого даже есть подходящий интринсик - countbits(uint4
value)) и получить отношение к общему их кол-ву. В результате мы корректно проинтегрируем скрытую (видимую) площадь источника света, представленную в промежутке [0..1], которую можно сразу записывать в coverage буфер.
И все :)
P. S. На работе в непосредственной близости от меня появилась DirectX 11 система (Windows 7 + Radeon HD 5850, так что дело скоро сдвинется с места).