Реализация отображения теней в JavaScript и WebGL

Одной из наиболее важных функций для создания глубины и реализма в 3D-сцене являются тени. Они дают визуальные подсказки о форме и положении объектов. Тени также передают настроение и атмосферу.

Основным методом, используемым для рендеринга теней в 3D-приложениях в реальном времени, является отображение теней. Теневые объемы были альтернативой в прошлом, но в последние годы потеряли популярность по сравнению с гибкостью и простотой использования карт теней.

Картирование теней

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

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

#version 300 es
layout( location = 0 ) in vec3 vertexPosition;
uniform mat4 M_mvp;
uniform mat4 M_shadowMap_mvp;
out vec4 shadowCoord;
void main()
{
    gl_Position = M_mvp * vec4( vertexPosition , 1.0 );
    shadowCoord = M_shadowMap_mvp * vec4( vertexPosition , 1.0 );
}

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

#version 300 es
precision mediump float;
in vec4 shadowCoord;
uniform sampler2D shadowMap;
out vec4 fragColor;
void main()
{
    float visibility = 0.0;
    float depthValue = texture( shadowMap …