192 lines
5.6 KiB
GLSL
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
|
|
);
|
|
} |