Lighting and clouds are somehow working

This commit is contained in:
Vladislav Khorev 2026-01-04 18:11:10 +03:00
parent 8168734fbb
commit 34fee1005a
13 changed files with 586 additions and 123 deletions

View File

@ -6,43 +6,113 @@ uniform float uDistanceToPlanetSurface; // Расстояние корабля
const float DIST_FOG_MAX = 2000.0; const float DIST_FOG_MAX = 2000.0;
const float DIST_FOG_MIN = 1000.0; const float DIST_FOG_MIN = 1000.0;
varying vec3 vWorldNormal;
varying vec3 vViewNormal; varying vec3 vViewNormal;
varying vec3 vViewPosition; varying vec3 vViewPosition;
// Добавь эти uniform-ы
uniform float uTime;
uniform vec3 uLightDirView;
uniform vec3 uPlayerDirWorld;
// Простая функция псевдослучайного шума
float hash(float n) { return fract(sin(n) * 43758.5453123); }
float noise(vec3 x) {
vec3 p = floor(x);
vec3 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
float n = p.x + p.y * 57.0 + 113.0 * p.z;
return mix(mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y),
mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
mix(hash(n + 170.0), hash(n + 171.0), f.x), f.y), f.z);
}
// Fractal Brownian Motion для "кучевости" облаков
float fbm(vec3 p) {
float f = 0.5000 * noise(p); p *= 2.02;
f += 0.2500 * noise(p); p *= 2.03;
f += 0.1250 * noise(p);
return f;
}
void main() void main()
{ {
// --- 1. Расчет плотности по Френелю (краевой эффект) --- vec3 normal = normalize(vViewNormal);
vec3 viewVector = normalize(-vViewPosition); vec3 viewVector = normalize(-vViewPosition);
float NdotV = dot(vViewNormal, viewVector); float NdotV = dot(normal, viewVector);
float factor = 1.0 - abs(NdotV);
float atmosphereDensity = pow(factor, 5.0);
// --- 2. Расчет коэффициента затухания по дистанции --- // Вектор направления от центра планеты к текущему фрагменту атмосферы (Мировой)
vec3 fragmentDir = normalize(vWorldNormal);
// Вектор направления от центра планеты к игроку (нужно передать как uniform)
// Передай его в C++: renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
float distanceFactor = 1.0;
if (uDistanceToPlanetSurface > DIST_FOG_MAX) // --- 1. Плавное отсечение за горизонтом (Horizon Mask) ---
// Считаем косинус угла между игроком и точкой атмосферы
float cosAngle = dot(fragmentDir, uPlayerDirWorld);
// Плавно затухаем от 0.0 (горизонт) до 0.2 (над головой)
//float horizonMask = smoothstep(0.0, 0.4, cosAngle);
float horizonMask = smoothstep(0.93, 1.0, cosAngle);
// --- 2. Плавный переход при прохождении сквозь слой (Transition Mask) ---
// Определяем "высоту" игрока относительно слоя (напр. слой на 1.03 * R)
// uDistanceToPlanetSurface уже вычислен в PlanetData
float layerHeight = 600.0; // Примерная толщина слоя атмосферы (3% от 20000)
// Делаем прозрачным при приближении к границе (dist около layerHeight)
// и снова видимым, когда спустились ниже
float distToLayer = abs(uDistanceToPlanetSurface - layerHeight);
float transitionMask = smoothstep(0.0, 200.0, distToLayer);
// --- 3. Освещение и облака ---
float diff = max(dot(normal, uLightDirView), 0.0);
if (uDistanceToPlanetSurface < layerHeight) {
diff = max(dot(-normal, uLightDirView), 0.0); // Инверсия для взгляда снизу
}
float lightIntensity = diff + 0.05;
vec3 cloudCoord = fragmentDir * 6.0;
cloudCoord.x += uTime * 0.03;
float n = fbm(cloudCoord);
float cloudMask = smoothstep(0.4, 0.65, n);
// --- 4. Финальный расчет альфы ---
float atmosphereDensity = pow(1.0 - abs(NdotV), 5.0);
float distanceFactor = clamp((uDistanceToPlanetSurface - DIST_FOG_MIN) / (DIST_FOG_MAX - DIST_FOG_MIN), 0.0, 1.0);
// Базовая прозрачность облаков
float cloudAlpha = cloudMask * 0.8;
// Применяем маски:
// В космосе важен distanceFactor, на планете важен horizonMask
float finalCloudAlpha = cloudAlpha * transitionMask;
if (uDistanceToPlanetSurface < layerHeight) {
finalCloudAlpha *= horizonMask; // На планете скрываем то, что под ногами
} else {
finalCloudAlpha *= distanceFactor; // В космосе скрываем по вашей старой логике
if (NdotV <=0)
{ {
// Дистанция > 2000.0: Полностью видно (Factor = 1.0) discard;
distanceFactor = 1.0;
} }
else if (uDistanceToPlanetSurface < DIST_FOG_MIN)
{
// Дистанция < 1000.0: Полностью не видно (Factor = 0.0)
distanceFactor = 0.0;
}
else
{
// Плавный переход (линейная интерполяция)
// normalizedDistance: от 0.0 (на DIST_FOG_MIN) до 1.0 (на DIST_FOG_MAX)
float normalizedDistance = (uDistanceToPlanetSurface - DIST_FOG_MIN) / (DIST_FOG_MAX - DIST_FOG_MIN);
distanceFactor = clamp(normalizedDistance, 0.0, 1.0);
} }
// --- 3. Финальный цвет и прозрачность --- vec3 currentAtmo = mix(vec3(0.01, 0.02, 0.1), uColor, lightIntensity);
vec3 currentCloud = mix(vec3(0.1, 0.1, 0.15), vec3(1.0, 1.0, 1.0), lightIntensity);
vec3 finalRGB = mix(currentAtmo, currentCloud, cloudMask);
float finalAlpha = atmosphereDensity * distanceFactor; // Для дымки (atmo) оставляем затухание при посадке
float atmoAlpha = atmosphereDensity * distanceFactor;
gl_FragColor = vec4(uColor, finalAlpha); float finalAtmoAlpha = atmoAlpha;
if (uDistanceToPlanetSurface < layerHeight) {
finalCloudAlpha *= horizonMask;
finalAtmoAlpha *= horizonMask; // Принудительно гасим дымку под горизонтом
}
gl_FragColor = vec4(finalRGB, max(finalAtmoAlpha, finalCloudAlpha));
} }

View File

@ -1,28 +1,17 @@
// Вершинный шейдер: // Вершинный шейдер:
attribute vec3 vPosition; attribute vec3 vPosition;
attribute vec3 vNormal; // <-- Новый атрибут attribute vec3 vNormal;
uniform mat4 ProjectionModelViewMatrix; uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix; uniform mat4 ModelViewMatrix;
// Выходные переменные (передаются во фрагментный шейдер) varying vec3 vWorldNormal;
varying vec3 vViewNormal; varying vec3 vViewNormal;
varying vec3 vViewPosition; varying vec3 vViewPosition;
void main() void main() {
{ vWorldNormal = vPosition; // Локальная позиция вершины на сфере радиуса 1.0
// 1. Позиция в пространстве вида (View Space) vViewPosition = (ModelViewMatrix * vec4(vPosition, 1.0)).xyz;
vec4 positionView = ModelViewMatrix * vec4(vPosition, 1.0);
vViewPosition = positionView.xyz;
// 2. Нормаль в пространстве вида (View Space)
// Преобразуем нормаль, используя ModelViewMatrix. Поскольку нормаль - это вектор,
// трансляционная часть (четвертый столбец) матрицы нам не нужна.
// Если бы было неединообразное масштабирование, потребовалась бы Inverse Transpose Matrix.
// В вашем случае, где есть только униформное масштабирование и вращение,
// достаточно просто умножить на ModelViewMatrix:
vViewNormal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz); vViewNormal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz);
// W=0.0 для векторов, чтобы игнорировать трансляцию gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
} }

View File

@ -0,0 +1,118 @@
// Фрагментный шейдер:
uniform vec3 uColor;
uniform float uDistanceToPlanetSurface; // Расстояние корабля до поверхности
// Константы затухания, определенные прямо в шейдере
const float DIST_FOG_MAX = 2000.0;
const float DIST_FOG_MIN = 1000.0;
varying vec3 vWorldNormal;
varying vec3 vViewNormal;
varying vec3 vViewPosition;
// Добавь эти uniform-ы
uniform float uTime;
uniform vec3 uLightDirView;
uniform vec3 uPlayerDirWorld;
// Простая функция псевдослучайного шума
float hash(float n) { return fract(sin(n) * 43758.5453123); }
float noise(vec3 x) {
vec3 p = floor(x);
vec3 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
float n = p.x + p.y * 57.0 + 113.0 * p.z;
return mix(mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y),
mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
mix(hash(n + 170.0), hash(n + 171.0), f.x), f.y), f.z);
}
// Fractal Brownian Motion для "кучевости" облаков
float fbm(vec3 p) {
float f = 0.5000 * noise(p); p *= 2.02;
f += 0.2500 * noise(p); p *= 2.03;
f += 0.1250 * noise(p);
return f;
}
void main()
{
vec3 normal = normalize(vViewNormal);
vec3 viewVector = normalize(-vViewPosition);
float NdotV = dot(normal, viewVector);
// Вектор направления от центра планеты к текущему фрагменту атмосферы (Мировой)
vec3 fragmentDir = normalize(vWorldNormal);
// Вектор направления от центра планеты к игроку (нужно передать как uniform)
// Передай его в C++: renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
// --- 1. Плавное отсечение за горизонтом (Horizon Mask) ---
// Считаем косинус угла между игроком и точкой атмосферы
float cosAngle = dot(fragmentDir, uPlayerDirWorld);
// Плавно затухаем от 0.0 (горизонт) до 0.2 (над головой)
//float horizonMask = smoothstep(0.0, 0.4, cosAngle);
float horizonMask = 1.0 - smoothstep(0.9, 1.0, cosAngle);
// --- 2. Плавный переход при прохождении сквозь слой (Transition Mask) ---
// Определяем "высоту" игрока относительно слоя (напр. слой на 1.03 * R)
// uDistanceToPlanetSurface уже вычислен в PlanetData
float layerHeight = 600.0; // Примерная толщина слоя атмосферы (3% от 20000)
// Делаем прозрачным при приближении к границе (dist около layerHeight)
// и снова видимым, когда спустились ниже
float distToLayer = abs(uDistanceToPlanetSurface - layerHeight);
float transitionMask = smoothstep(0.0, 200.0, distToLayer);
// --- 3. Освещение и облака ---
float diff = max(dot(normal, uLightDirView), 0.0);
if (uDistanceToPlanetSurface < layerHeight) {
diff = max(dot(-normal, uLightDirView), 0.0); // Инверсия для взгляда снизу
}
float lightIntensity = diff + 0.05;
vec3 cloudCoord = fragmentDir * 6.0;
cloudCoord.x += uTime * 0.03;
float n = fbm(cloudCoord);
float cloudMask = smoothstep(0.4, 0.65, n);
// --- 4. Финальный расчет альфы ---
float atmosphereDensity = pow(1.0 - abs(NdotV), 5.0);
float distanceFactor = clamp((uDistanceToPlanetSurface - DIST_FOG_MIN) / (DIST_FOG_MAX - DIST_FOG_MIN), 0.0, 1.0);
// Базовая прозрачность облаков
float cloudAlpha = cloudMask * 0.8;
// Применяем маски:
// В космосе важен distanceFactor, на планете важен horizonMask
float finalCloudAlpha = cloudAlpha * transitionMask;
if (uDistanceToPlanetSurface < layerHeight) {
finalCloudAlpha *= horizonMask; // На планете скрываем то, что под ногами
} else {
finalCloudAlpha *= distanceFactor; // В космосе скрываем по вашей старой логике
if (NdotV <=0)
{
discard;
}
}
vec3 currentAtmo = mix(vec3(0.01, 0.02, 0.1), uColor, lightIntensity);
vec3 currentCloud = mix(vec3(0.1, 0.1, 0.15), vec3(1.0, 1.0, 1.0), lightIntensity);
vec3 finalRGB = mix(currentAtmo, currentCloud, cloudMask);
// Для дымки (atmo) оставляем затухание при посадке
float atmoAlpha = atmosphereDensity * distanceFactor;
float finalAtmoAlpha = atmoAlpha;
if (uDistanceToPlanetSurface < layerHeight) {
finalCloudAlpha *= horizonMask;
finalAtmoAlpha *= horizonMask; // Принудительно гасим дымку под горизонтом
}
gl_FragColor = vec4(finalRGB, max(finalAtmoAlpha, finalCloudAlpha));
}

View File

@ -0,0 +1,122 @@
// Фрагментный шейдер:
uniform vec3 uColor;
uniform float uDistanceToPlanetSurface; // Расстояние корабля до поверхности
// Константы затухания, определенные прямо в шейдере
const float DIST_FOG_MAX = 2000.0;
const float DIST_FOG_MIN = 1000.0;
varying vec3 vWorldNormal;
varying vec3 vViewNormal;
varying vec3 vViewPosition;
// Добавь эти uniform-ы
uniform float uTime;
uniform vec3 uLightDirView;
uniform vec3 uPlayerDirWorld;
// Простая функция псевдослучайного шума
float hash(float n) { return fract(sin(n) * 43758.5453123); }
float noise(vec3 x) {
vec3 p = floor(x);
vec3 f = fract(x);
f = f * f * (3.0 - 2.0 * f);
float n = p.x + p.y * 57.0 + 113.0 * p.z;
return mix(mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y),
mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
mix(hash(n + 170.0), hash(n + 171.0), f.x), f.y), f.z);
}
// Fractal Brownian Motion для "кучевости" облаков
float fbm(vec3 p) {
float f = 0.5000 * noise(p); p *= 2.02;
f += 0.2500 * noise(p); p *= 2.03;
f += 0.1250 * noise(p);
return f;
}
void main()
{
vec3 normal = normalize(vViewNormal);
vec3 viewVector = normalize(-vViewPosition);
float NdotV = dot(normal, viewVector);
// Вектор направления от центра планеты к текущему фрагменту атмосферы (Мировой)
vec3 fragmentDir = normalize(vWorldNormal);
// Вектор направления от центра планеты к игроку (нужно передать как uniform)
// Передай его в C++: renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
// --- 1. Плавное отсечение за горизонтом (Horizon Mask) ---
// Считаем косинус угла между игроком и точкой атмосферы
float cosAngle = dot(fragmentDir, uPlayerDirWorld);
// Плавно затухаем от 0.0 (горизонт) до 0.2 (над головой)
//float horizonMask = smoothstep(0.0, 0.4, cosAngle);
float horizonMask = smoothstep(0.9, 1.0, cosAngle);
gl_FragColor = vec4(horizonMask, 0.0, 0.0, 0.5);
return;
/*
// --- 2. Плавный переход при прохождении сквозь слой (Transition Mask) ---
// Определяем "высоту" игрока относительно слоя (напр. слой на 1.03 * R)
// uDistanceToPlanetSurface уже вычислен в PlanetData
float layerHeight = 600.0; // Примерная толщина слоя атмосферы (3% от 20000)
// Делаем прозрачным при приближении к границе (dist около layerHeight)
// и снова видимым, когда спустились ниже
float distToLayer = abs(uDistanceToPlanetSurface - layerHeight);
float transitionMask = smoothstep(0.0, 200.0, distToLayer);
// --- 3. Освещение и облака ---
float diff = max(dot(normal, uLightDirView), 0.0);
if (uDistanceToPlanetSurface < layerHeight) {
diff = max(dot(-normal, uLightDirView), 0.0); // Инверсия для взгляда снизу
}
float lightIntensity = diff + 0.05;
vec3 cloudCoord = fragmentDir * 6.0;
cloudCoord.x += uTime * 0.03;
float n = fbm(cloudCoord);
float cloudMask = smoothstep(0.4, 0.65, n);
// --- 4. Финальный расчет альфы ---
float atmosphereDensity = pow(1.0 - abs(NdotV), 5.0);
float distanceFactor = clamp((uDistanceToPlanetSurface - DIST_FOG_MIN) / (DIST_FOG_MAX - DIST_FOG_MIN), 0.0, 1.0);
// Базовая прозрачность облаков
float cloudAlpha = cloudMask * 0.8;
// Применяем маски:
// В космосе важен distanceFactor, на планете важен horizonMask
float finalCloudAlpha = cloudAlpha * transitionMask;
if (uDistanceToPlanetSurface < layerHeight) {
finalCloudAlpha *= horizonMask; // На планете скрываем то, что под ногами
} else {
finalCloudAlpha *= distanceFactor; // В космосе скрываем по вашей старой логике
if (NdotV <=0)
{
discard;
}
}
vec3 currentAtmo = mix(vec3(0.01, 0.02, 0.1), uColor, lightIntensity);
vec3 currentCloud = mix(vec3(0.1, 0.1, 0.15), vec3(1.0, 1.0, 1.0), lightIntensity);
vec3 finalRGB = mix(currentAtmo, currentCloud, cloudMask);
// Для дымки (atmo) оставляем затухание при посадке
float atmoAlpha = atmosphereDensity * distanceFactor;
float finalAtmoAlpha = atmoAlpha;
if (uDistanceToPlanetSurface < layerHeight) {
finalCloudAlpha *= horizonMask;
finalAtmoAlpha *= horizonMask; // Принудительно гасим дымку под горизонтом
}
gl_FragColor = vec4(finalRGB, max(finalAtmoAlpha, finalCloudAlpha));*/
}

View File

@ -1,12 +1,9 @@
attribute vec3 vPosition; attribute vec3 vPosition;
uniform mat4 ProjectionModelViewMatrix; uniform mat4 ProjectionModelViewMatrix;
varying vec3 vViewDir;
varying vec3 dir;
void main() { void main() {
vec4 realVertexPos = vec4(vPosition.xyz, 1.0); gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
gl_Position = ProjectionModelViewMatrix * realVertexPos; // Направление взгляда в пространстве вида (вершина куба относительно центра)
vViewDir = vPosition;
dir = vPosition;
} }

View File

@ -1,11 +1,36 @@
uniform samplerCube Texture; uniform samplerCube Texture;
uniform float skyPercent; uniform float skyPercent;
uniform float uPlayerLightFactor; // Глобальный фактор дня/ночи для позиции игрока
uniform vec3 uSkyColor;
varying vec3 dir; varying vec3 vViewDir;
void main() { void main() {
vec4 skyBoxColor = textureCube(Texture, normalize(dir)); vec3 viewDir = normalize(vViewDir);
vec4 skyColor = vec4(0.0, 0.5, 1.0, 1.0);
gl_FragColor = skyPercent*skyColor + (1.0 - skyPercent) * skyBoxColor;
// 1. Получаем звезды
vec4 starsColor = textureCube(Texture, viewDir);
// 2. Цвета атмосферы
vec3 dayColor = uSkyColor;
vec3 nightColor = vec3(0.01, 0.01, 0.04);
// 3. Теперь всё небо окрашивается одинаково в зависимости от положения игрока
// Мы плавно смешиваем ночной и дневной купол
vec3 atmosphericColor = mix(nightColor, dayColor, uPlayerLightFactor);
// 4. Логика видимости звезд
// Звезды видны в космосе (skyPercent=0)
// И в атмосфере, если сейчас ночь (uPlayerLightFactor -> 0)
float starsVisibility = (1.0 - skyPercent) + (skyPercent * (1.0 - uPlayerLightFactor));
// Делаем звезды чуть ярче на ночном небе
starsVisibility = pow(starsVisibility, 1.5);
// 5. Итоговый цвет
// Добавляем слой атмосферы к звездам
vec3 skyLayer = atmosphericColor * skyPercent;
vec3 finalColor = (starsColor.rgb * starsVisibility) + skyLayer;
gl_FragColor = vec4(finalColor, 1.0);
} }

View File

@ -10,6 +10,7 @@ varying vec3 Color;
varying vec2 TexCoord; varying vec2 TexCoord;
varying vec3 vViewDirTangent; varying vec3 vViewDirTangent;
varying vec3 worldPosition; varying vec3 worldPosition;
varying vec3 vWorldNormal;
uniform mat4 ProjectionModelViewMatrix; uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix; uniform mat4 ModelViewMatrix;
@ -33,5 +34,7 @@ void main() {
worldPosition = vPosition; worldPosition = vPosition;
vWorldNormal = vPosition;
Color = vColor; Color = vColor;
} }

View File

@ -2,6 +2,7 @@ varying vec2 TexCoord;
varying vec3 vViewDirTangent; varying vec3 vViewDirTangent;
varying vec3 Color; varying vec3 Color;
varying vec3 worldPosition; varying vec3 worldPosition;
varying vec3 vWorldNormal;
uniform sampler2D Texture; uniform sampler2D Texture;
uniform sampler2D BakedTexture; uniform sampler2D BakedTexture;
@ -12,41 +13,58 @@ uniform float uCurrentZFar;
uniform vec3 uViewPos; uniform vec3 uViewPos;
const vec4 FOG_COLOR = vec4(0.0, 0.5, 1.0, 1.0); // Синий туман const vec4 FOG_COLOR = vec4(0.0, 0.5, 1.0, 1.0); // Синий туман
uniform vec3 uLightDirWorld; // Направление ЛУЧЕЙ (1, -1, -1)
uniform float uPlayerLightFactor;
uniform vec3 uPlayerDirWorld;
void main() { void main() {
// --- 1. Расчет освещения поверхности ---
// Направление НА источник света
vec3 lightToSource = normalize(-uLightDirWorld);
vec3 fragmentDir = normalize(vWorldNormal);
// Используем vNormal (нормаль в мировом пространстве, так как vPosition тоже в мировом)
// Важно: если vNormal не нормализована в вершинном, делаем это здесь
vec3 normal = normalize(fragmentDir); // На планете-сфере нормаль совпадает с направлением от центра
float diff = max(dot(normal, lightToSource), 0.0);
float ambient = 0.15; // Базовая освещенность ночной стороны
float surfaceLightIntensity = diff + ambient;
// --- 2. Динамический цвет тумана ---
// Синхронизируем туман с атмосферой: днем голубой, ночью темно-синий
vec3 dayFog = vec3(0.0, 0.5, 1.0);
vec3 nightFog = vec3(0.01, 0.01, 0.04);
vec3 dynamicFogColor = mix(nightFog, dayFog, uPlayerLightFactor);
// --- 3. Основная логика текстур (твой код) ---
vec3 viewDir = normalize(vViewDirTangent); vec3 viewDir = normalize(vViewDirTangent);
// Получаем высоту из альфа-канала запеченной текстуры
float height = texture2D(Texture, TexCoord).a; float height = texture2D(Texture, TexCoord).a;
// Смещение. Знак минус используется, если мы хотим "вдавить" камни
// Деление на viewDir.z помогает избежать сильных искажений под углом
//vec2 p = viewDir.xy * (height * uHeightScale) / viewDir.z;
vec2 p = vec2(viewDir.x, -viewDir.y) * (height * uHeightScale); vec2 p = vec2(viewDir.x, -viewDir.y) * (height * uHeightScale);
//vec2 p = vec2(0,0);
vec2 finalTexCoord = TexCoord + p; vec2 finalTexCoord = TexCoord + p;
float realDist = distance(worldPosition, uViewPos); float realDist = distance(worldPosition, uViewPos);
float textureMixFactor = clamp((2000.0 - realDist) / 500.0, 0.0, 1.0);
float textureMixFactor = clamp((2000 - realDist) / 500.0, 0.0, 1.0);
vec4 bakedTextureColor = texture2D(BakedTexture, finalTexCoord); vec4 bakedTextureColor = texture2D(BakedTexture, finalTexCoord);
vec4 textureColor = texture2D(Texture, TexCoord); vec4 textureColor = texture2D(Texture, TexCoord);
vec3 flavoredRGB = mix(textureColor.rgb, textureColor.rgb * Color, 0.7);
vec4 textureFlavoredColor = vec4(textureColor.rgb * Color, 1.0); vec4 textureFlavoredColor = vec4(textureColor.rgb * Color, 1.0);
if (bakedTextureColor.x < 0.01 && bakedTextureColor.y < 0.01 && bakedTextureColor.y < 0.01) if (bakedTextureColor.x < 0.01 && bakedTextureColor.y < 0.01 && bakedTextureColor.z < 0.01) {
{
textureMixFactor = 1.0; textureMixFactor = 1.0;
} }
vec4 finalColor = textureMixFactor*textureFlavoredColor + (1 - textureMixFactor) * bakedTextureColor; vec4 finalColor = textureMixFactor * textureFlavoredColor + (1.0 - textureMixFactor) * bakedTextureColor;
// --- 4. ПРИМЕНЕНИЕ ОСВЕЩЕНИЯ ---
// Умножаем результат текстурирования на освещенность
finalColor.rgb *= surfaceLightIntensity;
// --- 5. Расчет тумана (с использованием динамического цвета) ---
float h = uDistanceToPlanetSurface; float h = uDistanceToPlanetSurface;
float fogStart, fogEnd;
float fogStart;
float fogEnd;
if (h >= 1000.0) { if (h >= 1000.0) {
gl_FragColor = vec4(finalColor.rgb, 1.0); gl_FragColor = vec4(finalColor.rgb, 1.0);
@ -86,12 +104,11 @@ float h = uDistanceToPlanetSurface;
fogEnd = mix(250.0, 500.0, t); fogEnd = mix(250.0, 500.0, t);
} }
// Расчет фактора тумана
float fogRange = max(fogEnd - fogStart, 1.0); float fogRange = max(fogEnd - fogStart, 1.0);
float fogFactor = clamp((realDist - fogStart) / fogRange, 0.0, 1.0); float fogFactor = clamp((realDist - fogStart) / fogRange, 0.0, 1.0);
// --- Смешивание --- // Смешиваем с динамическим цветом тумана
vec3 mixedColor = mix(finalColor.rgb, FOG_COLOR.rgb, fogFactor); vec3 mixedColor = mix(finalColor.rgb, dynamicFogColor, fogFactor);
gl_FragColor = vec4(mixedColor, 1.0); gl_FragColor = vec4(mixedColor, 1.0);
} }

View File

@ -9,6 +9,7 @@ varying vec2 TexCoord;
varying vec3 Color; varying vec3 Color;
varying vec3 vViewDirTangent; varying vec3 vViewDirTangent;
varying vec3 worldPosition; varying vec3 worldPosition;
varying vec3 vWorldNormal;
uniform mat4 ProjectionModelViewMatrix; uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix; uniform mat4 ModelViewMatrix;
@ -16,6 +17,7 @@ uniform mat4 ModelViewMatrix;
uniform vec3 uViewPos; uniform vec3 uViewPos;
void main() { void main() {
vWorldNormal = vNormal;
gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0); gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0);
TexCoord = vTexCoord; TexCoord = vTexCoord;

View File

@ -1,34 +1,60 @@
// --- Улучшенный Фрагментный шейдер // planetStone фрагментный шейдер
varying vec2 TexCoord; varying vec2 TexCoord;
varying vec3 worldPosition; varying vec3 worldPosition;
varying vec3 vWorldNormal;
uniform sampler2D Texture; uniform sampler2D Texture;
uniform float uDistanceToPlanetSurface; uniform float uDistanceToPlanetSurface;
uniform vec3 uViewPos; uniform vec3 uViewPos;
uniform vec3 uLightDirWorld;
const vec4 FOG_COLOR = vec4(0.0, 0.5, 1.0, 1.0); uniform float uPlayerLightFactor;
void main() void main()
{ {
// --- 1. Подготовка векторов ---
vec3 normal = normalize(vWorldNormal);
vec3 lightToSource = normalize(-uLightDirWorld);
// Вектор от центра планеты к камню (нормаль самой поверхности планеты под камнем)
// Предполагаем, что центр планеты в (0,0,0)
vec3 surfaceNormal = normalize(worldPosition);
// --- 2. Расчет Shadow Mask ---
// Проверяем, освещена ли точка планеты, на которой стоит камень
float shadowMask = clamp(dot(surfaceNormal, lightToSource) * 5.0, 0.0, 1.0);
// Коэффициент 5.0 делает переход на терминаторе более четким
// --- 3. Освещение камня ---
float diff = max(dot(normal, lightToSource), 0.0);
// Применяем shadowMask к диффузной составляющей
// Если точка на планете в тени, то прямой свет (diff) обнуляется
float ambient = 0.15;
float lightIntensity = (diff * shadowMask);
lightIntensity *= mix(0.2, 1.0, shadowMask);
lightIntensity += ambient;
// --- 4. Остальная логика (цвета и туман) ---
vec3 dayFog = vec3(0.0, 0.5, 1.0);
vec3 nightFog = vec3(0.01, 0.01, 0.04);
vec3 dynamicFogColor = mix(nightFog, dayFog, uPlayerLightFactor);
vec4 textureColor = texture2D(Texture, TexCoord); vec4 textureColor = texture2D(Texture, TexCoord);
vec3 litColor = textureColor.rgb * lightIntensity;
float realDist = distance(worldPosition, uViewPos); float realDist = distance(worldPosition, uViewPos);
float h = uDistanceToPlanetSurface; float h = uDistanceToPlanetSurface;
float fogStart;
float fogEnd;
// --- Логика AlphaFactor (строго 2000-2500) ---
// Объект полностью исчезает на 2500.
// Если fogStart на высоте 1000 равен 2500, то туман и объект исчезнут одновременно.
float alphaFactor = clamp((2000.0 - realDist) / 500.0, 0.0, 1.0); float alphaFactor = clamp((2000.0 - realDist) / 500.0, 0.0, 1.0);
float fogStart, fogEnd;
// --- Логика расчета границ тумана --- // --- Логика расчета границ тумана ---
if (h >= 1000.0) { if (h >= 1000.0) {
gl_FragColor = vec4(textureColor.rgb, alphaFactor); gl_FragColor = vec4(litColor, alphaFactor);
} }
else else
{ {
@ -69,9 +95,8 @@ void main()
float fogRange = max(fogEnd - fogStart, 1.0); float fogRange = max(fogEnd - fogStart, 1.0);
float fogFactor = clamp((realDist - fogStart) / fogRange, 0.0, 1.0); float fogFactor = clamp((realDist - fogStart) / fogRange, 0.0, 1.0);
// --- Смешивание --- // Смешивание освещенного камня с динамическим туманом
vec3 mixedColor = mix(textureColor.rgb, FOG_COLOR.rgb, fogFactor); vec3 mixedColor = mix(litColor, dynamicFogColor, fogFactor);
gl_FragColor = vec4(mixedColor, alphaFactor); gl_FragColor = vec4(mixedColor, alphaFactor);
} }
} }

View File

@ -139,7 +139,7 @@ namespace ZL
#else #else
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor_fog.vertex", "./shaders/defaultColor_fog_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor_fog.vertex", "./shaders/defaultColor_fog_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env_sky.vertex", "./shaders/env_sky_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env_sky", "./shaders/env_sky.vertex", "./shaders/env_sky_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultColor2", "./shaders/defaultColor_fog2.vertex", "./shaders/defaultColor_fog2_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultColor2", "./shaders/defaultColor_fog2.vertex", "./shaders/defaultColor_fog2_desktop.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultColorStones", "./shaders/defaultColor_fog_stones.vertex", "./shaders/defaultColor_fog_stones_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultColorStones", "./shaders/defaultColor_fog_stones.vertex", "./shaders/defaultColor_fog_stones_desktop.fragment", CONST_ZIP_FILE);
@ -303,7 +303,7 @@ namespace ZL
void Game::drawCubemap(float skyPercent) void Game::drawCubemap(float skyPercent)
{ {
static const std::string defaultShaderName = "default"; static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env"; static const std::string envShaderName = "env_sky";
static const std::string vPositionName = "vPosition"; static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord"; static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture"; static const std::string textureUniformName = "Texture";
@ -320,6 +320,36 @@ namespace ZL
renderer.LoadIdentity(); renderer.LoadIdentity();
renderer.RotateMatrix(Environment::inverseShipMatrix); renderer.RotateMatrix(Environment::inverseShipMatrix);
Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
Matrix3f viewMatrix = Environment::inverseShipMatrix;
Vector3f viewLightDir = (viewMatrix * worldLightDir).normalized();
// Передаем вектор НА источник света
Vector3f lightToSource = -viewLightDir;
renderer.RenderUniform3fv("uLightDirView", lightToSource.data());
// 2. Базовый цвет атмосферы (голубой)
Vector3f skyColor = { 0.0f, 0.5f, 1.0f };
renderer.RenderUniform3fv("uSkyColor", skyColor.data());
// 1. Вектор направления от центра планеты к игроку (в мировых координатах)
// Предполагаем, что планета в (0,0,0). Если нет, то (shipPosition - planetCenter)
Vector3f playerDirWorld = Environment::shipPosition.normalized();
// 2. Направление света в мировом пространстве
//Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
// 3. Считаем глобальную освещенность для игрока (насколько он на свету)
// Это одно число для всего кадра
float playerLightFactor = playerDirWorld.dot(-worldLightDir);
// Ограничиваем и делаем переход мягче
playerLightFactor = std::clamp((playerLightFactor + 0.2f) / 1.2f, 0.0f, 1.0f);
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
renderer.RenderUniform1f("skyPercent", skyPercent);
CheckGlError(); CheckGlError();
glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture->getTexID()); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture->getTexID());
@ -509,17 +539,17 @@ namespace ZL
float skyPercent = 0.0; float skyPercent = 0.0;
float distance = planetObject.distanceToPlanetSurface(Environment::shipPosition); float distance = planetObject.distanceToPlanetSurface(Environment::shipPosition);
if (distance > 1900.f) if (distance > 1500.f)
{ {
skyPercent = 0.0f; skyPercent = 0.0f;
} }
else if (distance < 1000.f) else if (distance < 800.f)
{ {
skyPercent = 1.0f; skyPercent = 1.0f;
} }
else else
{ {
skyPercent = (1900.f - distance) / 900.f; skyPercent = (1500.f - distance) / (1500.f - 800.f);
} }

View File

@ -275,7 +275,18 @@ namespace ZL {
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
renderer.RenderUniform1f("uCurrentZFar", currentZFar); renderer.RenderUniform1f("uCurrentZFar", currentZFar);
// Направление на солнце в мировом пространстве
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
renderer.RenderUniform3fv("uLightDirWorld", sunDirWorld.data());
// Направление от центра планеты к игроку для расчета дня/ночи
Vector3f playerDirWorld = Environment::shipPosition.normalized();
renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
// Тот же фактор освещенности игрока
float playerLightFactor = playerDirWorld.dot(-sunDirWorld);
playerLightFactor = max(0.0f, (playerLightFactor + 0.2f) / 1.2f);
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
glActiveTexture(GL_TEXTURE1); glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID()); glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID());
@ -337,6 +348,14 @@ namespace ZL {
renderer.RenderUniform1f("uCurrentZFar", currentZFar); renderer.RenderUniform1f("uCurrentZFar", currentZFar);
renderer.RenderUniform3fv("uViewPos", Environment::shipPosition.data()); renderer.RenderUniform3fv("uViewPos", Environment::shipPosition.data());
// PlanetObject.cpp, метод drawStones
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
renderer.RenderUniform3fv("uLightDirWorld", sunDirWorld.data());
Vector3f playerDirWorld = Environment::shipPosition.normalized();
float playerLightFactor = max(0.0f, (playerDirWorld.dot(-sunDirWorld) + 0.2f) / 1.2f);
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
glEnable(GL_CULL_FACE); glEnable(GL_CULL_FACE);
glCullFace(GL_BACK); glCullFace(GL_BACK);
glEnable(GL_BLEND); glEnable(GL_BLEND);
@ -377,11 +396,16 @@ namespace ZL {
renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vNormalName); renderer.EnableVertexAttribArray(vNormalName);
float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipPosition); float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipPosition);
auto zRange = planetData.calculateZRange(dist); auto zRange = planetData.calculateZRange(dist);
const float currentZNear = zRange.first; float currentZNear = zRange.first;
const float currentZFar = zRange.second; float currentZFar = zRange.second;
if (currentZNear < 200)
{
currentZNear = 200;
currentZFar = currentZNear * 100;
}
// 2. Применяем динамическую матрицу проекции // 2. Применяем динамическую матрицу проекции
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
@ -405,6 +429,47 @@ namespace ZL {
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
// В начале drawAtmosphere или как uniform
float time = SDL_GetTicks() / 1000.0f;
renderer.RenderUniform1f("uTime", time);
// В методе PlanetObject::drawAtmosphere
Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
// Получаем текущую матрицу вида (ModelView без трансляции объекта)
// В вашем движке это Environment::inverseShipMatrix
Matrix3f viewMatrix2 = Environment::inverseShipMatrix;
// Трансформируем вектор света в пространство вида
Vector3f viewLightDir = viewMatrix2 * worldLightDir;
// Передаем инвертированный вектор (направление НА источник)
Vector3f lightToSource = -viewLightDir.normalized();
renderer.RenderUniform3fv("uLightDirView", lightToSource.data());
// В методе Game::drawCubemap
renderer.RenderUniform1f("uTime", SDL_GetTicks() / 1000.0f);
// Направление света в мировом пространстве для освещения облаков
//Vector3f worldLightDir = Vector3f(1.0f, -1.0f, -1.0f).normalized();
renderer.RenderUniform3fv("uWorldLightDir", worldLightDir.data());
// 1. Рассчитываем uPlayerLightFactor (как в Game.cpp)
Vector3f playerDirWorld = Environment::shipPosition.normalized();
Vector3f sunDirWorld = Vector3f(1.0f, -1.0f, -1.0f).normalized();
// Насколько игрок на свету
float playerLightFactor = playerDirWorld.dot(-sunDirWorld);
playerLightFactor = max(0.0f, (playerLightFactor + 0.2f) / 1.2f); // Мягкий порог
// 2. ОБЯЗАТЕЛЬНО передаем в шейдер
renderer.RenderUniform1f("uPlayerLightFactor", playerLightFactor);
// 3. Убедитесь, что uSkyColor тоже передан (в коде выше его не было)
renderer.RenderUniform3fv("uSkyColor", color.data());
//Vector3f playerDirWorld = Environment::shipPosition.normalized();
renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data());
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения

View File

@ -10,8 +10,7 @@
namespace ZL { namespace ZL {
const float StoneParams::BASE_SCALE = 10.0f; // Общий размер камня
const float StoneParams::BASE_SCALE = 15.0f; // Общий размер камня
const float StoneParams::MIN_AXIS_SCALE = 1.0f; // Минимальное растяжение/сжатие по оси const float StoneParams::MIN_AXIS_SCALE = 1.0f; // Минимальное растяжение/сжатие по оси
const float StoneParams::MAX_AXIS_SCALE = 1.0f; // Максимальное растяжение/сжатие по оси const float StoneParams::MAX_AXIS_SCALE = 1.0f; // Максимальное растяжение/сжатие по оси
const float StoneParams::MIN_PERTURBATION = 0.0f; // Минимальное радиальное возмущение вершины const float StoneParams::MIN_PERTURBATION = 0.0f; // Минимальное радиальное возмущение вершины
@ -22,6 +21,7 @@ namespace ZL {
const float StoneParams::MAX_PERTURBATION = 0.25f; // Максимальное радиальное возмущение вершины const float StoneParams::MAX_PERTURBATION = 0.25f; // Максимальное радиальное возмущение вершины
*/ */
const int StoneParams::STONES_PER_TRIANGLE = 40; const int StoneParams::STONES_PER_TRIANGLE = 40;
//const int StoneParams::STONES_PER_TRIANGLE = 1;
// Вспомогательная функция для получения случайного числа в диапазоне [min, max] // Вспомогательная функция для получения случайного числа в диапазоне [min, max]
float getRandomFloat(std::mt19937& gen, float min, float max) { float getRandomFloat(std::mt19937& gen, float min, float max) {