Полностью корректный кольцевой буффер

This commit is contained in:
2026-07-31 23:56:47 +04:00
parent 5e3f985025
commit eb497f257a
10 changed files with 519 additions and 247 deletions
@@ -20,18 +20,27 @@ out vec4 FragColor;
// Настройки
//------------------------------------------------------------
const float STEP_SIZE = 0.5;
const float MAX_DISTANCE = 256.0;
const int MAX_STEPS = int(MAX_DISTANCE / STEP_SIZE);
// Размер туманного объёма
const float FOG_SIZE_X = 256.0;
const float FOG_SIZE_Z = 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)
@@ -68,23 +77,19 @@ float density(vec3 worldPos)
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
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 = mod(
localX + float(RingBlockOffset.x),
FOG_SIZE_X
);
float physicalX =
localX + float(RingBlockOffset.x);
float physicalZ = mod(
localZ + float(RingBlockOffset.y),
FOG_SIZE_Z
);
float physicalZ =
localZ + float(RingBlockOffset.y);
uv.x = physicalX / FOG_SIZE_X;
uv.z = physicalZ / FOG_SIZE_Z;
@@ -95,6 +100,21 @@ float density(vec3 worldPos)
//------------------------------------------------------------
// Псевдослучайное значение для 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);
@@ -109,17 +129,52 @@ void main()
vec3 samplePos = CameraPosition;
float fog = 0.0;
float currentDistance = 0.0;
for (int i = 0; i < MAX_STEPS; i++)
{
float traveled = float(i) * STEP_SIZE;
// 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;
}
if (traveled >= rayLength)
break;
for (int i = 0; i < MAX_STEPS; i++) {
// Параметр t меняется от 0 до 1
float t = float(i + 1) / float(MAX_STEPS);
samplePos += rayDir * STEP_SIZE;
// Вычисляем следующее расстояние через нелинейное маппинг
float nextDistance = mapParameterToDistance(t);
fog += density(samplePos) * STEP_SIZE;
// Ограничиваем максимальной длиной луча
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);