17/shaders/lighting.frag

221 lines
10 KiB
GLSL
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#version 420 core
in vec2 texCoord;
layout(std140, binding = 0) uniform Camera
{
mat4 projection;
mat4 view;
vec3 position;
} camera;
struct LightData
{
vec3 position;
vec3 color;
vec3 attenuation;
vec4 direction_angle;
mat4 vp[6];
};
layout(std140, binding = 2) uniform Light
{
LightData data[64];
int count;
} light_f;
layout(std140, binding = 3) uniform Sun
{
vec3 direction;
vec3 color;
mat4 vp[4];
} sun;
uniform float camera_cascade_distances[4]; // Размер массива должен соответствовать количеству каскадов
uniform sampler2D gPosition;
uniform sampler2D gNormal;
uniform sampler2D gDiffuseP;
uniform sampler2D gAmbientSpecular;
uniform sampler2DArray sunShadowDepth;
uniform samplerCubeArray pointShadowDepth;
uniform sampler2D ssao;
uniform usampler2D gID;
uniform uvec3 selectedID;
layout(std140, binding = 4) uniform gamma
{
float inv_gamma;
};
out vec4 color;
void main()
{
// Получим данные из текстур буфера
vec3 fragPos = texture(gPosition, texCoord).rgb;
vec3 N = texture(gNormal, texCoord).rgb;
vec3 kd = texture(gDiffuseP, texCoord).rgb;
vec3 ka = texture(gAmbientSpecular, texCoord).rgb;
float ks = texture(gAmbientSpecular, texCoord).a;
float p = texture(gDiffuseP, texCoord).a;
float ssao_value = texture(ssao, texCoord).r;
// Переменные используемые в цикле:
vec3 L_vertex; // Данные об источнике относительно фрагмента
vec3 Cam_vertex = normalize(camera.position - fragPos); // Данные о камере относительно фрагмента
float diffuse; // Диффузная составляющая
vec3 H; // Вектор половины пути
float specular; // Зеркальная составляющая
float L_distance; // Расстояние от поверхности до источника
float attenuation; // Коэф. угасания
float acosA; // Косинус между вектором от поверхности к источнику и обратным направлением источника
float intensity; // Интенсивность для прожектора
vec3 fragPosLightSpace; // Фрагмент в пространстве источника
float shadowValue; // Значение затененности
vec2 texelSize = 1.0 / textureSize(sunShadowDepth, 0).xy; // Размер текселя текстуры теней
int x, y, z; // Счетчик для PCF
float pcfDepth; // Глубина PCF
float cubemap_offset = 0.05f; // Отступ в текстурных координатах для PCF
float cubemap_depth; // Дистанция между фрагментом и источником в диапазоне [0;1]
vec4 fragPosCamSpace = camera.view * vec4(fragPos, 1); // Фрагмент в пространстве камеры
int cascade_index; // Индекс текущего каскада для вычисления теней
// Определение индекса каскада в который попадает фрагмент (цикл на 1 меньше чем кол-во каскадов)
for (cascade_index = 0; cascade_index < 3; cascade_index++)
if (abs(fragPosCamSpace.z) < camera_cascade_distances[cascade_index])
break;
// Фоновая освещенность
color = vec4(ka, 1) * ssao_value;
// Расчет солнца, если его цвет не черный
if (length(sun.color) > 0)
{
// Расположение фрагмента в координатах теневой карты
fragPosLightSpace = (sun.vp[cascade_index] * vec4(fragPos, 1.0)).xyz;
// Переход от [-1;1] к [0;1]
fragPosLightSpace = (fragPosLightSpace + vec3(1.0)) / 2;
// Сдвиг для решения проблемы акне
fragPosLightSpace.z -= max(0.05 * (1.0 - dot(N, sun.direction)), 0.005);
// Проверка PCF
shadowValue = 0.0;
for(x = -1; x <= 1; ++x)
{
for(y = -1; y <= 1; ++y)
{
pcfDepth = texture(sunShadowDepth, vec3(fragPosLightSpace.xy + vec2(x, y) * texelSize, cascade_index)).r;
shadowValue += fragPosLightSpace.z > pcfDepth ? 1.0 : 0.0;
}
}
shadowValue /= 9;
// Рассчитываем освещенность, если значение тени меньше 1
if (shadowValue < 1.0)
{
// Данные об источнике относительно фрагмента
L_vertex = normalize(sun.direction);
// Диффузная составляющая
diffuse = max(dot(L_vertex, N), 0.0); // скалярное произведение с отсеканием значений < 0
// Вектор половины пути
H = normalize(L_vertex + Cam_vertex);
// Зеркальная составляющая
specular = pow(max(dot(H, N), 0.0), p*4); // скалярное произведение с отсеканием значений < 0 в степени p
// Результирующий цвет с учетом солнца
color += ( vec4(sun.color*kd*diffuse, 1)
+ vec4(sun.color*ks*specular, 1) ) * (1.0 - shadowValue);
}
}
// Цикл по источникам света
int i;
for (i = 0; i < light_f.count; i++)
{
// Обнулим значение тени
shadowValue = 0;
// Позиция фрагмента относительно источника
fragPosLightSpace = fragPos - light_f.data[i].position;
// Дистанция между фрагментом и источником в диапазоне [0;1]
cubemap_depth = length(fragPosLightSpace) / light_f.data[i].attenuation.r;
// Сдвиг для решения проблемы акне
cubemap_depth -= max(0.05 * (1.0 - dot(N, light_f.data[i].direction_angle.xyz)), 0.005);
for(x = -1; x <= 1; ++x)
{
for(y = -1; y <= 1; ++y)
{
for(z = -1; z <= 1; ++z)
{
// Значение из кубической текстуры с учетом источника (i)
pcfDepth = texture(pointShadowDepth, vec4(fragPosLightSpace + vec3(x, y, z)*cubemap_offset, i)).r;
if(cubemap_depth > pcfDepth)
shadowValue += 1.0;
}
}
}
shadowValue /= (27);
if (shadowValue < 1.0)
{
// Данные об источнике относительно фрагмента
L_vertex = light_f.data[i].position - fragPos;
// Расстояние от поверхности до источника
L_distance = length(L_vertex);
// Проверка на дистанцию
if (L_distance < light_f.data[i].attenuation.r)
{
// Нормирование вектора
L_vertex = normalize(L_vertex);
// арккосинус между вектором от поверхности к источнику и обратным направлением источника
acosA = degrees(acos(dot(-L_vertex, normalize(light_f.data[i].direction_angle.xyz))));
// Если угол меньше угла источника или угол источника минимален, то считаем освещенность
if(acosA <= light_f.data[i].direction_angle.a)
{
// Диффузная составляющая
diffuse = max(dot(L_vertex, N), 0.0); // скалярное произведение с отсеканием значений < 0
// Вектор половины пути
H = normalize(L_vertex + Cam_vertex);
// Зеркальная составляющая
specular = pow(max(dot(H, N), 0.0), p*4); // скалярное произведение с отсеканием значений < 0 в степени p
// Угасание с учетом расстояния
attenuation = 1 / (1 + light_f.data[i].attenuation[1] * L_distance + light_f.data[i].attenuation[2] * L_distance * L_distance);
// Если источник - прожектор, то добавим смягчение
if (light_f.data[i].direction_angle.a < 180)
{
intensity = clamp((light_f.data[i].direction_angle.a - acosA) / 5, 0.0, 1.0);
diffuse *= intensity;
specular *= intensity;
}
color += ( vec4(light_f.data[i].color*kd*diffuse * attenuation, 1)
+ vec4(light_f.data[i].color*ks*specular * attenuation, 1) ) * (1.0 - shadowValue);
}
}
}
}
// Применение гамма-коррекции
color.rgb = pow(color.rgb, vec3(inv_gamma));
vec3 ID = texture(gID, texCoord).rgb;
// Обводка выбранного объекта
if (length(selectedID.rg) > 0 && selectedID.rg == ID.rg && ID.b == 0)
{
int border_width = 3;
vec2 size = 1.0f / textureSize(gID, 0);
for (int i = -border_width; i <= +border_width; i++)
for (int j = -border_width; j <= +border_width; j++)
{
if (i == 0 && j == 0)
continue;
if (texture(gID, texCoord + vec2(i, j) * size).rg != selectedID.rg)
color.rgb = vec3(1.0);
}
}
}