Files
Veila/src/client/resources/assets/veila/shaders/red.frag
T

192 lines
5.6 KiB
GLSL

#version 150
uniform sampler2D DiffuseSampler;
uniform sampler2D DepthSampler;
uniform sampler3D FogVolume;
uniform mat4 InverseProjection;
uniform mat4 InverseView;
uniform vec3 CameraPosition;
uniform ivec2 FogOrigin;
uniform ivec2 RingBlockOffset;
in vec2 texCoord;
out vec4 FragColor;
//------------------------------------------------------------
// Настройки
//------------------------------------------------------------
const float FOG_SIZE_X = 256.0;
const float FOG_SIZE_Z = FOG_SIZE_X;
const int MAX_STEPS = 256; // Уменьшили количество шагов благодаря адаптивности
// Высота мира
const float WORLD_BOTTOM = -64.0;
const float WORLD_HEIGHT = 384.0;
const float MAX_DISTANCE = (FOG_SIZE_X / 2.0) - 18.0;
// Коэффициент нелинейности распределения шагов:
// 1.0 = равномерные шаги (как было)
// 1.5 = мягкое увеличение (рекомендуется для начала)
// 2.0 = линейный рост размера шага с расстоянием (хороший баланс)
// 2.5 = более агрессивное увеличение (лучшая производительность)
const float GAMMA = 2.0;
// Включение jittering для устранения banding артефактов
const bool USE_JITTERING = true;
//------------------------------------------------------------
vec3 reconstructViewPosition(vec2 uv)
{
float depth = texture(DepthSampler, uv).r;
vec4 clip = vec4(
uv * 2.0 - 1.0,
depth * 2.0 - 1.0,
1.0
);
vec4 view = InverseProjection * clip;
return view.xyz / view.w;
}
vec3 reconstructWorldPosition(vec2 uv)
{
vec3 viewPos = reconstructViewPosition(uv);
vec4 world = InverseView * vec4(viewPos, 1.0);
return world.xyz + CameraPosition;
}
//------------------------------------------------------------
float density(vec3 worldPos)
{
vec3 uv;
float localX = worldPos.x - float(FogOrigin.x);
float localZ = worldPos.z - float(FogOrigin.y);
if (
localX < 0.0 || localX >= FOG_SIZE_X ||
localZ < 0.0 || localZ >= FOG_SIZE_Z ||
worldPos.y < WORLD_BOTTOM ||
worldPos.y >= WORLD_BOTTOM + WORLD_HEIGHT
){
return 0.0;
}
float physicalX =
localX + float(RingBlockOffset.x);
float physicalZ =
localZ + float(RingBlockOffset.y);
uv.x = physicalX / FOG_SIZE_X;
uv.z = physicalZ / FOG_SIZE_Z;
uv.y = (worldPos.y - WORLD_BOTTOM) / WORLD_HEIGHT;
return texture(FogVolume, uv).r;
}
//------------------------------------------------------------
// Псевдослучайное значение для jittering (детерминированное по UV)
float random(vec2 st)
{
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
// Функция нелинейного маппинга параметра t [0,1] в расстояние
// Использует степенную функцию для распределения шагов
float mapParameterToDistance(float t)
{
return MAX_DISTANCE * pow(t, GAMMA);
}
//------------------------------------------------------------
void main()
{
vec4 sceneColor = texture(DiffuseSampler, texCoord);
vec3 worldPos = reconstructWorldPosition(texCoord);
vec3 rayDir = normalize(worldPos - CameraPosition);
float rayLength = distance(worldPos, CameraPosition);
rayLength = min(rayLength, MAX_DISTANCE);
vec3 samplePos = CameraPosition;
float fog = 0.0;
float currentDistance = 0.0;
// Jittering начальной позиции для устранения banding (ступенчатых артефактов)
// Это стандартная техника в volumetric rendering
float jitter = 0.0;
if (USE_JITTERING) {
// Вычисляем размер первого шага для корректного jittering
float firstStep = mapParameterToDistance(1.0 / float(MAX_STEPS));
jitter = random(texCoord) * firstStep;
currentDistance = jitter;
samplePos += rayDir * currentDistance;
}
for (int i = 0; i < MAX_STEPS; i++) {
// Параметр t меняется от 0 до 1
float t = float(i + 1) / float(MAX_STEPS);
// Вычисляем следующее расстояние через нелинейное маппинг
float nextDistance = mapParameterToDistance(t);
// Ограничиваем максимальной длиной луча
if (nextDistance > rayLength) {
nextDistance = rayLength;
}
// Размер текущего шага
float stepSize = nextDistance - currentDistance;
// Проверяем, не вышли ли за пределы луча
if (stepSize <= 0.0) {
break;
}
// Продвигаемся по лучу
samplePos += rayDir * stepSize;
// Интегрируем плотность (Beer-Lambert law approximation)
// Важно умножать на stepSize для корректной физической модели
fog += density(samplePos) * stepSize;
currentDistance = nextDistance;
// Прерываем, если достигли конца луча
if (currentDistance >= rayLength) {
break;
}
}
fog = clamp(fog, 0.0, 1.0);
vec3 fogColor = vec3(
0.75,
0.80,
0.90
);
FragColor = vec4(
mix(sceneColor.rgb, fogColor, fog),
1.0
);
}