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

Вертексная каустика воды и маска

#0
(Правка: 2:23) 2:15, 14 янв. 2021

Добрый день.
Сделал каустику воды с помощью высокополигональной меш-сетки в экранном пространстве. Предположим что сетка состоит из 1920*1080 вершин (для удобства вершина в каждом пикселе)
Параметры шейдера zTest  Always  / ZWrite Off /  blend additive
Теперь алгоритм вкратце:
1) получаю буфер глубины сцены
2) преобразую глубину в мировые координаты
3) проецирую вершины по этим координатам. Получаем высокополигональную сетку натянутую на объекты в сцене.
4) анимация меша воды использует displacement карту XYZ, и читается как-то так waterVertex.xyz = tex2Dlod(DisplacementMap, float4(waterWorldPos.xz, 0, 1));
Теперь я просто читаю эту же карту но для декали.
decalVertex.xz = tex2Dlod(DisplacementMap, float4(decalWorldPos.xz, 0, 1)).xz * 5;
Замечу что для каустики достаточно усилить XZ смещение из displacement текстуры. В моём случае усиление зависит от глубины между водой и дном (если смотреть сверху).

Вот как выглядит результат:
http://kripto289.com/Shared/gamedev/causticTest.webm
Изображение

Но есть проблема, смещение вершин выходит за границы объектов. Видно на стволах пальм.
Думал что смогу прочитать глубину из новой позиции вершины и отбросить её в случае, если глубина превышает старую глубину + небольшое смещение. Это хотя бы избавило бы от части артефактов, но не полностью.
Но скрин спейс uv/глубина после смещения выглядит неправильно.

+ Показать

Как можно побороть такие артефакты, если это вообще возможно?


#1
7:09, 14 янв. 2021

Когда приглушенно, то норм выглядит. Немного на стволах отражается, в реале тоже должно немного отражаться.

#2
9:33, 14 янв. 2021

Kripto289
> Как можно побороть такие артефакты
Что-то мне кажется, что никак. Да и сам метод очень тяжёлый.
Ещё недостаток - каустика попадает на участки, нормали которых направлены вверх, от воды.

#3
22:54, 14 янв. 2021

Mikle
>Да и сам метод очень тяжёлый.
В сравнении с чем?
В гифке используется всего 256к полигонов.
Так же я могу рендерить меш в рендер таргет (например 512x512). Я думаю ещё более быстрого способа рендерить каустику в несколько строк кода в вершинном шейдере не существует.

Mikle
> Ещё недостаток - каустика попадает на участки, нормали которых направлены
> вверх, от воды.
Тут что-то на эльфийском и я не понимаю что тут написано. Как нормали поверхности могут быть направлены вверх от воды?
Если имеется ввиду отсечение выше поверхности, то это не проблема. У меня есть глубина сцены + отдельная глубина поверхности воды + маска водного объёма, поэтому могу сделать каустику только внутри водного объема. (хотя в реальности каустика выходит из воды)
Если что-то другое, например каустика на пузе у акулы, то можно подсчитать ddx/ddy нормаль из буфера глубины и отсекать нужное направление каким-нибудь френелем.
Или тут о другом речь?

#4
8:26, 15 янв. 2021

Kripto289
> в реальности каустика выходит из воды
Естественно. У тебя по картинке не понятно, я думал, что это и нарисовано - стволы пальм торчат из воды.
А на стволах над поверхностью воды вижу каустику вне зависимости от того, в какую сторону направлены нормали.
Теперь понял, что это всё у тебя под водой, ты бы ещё кактусы там изобразил, чтобы совсем понятно было :)

#5
(Правка: 14:47) 14:45, 15 янв. 2021

Я делаю более простым, но чуть менее качественным путём.
Считаю проекцию по буферу глубины в вершинном шейдере, но вершины не смещаю, передаю результат как varying в фрагментный шейдер.
Вывожу каустику белым по черному.
В финальном рендере, перевожу координаты пикселя в координаты каустики и прибавляю цвет.
И каустика ложится прям ровно.


https://shielded-basin-29578.herokuapp.com/
Во вкладке Quality поставить Water resolution на максимум (512 сегментов)
Во вкладке Water меняем scale - получаем больше/меньше каустики.

#6
(Правка: 17:26) 17:23, 15 янв. 2021

Mikle
> Естественно. У тебя по картинке не понятно, я думал, что это и нарисовано -
> стволы пальм торчат из воды.
> А на стволах над поверхностью воды вижу каустику вне зависимости от того, в
> какую сторону направлены нормали.
> Теперь понял, что это всё у тебя под водой, ты бы ещё кактусы там изобразил,
> чтобы совсем понятно было :)
Я и забыл про это. Меш воды выглядел как-то так
ролпрол | Вертексная каустика воды и маска
Для меня то очевидно было где вода :)

Каустика на скрине сделана иным способом если что.

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

#7
(Правка: 18:00) 17:58, 15 янв. 2021

Kripto289
Что-то в этом духе)
Что-бы мы точно были уверенны что мы говорим об одном и том же, опишу более детально.

Орто камера, которая находится в центре меша воды и смотри на дно, по left, right, bottom, top по размеру меша.
Один pass на захват карты глубины дна, второй этой же камерой на проекцию меша по глубине, НО я проекцию не делаю, хотя в принципе может зря я и не делаю)))
Я передаю точку проекции в фрагмент, а там считаю производные.
В результате у меня квад остается квадом на экране, без лишних смещений и скручиваний плоскости.
А фрагментный все равно использует растеризированное значение из varying.

В финальном pass'е я просто беру позицию пикселя в мире, перевожу в координаты ортокамеры и из текстуры беру пиксель моей каустики.

Качество с глубиной не изменно.
Но, качество становится хуже(возможно не верный термин) с разницей угла между нормалью в текущем пикселе и направлением ортокамеры.
Т.е. на дно, где нормаль смотрит прямиком на плоскость воды каустика будет шикарной и в весь обьем.
А для ствола пальмы например, где практически весь ствол смотрит перпендикулярно, будет сходится в практически одну и туже точку в текстуре каустики, т.к. ствол меняется по Y, но по X, Z остается почти не изменным, что по большей и есть координатой пикселя в текстуре каустики.
В этом случае по столбу пальмы каустика пойдет более в виде линии (скрин прилагается)

Side caustic | Вертексная каустика воды и маска
#8
(Правка: 19:12) 19:04, 15 янв. 2021

IIIarp
> Орто камера, которая находится в центре меша воды и смотри на дно, по left,
> right, bottom, top по размеру меша.
Да, у меня сделано так же, правда у меня океан и я беру меш произвольного размера.

IIIarp
> Один pass на захват карты глубины дна, второй этой же камерой на проекцию меша
> по глубине, НО я проекцию не делаю, хотя в принципе может зря я и не делаю)))
> Я передаю точку проекции в фрагмент, а там считаю производные.
Тут я меняю силу xz смещение меша в зависимости от глубины, и потом считаю производные.

    float oldArea = length(ddx(i.oldPos.xyz)) * length(ddy(i.oldPos.xyz));
    float newArea = length(ddx(i.newPos.xyz)) * length(ddy(i.newPos.xyz));

    float color = oldArea / newArea * 0.1;


IIIarp
> В результате у меня квад остается квадом на экране, без лишних смещений и
> скручиваний плоскости.
> А фрагментный все равно использует растеризированное значение из varying.
Но если сам меш воды имеет огромные смещения (как у океанских волн), то они всё равно будут ?

IIIarp
> В финальном pass'е я просто беру позицию пикселя в мире, перевожу в координаты
> ортокамеры и из текстуры беру пиксель моей каустики.
Именно от этого момента я и хочу избавиться. У меня океан, а значит и текстура ортокамеры должна быть огромной, что бы охватить огромную дистанцию. Что бы покрыть хотя бы 100 метров дистанции воды, мне надо считать 4 каскада с текстурой 1024x1024. А если смотреть на океан не под водой, то дистанция каустики должна быть сотни метров.
Наглядно. Вот каустика с дистанцией 100 метров, хотя этого уже не хватает.

+ Показать

А вот как видно качество с 4 каскадами по 1024 и 256 пикселей на каскад.

+ Показать

И это всего-то 100 метров, что явно не достаточно.
Если рендерить одним каскадом 100 метров, то мне придётся брать текстуру 8к, что бы добиться приемлемого качества. Но в паре метрах от дна, качество всё равно будет паршивое.
Именно поэтому я хочу избавиться от текстур вообще и рендерить скрин спейс мешем. Качество будет одинаковое на любой дистации и без антиалиазинга текстуры.


IIIarp
> Но, качество становится хуже(возможно не верный термин) с разницей угла между
> нормалью в текущем пикселе и направлением ортокамеры.
> Т.е. на дно, где нормаль смотрит прямиком на плоскость воды каустика будет
> шикарной и в весь обьем.
> А для ствола пальмы например, где практически весь ствол смотрит
> перпендикулярно, будет сходится в практически одну и туже точку в текстуре
> каустики, т.к. ствол меняется по Y, но по X, Z остается почти не изменным, что
> по большей и есть координатой пикселя в текстуре каустики.
> В этом случае по столбу пальмы каустика пойдет более в виде линии (скрин
> прилагается)
Как раз таки это так и должно быть. Если представить луч лазера и светить им со стороны солнца, то луч будет размазываться по пальме в виде огромной полосы.
Как тут:

+ Показать

#9
(Правка: 19:58) 19:53, 15 янв. 2021

Kripto289
> Но если сам меш воды имеет огромные смещения (как у океанских волн), то они всё
> равно будут ?

В моем случае все немного проще, у меня у волн нет смещений по XZ, волна и так со временем двигается по XZ, а вот смещение по Y для ортокамеры никакого значения не несет.

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

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

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

По этому я не претендую на вручение премии за идеальное решение в плане качества/правильности каустики :)

Но, что могу сказать на счет ваших затей, каустику в итоге без проекции не построить правильным образом.

Самым безпорядочно хитрым способом будет генерить ее в 1 текстурку и просто мапить по всему оканскому дну))
Но эт не честно, но какой никакой эффект будет.
В остальном без проекций HD меша судя по всему никак(это чисто мое мнение).

Еще, что-бы я попробовал, использовать то, что есть у вас сейчас, только более хитрым способом.
Уменьшать разрешение сетки на отдалении от плоскости воды, что-то по типу octree.
Тогда получатся своеобразные LOD'ы в каустике да и для самой плоскоти океана на дальних расстояниях.
Ведь все равно на дальних расстояниях вся размерность воды и каустики не поместится в доступные пиксели экрана, и разницы не будет от HD и на производительности сэкономите.
В таком случае, на дальних лодах, тексутрка 128x128 уже будет идеальная, а на самых дальних каскадах 32х32, ну вы поняли :)

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