ПрограммированиеФорумГрафика

Параметрическое размытие теней

Страницы: 1 2 3 Следующая »
#0
10:30, 29 июля 2014

Имеется topdown-сцена по которой летают мухи.

Мухи | Параметрическое размытие теней

Летают они, значит, и тени на землю отбрасывают.
Задача такая: размывать тени тем сильнее, чем выше муха поднялась от поверхности.
В игре много каких объектов по фиктивной оси z поднимаются, просто на мухах нагляднее.

То, что на скрине, это эрзац - тень хранится отдельным спрайтом и заранее размыта.
Хочется тень делать прямо из цветного спрайта мухи и размывать программно в шейдере (HLSL).

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

#define PIXEL_SIZE 0.00390625

//Уже отрисованная в текстуру тень, которую надо размыть
sampler ShadowTex : register(s0);

//Параметр передаваемый снаружи 
//param.x=1 param.y=0 - размытие по горизонтали
//param.x=0 param.y=1 - размытие по вертикали
float4 param: register(c0);

float4 main(float2 texCoord : TEXCOORD0) : COLOR0
{  
  float r = 10; //ПРОБЛЕМА 1: Не получается задавать радиус параметром r = param.z
  
  float totalScale = 1.0 + r;
  
  float4 value = tex2D(ShadowTex, texCoord) * totalScale;

  for(int x=1; x <= r; ++x)
  {
    float shift = PIXEL_SIZE * x;

    float scale = 1.0 + r - x;

    float2 sampleCoord = texCoord;
            
    //ПРОБЛЕМА 2: Не получается в рамках шейдеров 2.0 сначала
    //вычитать сдвиг, а потом прибавлять, для выборки до и после текущего пикселя
    sampleCoord.x = texCoord.x - shift * param.x;
    sampleCoord.y = texCoord.y - shift * param.y;
    value += tex2D(ShadowTex, sampleCoord.xy) * scale;
      
    sampleCoord.x = texCoord.x + shift * param.x;
    sampleCoord.y = texCoord.y + shift * param.y;
    value += tex2D(ShadowTex, sampleCoord.xy) * scale;
  }

  return value / (totalScale * totalScale);
}

Столкнулся с двумя проблемами, которые пока не в состоянии решить из-за потсутствия опыта работы с шейдерами:

ПРОБЛЕМА 1: Как мне сделать параметрический цикл? Шейдер отказывается компилироваться, если он не может развернуть цикл (что, конечно, с параметрическим циклом не получится).

ПРОБЛЕМА 2: Хотелось бы уместить решение задачи в модель шейдеров 2.0, если это возможно. Как делать выборки и до и после текущего пикселя так, чтобы шейдер компилировался в 2.0?

Окружение:

1. У меня DirectX 2D-движок на квадах в пространстве экрана, поэтому вершинные шейдеры не работают, только пиксельные.
2. Закон размытия в целом мне не сильно важен - если можно усреднением пикселей сделать параметрически - этого будет вполне достаточно.
3. Значение параметра размытия единое для всего отрисовываемого квада. Одна муха - одна тень с фиксированной степенью размытия, другая муха - другая степень размытия тени.
4. Тени черные, короче цвета не важны, важен альфа-канал.

#1
10:52, 29 июля 2014

Kozinaka
Самый очевидный и простой вариант - генерировать тени на ходу в программе (и кешировать), там же размывать, а в шейдере интерпоирвать между двумя уровнями размытия.

#2
10:52, 29 июля 2014

Попробуй сделать размытие с фиксированным радиусом в пикселях, но в зависимости от высоты мухи читать из разных mipmap уровней текстуры. Функция вроде tex2DLod называется, правда не уверен, есть ли она в SM2. Но если нет, то можно смещение mip уровней средствами самого DX'а сделать.

#3
11:05, 29 июля 2014

Kozinaka
>Шейдер отказывается компилироваться, если он не может развернуть цикл (что, конечно, с параметрическим циклом не получится).
>Хотелось бы уместить решение задачи в модель шейдеров 2.0, если это возможно. Как делать выборки и до и после текущего пикселя так, чтобы шейдер компилировался в 2.0?
>У меня DirectX 2D-движок на квадах в пространстве экрана, поэтому вершинные шейдеры не работают, только пиксельные.
Советую все-таки введение в шейдеры прочитать, чтобы не писать такое ;)

>Шейдер отказывается компилироваться, если он не может развернуть цикл (что, конечно, с параметрическим циклом не получится).
В твоей же ссылке опровержение.

>Хотелось бы уместить решение задачи в модель шейдеров 2.0, если это возможно. Как делать выборки и до и после текущего пикселя так, чтобы шейдер компилировался в 2.0?
Тебе дан sampler. В uniform можно передать размеры текстуры, откуда вычисляются параметры шага пикселя (допустим - текстура 256x256, шаг равен 1/256). Но шейдер на 2.0 выходит тяжелый, и на мобильниках такое не потянет.

>У меня DirectX 2D-движок на квадах в пространстве экрана, поэтому вершинные шейдеры не работают, только пиксельные.
shader состоит всегда как минимум из двух компонентов - vertex/pixel, а еще есть геометрический.

#4
11:13, 29 июля 2014

Kozinaka
Signed distance field тебе поможет. Очень дешево, и очень эффективно (именно то что тебе надо) и никаких циклов.
Первоисточник: http://www.valvesoftware.com/publications/2007/SIGGRAPH2007_Alpha… ification.pdf
Кое-что на русском: http://habrahabr.ru/post/215905/
Сама идея такого Signed distance field - получить из размытого изображения четкие границы. Четкость границ определяется собственно шейдером.
В твоем случае нужно создать Signed distance field под максимальное размытие, и плавно увеличивать четкость по мере приближения мухи к земле.

#5
11:25, 29 июля 2014

x
> O'rly?
Вот гифка из хабрастатьи для убедительности:
Изображение

#6
11:51, 29 июля 2014

x
> К примеру, как там насчет спрайтов с изначально не бинарной альфой?
Ты сейчас о чем?

#7
11:56, 29 июля 2014

MrShoor
Как генерится Sdf для чёрного контура?

#8
12:02, 29 июля 2014

-Eugene-
> Как генерится Sdf для чёрного контура?
Нужно обязательно сюда копипастить с тех ссылок?

#9
12:05, 29 июля 2014

MrShoor
> Нужно обязательно сюда копипастить с тех ссылок?
Тьфу, не то написал. Как генерится SDF для серого-черного конутра?

#10
12:09, 29 июля 2014

-Eugene-
> Тьфу, не то написал. Как генерится SDF для серого-черного конутра?
Очевидно, что SDF генерится для монохромного контура.

#11
12:14, 29 июля 2014

MrShoor
Вот в этом может быть проблема

#12
12:23, 29 июля 2014

-Eugene-
> Вот в этом может быть проблема
Проблема может быть во всем.

#13
13:11, 29 июля 2014

Проблема в дополнительных спрайтах для хранения тени, хотелось бы обойтись без них. :)

CasDev
> shader состоит всегда как минимум из двух компонентов - vertex/pixel, а еще есть геометрический.
Да кто спорит, просто я использую флаг D3DDECLUSAGE_POSITIONT (вся фишка в букве T в конце), сообщая, что вершины у меня уже обработаны и содержат экранные координаты. При таком раскладе пользовательские вершинные шейдеры просто не запускаются. Вот тут немного по теме: http://www.gamedev.net/topic/417067-question-about-vertex-shaders/#entry3777937 Я просто уточнил, что мне нужно решение без задействования вершинных шейдеров.


-Eugene-
> генерировать тени на ходу в программе (и кешировать), там же размывать, а в шейдере интерполирвать между двумя уровнями
x
> 3д текстура
gammaker
> Попробуй сделать размытие с фиксированным радиусом в пикселях, но в зависимости
> от высоты мухи читать из разных mipmap уровней текстуры.
MrShoor
> создать Signed distance field под максимальное размытие, и плавно увеличивать
> четкость по мере приближения мухи к земле
Спасибо за такое количество альтернатив! Особенно за Signed Distance Field, интереснейшая штука! Боюсь, все предложенные решения требуют хранения тени отдельным спрайтом, а это в два раза больше памяти, чем уже занимают спрайты. Сейчас я генерирую тени просто блендингом с чёрным цветом и полупрозрачностью, двумя проходами шейдера размытия я получаю почти всё, что требуется, осталось только заставить его размывать по параметру.

90% существ и объектов в игре не поднимаются от поверхности, их тени я буду размывать шейдером с фиксированной степенью размытия (самой маленькой), а вот для тех, кто поднялся в воздух я готов рассчитывать даже с избытком проходов, лишь бы иметь возможность плавно задавать степень размытия. Вот, собственно, игруха в своём текущем состоянии (от земли отрываются части червей, когда они прыгают, летающие мухи и прыгающие блохи):

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

x, MrShoor зацените блики! :) Я их сделал исключительно благодаря вашим советам в этой теме: http://www.gamedev.ru/code/forum/?id=184279

Позвольте чуть сузить тему: а что если сделать массив чисел в котором хранить функцию распределения весов соседних пикселей при формировании значения текущего пикселя? И передавать его в шейдер для каждого квада. Передача 20 float в шейдер, это тяжелая операция?

В таком случае количество выборок у меня всегда будет максимальным, как для r = 20. Что если использовать четыре разных шейдера, проходящих по 5, 15 и 20 соседним пикселям, взвешивая соответствующим фрагментом функции распределения? Переключение шейдера, это тяжелее, чем проход шейдера по спрайту максимум 256х256 с избыточным количеством проходов?

Если я в едином шейдере поставлю несколько грубых if'ов, разделив его на четыре, например, статических цикла по 5, 10, 15 и 20 выборок с фрагментом моей переданной функции распределения, то будут ли эти if'ы для каждого пикселя выполняться? Или соптимизируется и переключение веток будет один раз на квад производиться?

#14
13:26, 29 июля 2014

Kozinaka
Выглядит интересно, а уже есть где-нибудь демка?

Страницы: 1 2 3 Следующая »
ПрограммированиеФорумГрафика

Тема в архиве.