image

Аналогов подобных теней для точечного источника света (Pointlight с эффектом размытия на расстоянии, имитирующий arealight) в компьютерных играх я почему-то до сих пор не встречал. Везде — либо полностью запечённые тени, либо «лампочки» вообще без теней, максимум — обыкновенная PCF-фильтрация. Хотя для направленного солнечного света уже давно применяются PCSS-тени (GTA5, например). В Unreal есть интересный алгоритм сродни рейтрейсингу, который рисует красивые arealight-тени, но только для статической геометрии (требуется генерация дополнительных объёмов). В Unity же всё совсем плохо — мягко фильтруется только солнечный свет, а «прожекторы» и «лампочки» в пролёте.

Если на «прожекторах» в Unity висит хоть какая-то худо-бедно билинейная фильтрация, то на точечных «лампочках» такой вот ужас:



Неужели, добавить обыкновенную PCF-фильтрацию — это такой удар по производительности? Даже если и удар, то почему не сделать возможность включения этой фильтрации опционально?

Как выяснилось, удара по производительности нет. В Unity 5 действует технология deffered lighting (отложенное освещение), и чем больше в кадре источников света с динамической тенью, тем хуже производительность. А уж какая фильтрация у этих теней — не столь важно. Осуществляется одно и то же число проходов, обрабатывается столько же пикселей, этот момент как раз и требует оптимизации сцены. А число выборок из кубической текстуры глубины «лампочки» пусть и влияет на производительность, но очень незначительно (в районе пары FPS).

Также стоит сказать, что размывать полученную тень image-эффектом не получится. Нет никакой прослойки между выборкой из текстуры глубины и наложением света, такая уж система в Unity. А жаль, можно было бы в чём-то выиграть.

Итак, меняем алгоритм

Создавать специальные материалы ради мягких теней кропотливо, вносить изменения в имеющиеся шейдеры скриптом (как это сделано в ShadowSoftener) — тоже как-то не очень удобно. Гораздо быстрее и практичнее применить тени сразу ко всем шейдерам в проекте (встроенным, рукописным, скачанным), изменив всего один файл «UnityShadowLibrary.cginc», который находится в директории редактора: "...\Unity5\Editor\Data\CGIncludes".

Находим этот кусочек, который отвечает за тень от точечного источника света:

    #if defined (SHADOWS_SOFT)

        float z = 1.0/128.0;
        float4 shadowVals;
        shadowVals.x = SampleCubeDistance (vec+float3( z, z, z));
        shadowVals.y = SampleCubeDistance (vec+float3(-z,-z, z));
        shadowVals.z = SampleCubeDistance (vec+float3(-z, z,-z));
        shadowVals.w = SampleCubeDistance (vec+float3( z,-z,-z));
        half4 shadows = (shadowVals < mydist.xxxx) ? _LightShadowData.rrrr : 1.0f;

        return dot(shadows,0.25); 

    #else 


На место него можно поставить любой понравившийся нам алгоритм, но сначала разберём входные параметры:

vec — четырёхмерный вектор. x,y и z — это направление от «лампочки» к пикселю. Делая выборку по этому значению с помощью SampleCubeDistance, мы получаем расстояние затенившего объекта. Если расстояние больше длины vec, тень на пиксель не падает. (mydist — то самое расстояние, но это не входной праметр, он вычисляется выше в этом же файле) В каждой выборке координата смещается на фиксированную величину, создавая эффект неоднородной границы. Не смейтесь. Профессиональные программисты, разрабатывающие Unity, называют это «мягкой тенью».

_LightShadowData.r — это значение ползунка, настраивает яркость тени. Можно воспользоваться им, например, для изменения степени размытия тени или для изменения какого-то другого параметра, чтобы отлаживать шейдер непосредственно в редакторе. К сожалению, я так и не выяснил, на что влияют другие компоненты _LightShadowData. Видимо, для точечного источника света больше параметров нет.

Возвращаем из функции яркость (0 до 1.0), т.е. множитель, который во всех шейдерах действует под именем atten (attenuation), поэтому изменённая тень будет действовать тоже во всех шейдерах, поддерживающих затенение.

Чтобы увидеть изменения, найдите папку своего проекта, в ней – папку «Library». Удаляем из этой папки директорию «ShaderCache» и перезапускаем Unity. Я тоже перезапускал Unity после каждого редактирования шейдера, это нервировало и усложняло отладку.

Вот и всё.

Я попробовал вот такой вариант, замазав края неказистым дизерингом:

   #if defined (SHADOWS_SOFT)

        // Чем меньше, тем тень размытестее
        float downscale = 32.0f;

        // Случайный вектор
        const float3 rndseed = float3(12.9898,78.233,45.5432);
        float3 randomvec = float3( dot(vec,rndseed) , dot(vec.yzx,rndseed) , dot(vec.zxy,rndseed) );
        randomvec = frac(sin(randomvec) * 43758.5453);

        // Вот эти вектора для смещений
        float3 xvec = normalize(cross(vec,randomvec));
        float3 yvec = normalize(cross(vec,xvec));
        float3 vec1 = xvec / downscale;
        float3 vec2 = yvec / downscale;

        float4 shadowVals;

        // Выборки из кубмапы
        shadowVals.x = SampleCubeDistance (vec+vec1);
        shadowVals.y = SampleCubeDistance (vec+vec2);
        shadowVals.z = SampleCubeDistance (vec-vec1);
        shadowVals.w = SampleCubeDistance (vec-vec2);

        // Смешиваем
        half4 shadows = (shadowVals < mydist.xxxx) ? _LightShadowData.rrrr : 1.0f;
        return dot(shadows,0.25);

    #else 


Выборок столько же, сколько и в жуткой каноничной реализации, но, по крайней мере, пиксели не мозолят глаза:



Не долго думая (но долго отлаживая) сделал такую вот реализацию PCF4x4 с «ручной» билинейной фильтрацией (в шейдеры Unity можно, конечно, вставлять команды DirectX11, среди которых есть и билинейная фильтрация теней кубмапа, но я пошёл по пути универсальности и сделал дополнительный ряд выборок для «ручного» сглаживания, получилось не 16 выборок, а 25).

image

Или даже вот так:



Это не честный PCSS, а более быстрая и простая реализация. Максимальное размытие — это то же самое PCF4x4. А чёткая тень вблизи затенителя достигается увеличением резкости. Между прочим, эффект shapen тоже много где применяется. Здесь же у нас симбиоз, с помощью которого можно добиться красивого эффекта.

Выше упомянутый бегунок настраивает угол перехода мягкой тени в резкую. Этот параметр можно подбирать разным образом для разных «лампочек», исходя из размеров помещений, в которых они находятся.

Код последней реализации я поместил под кат. Он немного громоздок и неопрятен, ведь я не использовал двумерный цикл выборок, а прописывал их вручную, чтобы снизить число команд и количество используемых переменных, а заодно применить операции с векторами там, где можно вычислить сразу несколько переменных. Плюс я принципиально не использовал условные переходы, заменив их математикой. Поэтому смотрите на свой страх и риск.

Некрономикон
inline half UnitySampleShadowmap (float3 vec)
{
	float mydist = length(vec) * _LightPositionRange.w;
	mydist *= 0.97; // bias

	const float downscale = 128.0f;
	const float sat_mult = 312.0f;

	#if defined (SHADOWS_SOFT)

		#define shadow_close_scalefactor (_LightShadowData.r + 0.001f)

		// Виртуальные оси и их преобразование в настоящие
		#define xvec main_axis.zxy
		#define yvec main_axis.yzx
		#define VIRTUAL_COORD(x,y) (ceilvec + xvec*x + yvec*y)

		// Нам надо узнать, вдоль какой плоскости направить изначальный вектор
		// (ось вперёд для правильного направления пикселей)
		half3 main_axis = abs(vec)*1666.666f;
		main_axis = normalize(clamp(main_axis.xyz - main_axis.yzx,0.0f,1.0f)*clamp(main_axis.xyz - main_axis.zxy,0.0f,1.0f));

		// Упираем вектор в кубмап
		vec /= abs(dot(vec,main_axis));

		// Это центр виртуального пикселя, относительно которого будет вестись интерполяция цвета
		fixed3 ceilvec = ceil(vec*downscale) / downscale;

		// От нуля до единицы - значения для интерполяции между 4-мя положениями
		fixed4 lerp_delta;
		vec = (ceilvec - vec) * downscale;
		lerp_delta.x = dot(vec * xvec,1.0f);
		lerp_delta.y = dot(vec * yvec,1.0f);
		lerp_delta.z = 1.0f - lerp_delta.x;
		lerp_delta.w = 1.0f - lerp_delta.y;

		// Подготовка к выборкам
		main_axis /= downscale;
		ceilvec -= (xvec + yvec)*0.5f;

		//Переменные и паттерны для выборки
		float4 shadowVals, distance_sums, distancesides; fixed4 shadowsides, shadow_sums, distancesides_nums, distance_nums;
		#define DISTANCE_COMPARE_X4(sum,distance_sum,distance_num) shadowVals = mydist.xxxx - shadowVals;  sum = dot(clamp(shadowVals    *sat_mult,0.0f,1.0f),1.0f); distance_sum = dot(clamp(shadowVals,    0.0f,100.0f),1.0f);  distance_num = dot(clamp(shadowVals    *sat_mult,0.0f,1.0f),1.0f)
		#define DISTANCE_COMPARE_X3(sum,distance_sum,distance_num) shadowVals = mydist.xxxx - shadowVals;  sum = dot(clamp(shadowVals.xyz*sat_mult,0.0f,1.0f),1.0f); distance_sum = dot(clamp(shadowVals.xyz,0.0f,100.0f),1.0f);  distance_num = dot(clamp(shadowVals.xyz*sat_mult,0.0f,1.0f),1.0f)
		#define DISTANCE_COMPARE_X1(sum,distance_sum,distance_num) shadowVals.x = mydist  - shadowVals.x;  sum =     clamp(shadowVals.x  *sat_mult,0.0f,1.0f);       distance_sum =     clamp(shadowVals.x,  0.0f,100.0f);		  distance_num =     clamp(shadowVals.x  *sat_mult,0.0f,1.0f)

		// Выборка значений из центральной области - для всех положений одинакова
		// Первые 4 пикселя
		shadowVals.x = SampleCubeDistance (VIRTUAL_COORD(-1.0f,-1.0f));
		shadowVals.y = SampleCubeDistance (VIRTUAL_COORD(0.0f,-1.0f));
		shadowVals.z = SampleCubeDistance (VIRTUAL_COORD(1.0f,-1.0f));
		shadowVals.w = SampleCubeDistance (VIRTUAL_COORD(1.0f,0.0f));
		DISTANCE_COMPARE_X4(shadowsides.x,distancesides.x,distancesides_nums.x);
		// Вторые 4 пикселя
		shadowVals.x = SampleCubeDistance (VIRTUAL_COORD(-1.0f,-0.0f));
		shadowVals.y = SampleCubeDistance (VIRTUAL_COORD(-1.0f,1.0f));
		shadowVals.z = SampleCubeDistance (VIRTUAL_COORD(0.0f,1.0f));
		shadowVals.w = SampleCubeDistance (VIRTUAL_COORD(1.0f,1.0f));
		DISTANCE_COMPARE_X4(shadowsides.y,distancesides.y,distancesides_nums.y);
		// Центральный пиксель
		shadowVals.x = SampleCubeDistance (VIRTUAL_COORD(0.0f,0.0f));
		DISTANCE_COMPARE_X1(shadowsides.z,distancesides.z,distancesides_nums.z);
		// Раскладываем суммы по 
		shadow_sums = dot(shadowsides.xyz,1.0f).xxxx;
		distance_sums = dot(distancesides.xyz,1.0f).xxxx;
		distance_nums = dot(distancesides_nums.xyz,1.0f).xxxx + fixed4(0.01f,0.01f,0.01f,0.01f);

		// Выборка значений для индивидуальных областей

		// Лево
		shadowVals.x = SampleCubeDistance (VIRTUAL_COORD(-2.0f,-1.0f));
		shadowVals.y = SampleCubeDistance (VIRTUAL_COORD(-2.0f,0.0f));
		shadowVals.z = SampleCubeDistance (VIRTUAL_COORD(-2.0f,1.0f));
		DISTANCE_COMPARE_X3(shadowsides.x,distancesides.x,distancesides_nums.x);
		// Низ
		shadowVals.x = SampleCubeDistance (VIRTUAL_COORD(-1.0f,-2.0f));
		shadowVals.y = SampleCubeDistance (VIRTUAL_COORD(0.0f,-2.0f));
		shadowVals.z = SampleCubeDistance (VIRTUAL_COORD(1.0f,-2.0f));
		DISTANCE_COMPARE_X3(shadowsides.y,distancesides.y,distancesides_nums.y);
		// Право
		shadowVals.x = SampleCubeDistance (VIRTUAL_COORD(2.0f,-1.0f));
		shadowVals.y = SampleCubeDistance (VIRTUAL_COORD(2.0f,0.0f));
		shadowVals.z = SampleCubeDistance (VIRTUAL_COORD(2.0f,1.0f));
		DISTANCE_COMPARE_X3(shadowsides.z,distancesides.z,distancesides_nums.z);
		// Верх
		shadowVals.x = SampleCubeDistance (VIRTUAL_COORD(-1.0f,2.0f));
		shadowVals.y = SampleCubeDistance (VIRTUAL_COORD(0.0f,2.0f));
		shadowVals.z = SampleCubeDistance (VIRTUAL_COORD(1.0f,2.0f));
		DISTANCE_COMPARE_X3(shadowsides.w,distancesides.w,distancesides_nums.w);
		// Раскладываем суммы по соответствующим секциям
		shadow_sums += (shadowsides.xzxz + shadowsides.yyww);
		distance_sums += (distancesides.xzxz + distancesides.yyww);
		distance_nums += distancesides_nums.xzxz + distancesides_nums.yyww;

		// Угловые точки
		shadowVals.x = SampleCubeDistance (VIRTUAL_COORD(-2.0f,-2.0f));
		shadowVals.y = SampleCubeDistance (VIRTUAL_COORD(2.0f,-2.0f));
		shadowVals.z = SampleCubeDistance (VIRTUAL_COORD(-2.0f,2.0f));
		shadowVals.w = SampleCubeDistance (VIRTUAL_COORD(2.0f,2.0f));
		// Считаем вручную, ибо нефиг дефайны плодить ради одного вызова
		shadowVals = mydist.xxxx - shadowVals;
		shadow_sums += clamp(shadowVals*sat_mult,0.0f,1.0f);
		distance_sums += clamp(shadowVals,0.0f,1.0f);
		distance_nums += clamp(shadowVals*sat_mult,0.0f,1.0f);

		// Интерполируем между четырьмя позициями
		shadow_sums.x = dot(shadow_sums * lerp_delta.xzxz * lerp_delta.yyww, 1.0f) / 16.0f;
		distance_sums.x = dot(clamp((distance_sums/distance_nums),0.0f,1.0f) * lerp_delta.xzxz * lerp_delta.yyww, 1.0f);

		// Увеличиваем констраст вблизи источника тени
		fixed contrastfactor = 1.0f - clamp(distance_sums.x/shadow_close_scalefactor,0.0f,1.0f);
		shadow_sums.x = clamp((shadow_sums.x - 0.5f) * (1.0f + contrastfactor*4.0f)+0.5f,0.0f,1.0f);

		return 1.0f - shadow_sums.x;

	#else

		// Тень с дизерингом и 4 выборками для упрощённой детализации

		vec = normalize(vec) * 0.5f; // Компенсация размера тени

		const float3 rndseed = float3(12.9898,78.233,45.5432);
		float3 randomvec = float3( dot(vec,rndseed) , dot(vec.yzx,rndseed) , dot(vec.zxy,rndseed) );
		randomvec = frac(sin(randomvec) * 43758.5453);

		float3 vec1 = normalize(cross(vec,randomvec)) / downscale;
		float3 vec2 = normalize(cross(vec,vec1)) / downscale;

		float4 shadowVals;

		shadowVals.x = SampleCubeDistance (vec+vec1);
		shadowVals.y = SampleCubeDistance (vec+vec2);
		shadowVals.z = SampleCubeDistance (vec-vec1);
		shadowVals.w = SampleCubeDistance (vec-vec2);

		shadowVals = mydist.xxxx - shadowVals;
		fixed4 shadows = clamp(shadowVals*sat_mult,0.0f,1.0f);

		return dot(shadows,0.25);

	#endif

}



Если вас не устраивает конкретно этот подход, реализуйте в рамках Unity более подходящие алгоритмы. Буду только рад такой активности, ведь меня правда удручает застой в Unity в плане реалтайм теней, который тянется уже много лет.

P.S.: Больше всего мне хотелось бы услышать мнения тех, кто работает непосредственно с Unity и знает, что там можно, а что нельзя, хотя скорее всего будут пространные размышления об алгоритмах, впилить которые в движок не удастся ввиду его закрытой архитектуры. Но и к пространным размышлениям с радостью присоединюсь и поясню некоторые моменты.
Поделиться с друзьями
-->

Комментарии (19)


  1. clamaw
    11.08.2016 15:28
    +4

    Как выяснилось, удара по производительности нет.

    А бенчмарки есть?


    1. LifeKILLED
      11.08.2016 22:15

      Нужно бы выпустить демку, но на это нет времени :)

      К сожалению, у меня сейчас нет доступа к актуальным компьютерам, работаю на ноутбуке. Поэтому — никаких графиков и замеров. У меня — видеокарта GeForce 540M, но поскольку даже на ней всё вполне себе работает, судите сами.

      Был бы рад, если бы кто-нибудь провёл такие тесты и выложил результаты, можно было бы приложить их к статье.

      В любом случае, дело ДАЛЕКО не в тенях. Производительность — это прежде всего оптимизация сцены. Отсечение геометрии, разбиение на зоны. Если уделять этому внимание, производительность будет на высоте. Если не уделять — то какой бы простой ни был алгоритм у теней, игра будет ужасно тормозить.

      Сам по себе движок Unity хорошо оптимизирован только под Outdoor, то есть единственный динамический Directional light, деревья, заменяемые на billboard'ы, и occlusion culling. Но в коридорных играх отсечение геометрии работает плохо, приходится прятать невидимые комнаты скриптом, вручную выключать источники света за закрытыми дверями, и в конечном счёте можно и вовсе вырубить на фиг встроенное отсечение геометрии, производительность от этого только вырастет.

      Смысл сказанного выше — на практике производительность зависит от многих вещей гораздо больше, чем от алгоритма фильтрации теней.


  1. marsermd
    11.08.2016 19:35
    +1

    Было бы интересно посмотреть на замеры производительности на мобильных девайсах.
    Чтения из текстур довольно заметно влияют на скорость отрисовки.


    1. LifeKILLED
      11.08.2016 22:20

      Насчёт мобильных девайсов я, если честно, вообще без понятия. Я ориентировался прежде всего на шутеры от первого лица, когда ты подходишь к стене и в упор разглядываешь текстуры и тени :)

      На мобильниках наверняка можно что-нибудь упростить. Например, делать не PCF4x4 (25 выборок), а PCF 2x2 (9 выборок, т.е. четыре пикселя плюс ещё один ряд для ручной фильтрации). Можно сделать дизеринг опять же. Всё это надо пробовать и тестировать.

      Я своим шейдером и этой статьёй только хотел показать, что совершенствовать графическую часть Unity можно и нужно. Но мой подход в конкретном алгоритме прямолинеен. Я в шейдерах новичок и не знаю всех тонкостей. Поэтому и прошу помощи, совета и особенно лУчшей реализации у более знающих людей.


      1. LifeKILLED
        11.08.2016 22:31

        Ещё можно переключать далёкие «лампочки» на упрощённый алгоритм. Скажем, в конце коридора пусть будет 4 выборки с дизерингом, а вблизи — честные 25 выборок. Какой-то небольшой прирост производительности это даст.


  1. nxrighthere
    11.08.2016 21:20
    +1

    Тени одно из самых больных мест Unity, головной боли с ними сполна. Я давно мечтаю о PCSS для directional light, жаль я в шейдерных программах мало что понимаю…


    1. LifeKILLED
      11.08.2016 22:25

      Вообще-то, можно попробовать. Алгоритм directed-теней лежит в том же файле, там выше — такая же небольшая и очень простая функция. Если вы знаете кого-то, кто может сделать реально честный PCSS, пусть заглянет в тот файлик и допишет туда свою реализацию. Буду очень рад таким подвижкам. У меня, к сожалению, сейчас нет на это времени. Я этот алгоритм-то сделал полгода назад, в новогодние праздники. А сейчас просто оформил в статью :)


      1. nxrighthere
        11.08.2016 22:39
        +1

        Вы наверняка видели NVidia ShadowWorks? Библиотека и её исходный код лежит в открытом доступе и существует даже проект с интегрированным в Unity HBAO+. Я думаю вполне можно было бы использовать их решение.


        1. LifeKILLED
          11.08.2016 22:57

          Слышал про NVidia ShadowWorks, смотрел демки, но ещё не заглядывал «под капот». Но обязательно попытаюсь разобраться, хотя быстрого результата можно не ждать. К сожалению, сейчас я на той ступени освоения шейдеров, что мне легче написать свой велосипед, чем вникнуть в готовые алгоритмы, особенно хитро оптимизированные. Я заниматься этим буду в любом случае, но выкладывая эту статью, я в первую очередь хотел участия в этой проблеме тех людей, которые хорошо разбираются в алгоритмах теней. Реализацию PCSS я видел ещё лет десять назад, наверняка на Хабре есть какой-нибудь профессионал, который по моей наводке с лёгкостью преобразит графику в Unity.

          (а ещё больше мне хотелось бы, чтобы этой проблемой занялись сами разработчики Unity и хотя бы сделали возможность как-то менять алгоритмы, добавлять новые ползунки в настройки источника света и выложили какой-нибудь туториал у себя в документации… а ещё лучше, сделали бы пять презентов различных алгоритмов на выбор… эх, мечты...)


  1. LifeKILLED
    13.08.2016 22:48

    Последние новости.

    Я получше изучил алгоритмы AMD CHS и nVidia PCSS и пришёл к выводу, что мой алгоритм (разрабатываемый, как велосипед) является подобным CHS, т.е. есть минимальный уровень размытия и есть максимальный, выше которого «размазать» тень не получится. Угол размера источника света (penumbra) действует только рядом с затенителем, а вдали уровень размытия у всех теней одинаков и максимален. В отличие от моего алгоритма у CHS нет эффекта shapen (т.е. он сохраняет детали в резком варианте), у меня была такая версия алгоритма, но мне не понравилась. При этом CHS использует мипмапы теневой карты, которые я не могу использовать (Unity их изначально не считает, я со своей стороны не могу их добавить). И фильтрация в CHS используется «родная», что в Unity недоступно для кубической текстуры. То есть в сравнении с CHS мой алгоритм актуален.

    В результате я решил впилить PCSS во все типы источников света. Вроде бы, не так уж он и сложен: есть массив точек для выборок, распределённых по принципу poisson, по этим координатам идёт LookUp с замером дальности и сама выборка. Отлаживаю потихоньку, как впилю в Unity, можно будет написать вторую статью на эту тему.

    Правда, появилась новая проблема. В файле «UnityShadowLibrary.cginc» лежат алгоритмы для Spot и Point источников света. Directed лежит где-то ещё. Что-то там автопоиском по файлам я нашёл, но пока не удаётся «нащупать» тот самый кусок с выборкой из теневой карты. Но будем надеяться на лучшее :(


    1. nxrighthere
      13.08.2016 23:21
      +1

      Посмотрите в AutoLight.cginc, тени для directional light модифицируются именно в нём насколько я знаю.


      1. LifeKILLED
        14.08.2016 03:18

        Я в нём и смотрю, но как-то там всё запутано.


    1. nxrighthere
      14.08.2016 02:22
      +1


      А в этом видео корейский разработчик вообще прям поверх жестких теней своё решение использует.


      1. LifeKILLED
        14.08.2016 03:17

        Ничего себе! Выглядит круто.


      1. LifeKILLED
        14.08.2016 16:12

        Вообще-то, надо и правда попробовать вырубить все каскады, включить самую простую тень и попробовать модифицировать её. У меня такое ощущение, что каскады прописаны в хардкоде движка, потому что в шейдерах на них я не видел ни одного намёка. Именно так поступил автор видео. Но с другой стороны, было бы неплохо использовать каскады. Что ж, будем эксперементировать.


    1. nitrocaster
      14.08.2016 11:57
      +1

      Чтобы PCSS давал красивые тени, нужно много сэмплов, что сильно бьет по филлрейту, а это слабое место GPU среднего уровня.


      1. LifeKILLED
        14.08.2016 16:08

        Судя по демке от nVidia, PCSS нагружает видеокарту тогда, когда очень много размытых теней. Например, радиус максимального пятна — в четверть экрана. При том же числе максимальных выборок в настройках, большой параметр penumbra просаживает FPS относительно маленького penumbra на удалённых от затенителя участках. Это значение всегда можно ограничить.

        Пока я планирую так: выборок сделаю всего 25 (самый минимальный параметр в демке от nVidia). Большое пятно в таком случае похоже на множество повторяющихся контуров, чтобы устранить это, в демке nVidia делали параметр в 100 выборок. Но я хочу попробовать оставить 25 выборок, но поворачивать паттерн случайным образом, тогда повторяющихся паттернов не будет видно, вместо этого на месте них будет виден едва различимый шум. Точнее, на белом фоне различить его будет возможно, но в сцене с хорошо проработанными текстурами, уверен, это не будет заметно.


        1. nitrocaster
          14.08.2016 16:36
          +1

          Ради большого количества размытых теней PCSS и используют. Возможно, вам будет интересно попробовать вот эту модификацию PCSS.


          1. LifeKILLED
            14.08.2016 19:42

            Спасибо, интересно. Хотя для начала нужно запустить PCSS в Unity хоть каком-то виде.

            Я согласен, что PCSS требует больших вычислительных мощностей, но всегда ведь можно выключить их в настройках.