#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; }; layout(std140, binding = 2) uniform Light { LightData data[300]; 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; 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; // Переменные используемые в цикле: 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; // Счетчик для PCF float pcfDepth; // Глубина PCF 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); // Расчет солнца, если его цвет не черный 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++) { // Данные об источнике относительно фрагмента 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); } } } }