#define MAX_LIGHTS 8 struct Light { vec2 position; vec3 color; }; extern Light lights[MAX_LIGHTS]; extern int num_lights; extern vec2 sprite_pos; // Мировая позиция спрайта (в метрах) extern vec3 ambient; // Эмбиентное освещение extern vec3 sky; // Цвет неба // Функция для имитации easing.easeInSine float easeInSine(float x) { return 1.0 - cos((x * 3.14159) / 2.0); } vec4 effect(vec4 vcolor, Image tex, vec2 texture_coords, vec2 screen_coords) { vec3 baseLight = ambient + (vec3(1.0) - ambient) * sky; float luma = dot(baseLight, vec3(0.2126, 0.7152, 0.0722)); // https://en.wikipedia.org/wiki/Relative_luminance vec3 characterBaseLight = mix(baseLight, vec3(luma), 0.3); // 30% обесцвечивания, а то глаза выгорают vec4 texColor = Texel(tex, texture_coords); if (texColor.a == 0.0) { return vec4(0.0); } vec3 pointLight = vec3(0.0); for (int i = 0; i < num_lights; i++) { vec2 lightPos = lights[i].position; vec2 lightVec = lightPos - sprite_pos; float dist = length(lightVec); float attenuation = 0.0; // Логика из shadowcaster.lua: // if lightPos.y > position.y then // 1 - 0.3 * lightVec:length() // elseif position.y - lightPos.y < 3 then // (1 - easing.easeInSine((position.y - lightPos.y))) - 0.3 * lightVec:length() if (lightPos.y > sprite_pos.y) { attenuation = 1.0 - 0.3 * dist; } else { float yDiff = sprite_pos.y - lightPos.y; if (yDiff < 3.0) { attenuation = (1.0 - easeInSine(yDiff)) - 0.3 * dist; } } attenuation = max(attenuation, 0.0); pointLight += lights[i].color * attenuation; } pointLight = clamp(pointLight, 0.0, 1.0); vec3 a = clamp(ambient, 0.0, 1.0); // Канальный множитель: от ambient до 1 в зависимости от точечного света // Это гарантирует, что спрайт не будет черным в отсутствие источников света vec3 lightMultiplier = a + (vec3(1.0) - a) * pointLight; return vec4(texColor.rgb * (characterBaseLight + pointLight), texColor.a); }