Я создал http://madebyevan.com/webgl-water/ еще в 2011 году и всегда хотел объяснить, как это работает. На мой взгляд, наиболее интересным аспектом демонстрации является мой подход к рендерингу каустики. Термин каустик относится к рисунку, который образует свет, когда он отражается или преломляется изогнутой поверхностью. Каустики выглядят так:

Обычно это недоступно для графики в реальном времени, потому что объем требуемых вычислений в общем случае огромен. Очень трудно сказать, какой свет пришел в данную точку, если свет идет со многих разных направлений. Общий подход состоит в том, чтобы на некоторое время отражать лучи света повсюду и позволить им накапливаться, а затем посмотреть, где они собрались. Это известно как отслеживание пути. Свет, который был сфокусирован на меньшей площади, накапливается больше и становится ярче (вспомните, как увеличительное стекло концентрирует свет в яркое пятно), а рассеянный свет становится тусклее.

Трассировка пути не является специфической для каустики; это общее решение для моделирования поведения света и для рендеринга реалистичных изображений. Существуют некоторые методы реального времени, которые в определенных случаях приближаются к этому, но они основаны на том факте, что непрямое освещение в рассеянных средах обычно медленно изменяется вдоль поверхности, и в этом случае вы можете обойтись редкими выборками. Это совсем не относится к каустикам. Каустики создают сложные световые узоры с резкими складками, которые будут размыты и даже могут полностью исчезнуть при использовании этих приближений в реальном времени.

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

Этот метод особенно удобен, поскольку он очень эффективно отображает оборудование для растеризации. Обычно вычисление площади может выполняться на графическом процессоре с использованием геометрического шейдера, поскольку геометрические шейдеры имеют одновременный доступ ко всем трем вершинам треугольника, но WebGL поддерживает только вершинные и фрагментные шейдеры. Вершинные шейдеры смотрят только на одну вершину, поэтому стандартный вершинный шейдер не имеет достаточно информации. Вы можете раздувать формат вершин, чтобы также включить две другие вершины в треугольник, и заставить каждый вершинный шейдер преобразовывать все три вершины для определения площади треугольника, но это означает, что вы больше не можете совместно использовать вершины между соседними треугольниками в сетке. Есть способ получше.

Фактически можно вычислить соотношение площадей исходного и конечного треугольника, даже не требуя расположения вершин. У фрагментных шейдеров есть интересная стратегия оценки: они всегда оцениваются по четыре за раз в группе 2x2 (подробности см. В этом посте). Поскольку все фрагментные шейдеры в этой группе используют один и тот же указатель инструкций, каждый из них может вычислить мгновенную частную производную экранного пространства по осям x и y любого значения, используя конечную разность между собой и соседним фрагментным шейдером по этой оси. Обычно это используется для вычисления уровня MIP-карты текстуры, но мы можем использовать это здесь, чтобы получить соотношение площадей. Все, что требуется от вершинного шейдера, - это передать фрагментному шейдеру старые и новые позиции вершин в виде переменных значений.

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