diff --git a/shaders/defaultAtmosphere.fragment b/shaders/defaultAtmosphere.fragment index 04d43b2..2564d5f 100644 --- a/shaders/defaultAtmosphere.fragment +++ b/shaders/defaultAtmosphere.fragment @@ -6,43 +6,113 @@ 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() { - // --- 1. Расчет плотности по Френелю (краевой эффект) --- - vec3 viewVector = normalize(-vViewPosition); - float NdotV = dot(vViewNormal, viewVector); - float factor = 1.0 - abs(NdotV); - float atmosphereDensity = pow(factor, 5.0); + vec3 normal = normalize(vViewNormal); + vec3 viewVector = normalize(-vViewPosition); + float NdotV = dot(normal, viewVector); - // --- 2. Расчет коэффициента затухания по дистанции --- + // Вектор направления от центра планеты к текущему фрагменту атмосферы (Мировой) + vec3 fragmentDir = normalize(vWorldNormal); + // Вектор направления от центра планеты к игроку (нужно передать как uniform) + // Передай его в C++: renderer.RenderUniform3fv("uPlayerDirWorld", playerDirWorld.data()); + - float distanceFactor = 1.0; + // --- 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); - if (uDistanceToPlanetSurface > DIST_FOG_MAX) - { - // Дистанция > 2000.0: Полностью видно (Factor = 1.0) - 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); + + // --- 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; // Принудительно гасим дымку под горизонтом } - // --- 3. Финальный цвет и прозрачность --- - - float finalAlpha = atmosphereDensity * distanceFactor; - - gl_FragColor = vec4(uColor, finalAlpha); + gl_FragColor = vec4(finalRGB, max(finalAtmoAlpha, finalCloudAlpha)); } \ No newline at end of file diff --git a/shaders/defaultAtmosphere.vertex b/shaders/defaultAtmosphere.vertex index 2faba8b..8199dae 100644 --- a/shaders/defaultAtmosphere.vertex +++ b/shaders/defaultAtmosphere.vertex @@ -1,28 +1,17 @@ // Вершинный шейдер: attribute vec3 vPosition; -attribute vec3 vNormal; // <-- Новый атрибут +attribute vec3 vNormal; uniform mat4 ProjectionModelViewMatrix; uniform mat4 ModelViewMatrix; -// Выходные переменные (передаются во фрагментный шейдер) -varying vec3 vViewNormal; +varying vec3 vWorldNormal; +varying vec3 vViewNormal; varying vec3 vViewPosition; -void main() -{ - // 1. Позиция в пространстве вида (View Space) - 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); - // W=0.0 для векторов, чтобы игнорировать трансляцию - - gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); +void main() { + vWorldNormal = vPosition; // Локальная позиция вершины на сфере радиуса 1.0 + vViewPosition = (ModelViewMatrix * vec4(vPosition, 1.0)).xyz; + vViewNormal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz); + gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0); } \ No newline at end of file diff --git a/shaders/defaultAtmosphere_debug1.fragment b/shaders/defaultAtmosphere_debug1.fragment new file mode 100644 index 0000000..aa6a13e --- /dev/null +++ b/shaders/defaultAtmosphere_debug1.fragment @@ -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)); +} \ No newline at end of file diff --git a/shaders/defaultAtmosphere_debug2.fragment b/shaders/defaultAtmosphere_debug2.fragment new file mode 100644 index 0000000..6c0385f --- /dev/null +++ b/shaders/defaultAtmosphere_debug2.fragment @@ -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));*/ +} \ No newline at end of file diff --git a/shaders/env_sky.vertex b/shaders/env_sky.vertex index 5903df6..44dbb0f 100644 --- a/shaders/env_sky.vertex +++ b/shaders/env_sky.vertex @@ -1,12 +1,9 @@ attribute vec3 vPosition; - uniform mat4 ProjectionModelViewMatrix; +varying vec3 vViewDir; -varying vec3 dir; - -void main(){ - vec4 realVertexPos = vec4(vPosition.xyz, 1.0); - gl_Position = ProjectionModelViewMatrix * realVertexPos; - - dir = vPosition; +void main() { + gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0); + // Направление взгляда в пространстве вида (вершина куба относительно центра) + vViewDir = vPosition; } \ No newline at end of file diff --git a/shaders/env_sky_desktop.fragment b/shaders/env_sky_desktop.fragment index c6139e3..b3b00aa 100644 --- a/shaders/env_sky_desktop.fragment +++ b/shaders/env_sky_desktop.fragment @@ -1,11 +1,36 @@ uniform samplerCube Texture; -uniform float skyPercent; +uniform float skyPercent; +uniform float uPlayerLightFactor; // Глобальный фактор дня/ночи для позиции игрока +uniform vec3 uSkyColor; -varying vec3 dir; +varying vec3 vViewDir; -void main(){ - vec4 skyBoxColor = textureCube(Texture, normalize(dir)); - vec4 skyColor = vec4(0.0, 0.5, 1.0, 1.0); - gl_FragColor = skyPercent*skyColor + (1.0 - skyPercent) * skyBoxColor; +void main() { + vec3 viewDir = normalize(vViewDir); + + // 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); } \ No newline at end of file diff --git a/shaders/planet_land.vertex b/shaders/planet_land.vertex index 856c57b..bbec690 100644 --- a/shaders/planet_land.vertex +++ b/shaders/planet_land.vertex @@ -10,6 +10,7 @@ varying vec3 Color; varying vec2 TexCoord; varying vec3 vViewDirTangent; varying vec3 worldPosition; +varying vec3 vWorldNormal; uniform mat4 ProjectionModelViewMatrix; uniform mat4 ModelViewMatrix; @@ -33,5 +34,7 @@ void main() { worldPosition = vPosition; + vWorldNormal = vPosition; + Color = vColor; } \ No newline at end of file diff --git a/shaders/planet_land_desktop.fragment b/shaders/planet_land_desktop.fragment index 054fc08..aa1e657 100644 --- a/shaders/planet_land_desktop.fragment +++ b/shaders/planet_land_desktop.fragment @@ -2,6 +2,7 @@ varying vec2 TexCoord; varying vec3 vViewDirTangent; varying vec3 Color; varying vec3 worldPosition; +varying vec3 vWorldNormal; uniform sampler2D Texture; uniform sampler2D BakedTexture; @@ -12,41 +13,58 @@ uniform float uCurrentZFar; uniform vec3 uViewPos; 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() { + // --- 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); - - // Получаем высоту из альфа-канала запеченной текстуры 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(0,0); + vec2 p = vec2(viewDir.x, -viewDir.y) * (height * uHeightScale); vec2 finalTexCoord = TexCoord + p; - - float realDist = distance(worldPosition, uViewPos); - - - float textureMixFactor = clamp((2000 - realDist) / 500.0, 0.0, 1.0); - - vec4 bakedTextureColor = texture2D(BakedTexture, finalTexCoord); - vec4 textureColor = texture2D(Texture, TexCoord); - - vec3 flavoredRGB = mix(textureColor.rgb, textureColor.rgb * Color, 0.7); - vec4 textureFlavoredColor = vec4(textureColor.rgb * Color, 1.0); - - if (bakedTextureColor.x < 0.01 && bakedTextureColor.y < 0.01 && bakedTextureColor.y < 0.01) - { - textureMixFactor = 1.0; - } - - vec4 finalColor = textureMixFactor*textureFlavoredColor + (1 - textureMixFactor) * bakedTextureColor; - -float h = uDistanceToPlanetSurface; - float fogStart; - float fogEnd; + float realDist = distance(worldPosition, uViewPos); + float textureMixFactor = clamp((2000.0 - realDist) / 500.0, 0.0, 1.0); + + vec4 bakedTextureColor = texture2D(BakedTexture, finalTexCoord); + vec4 textureColor = texture2D(Texture, TexCoord); + vec4 textureFlavoredColor = vec4(textureColor.rgb * Color, 1.0); + + if (bakedTextureColor.x < 0.01 && bakedTextureColor.y < 0.01 && bakedTextureColor.z < 0.01) { + textureMixFactor = 1.0; + } + + vec4 finalColor = textureMixFactor * textureFlavoredColor + (1.0 - textureMixFactor) * bakedTextureColor; + + // --- 4. ПРИМЕНЕНИЕ ОСВЕЩЕНИЯ --- + // Умножаем результат текстурирования на освещенность + finalColor.rgb *= surfaceLightIntensity; + + // --- 5. Расчет тумана (с использованием динамического цвета) --- + float h = uDistanceToPlanetSurface; + float fogStart, fogEnd; if (h >= 1000.0) { gl_FragColor = vec4(finalColor.rgb, 1.0); @@ -86,12 +104,11 @@ float h = uDistanceToPlanetSurface; fogEnd = mix(250.0, 500.0, t); } - // Расчет фактора тумана float fogRange = max(fogEnd - fogStart, 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); } diff --git a/shaders/planet_stone.vertex b/shaders/planet_stone.vertex index 55496c8..841ea5f 100644 --- a/shaders/planet_stone.vertex +++ b/shaders/planet_stone.vertex @@ -9,6 +9,7 @@ varying vec2 TexCoord; varying vec3 Color; varying vec3 vViewDirTangent; varying vec3 worldPosition; +varying vec3 vWorldNormal; uniform mat4 ProjectionModelViewMatrix; uniform mat4 ModelViewMatrix; @@ -16,6 +17,7 @@ uniform mat4 ModelViewMatrix; uniform vec3 uViewPos; void main() { + vWorldNormal = vNormal; gl_Position = ProjectionModelViewMatrix * vec4(vPosition, 1.0); TexCoord = vTexCoord; diff --git a/shaders/planet_stone_desktop.fragment b/shaders/planet_stone_desktop.fragment index 8411b83..1bae2c5 100644 --- a/shaders/planet_stone_desktop.fragment +++ b/shaders/planet_stone_desktop.fragment @@ -1,34 +1,60 @@ -// --- Улучшенный Фрагментный шейдер +// planetStone фрагментный шейдер varying vec2 TexCoord; varying vec3 worldPosition; +varying vec3 vWorldNormal; uniform sampler2D Texture; uniform float uDistanceToPlanetSurface; uniform vec3 uViewPos; - -const vec4 FOG_COLOR = vec4(0.0, 0.5, 1.0, 1.0); - +uniform vec3 uLightDirWorld; +uniform float uPlayerLightFactor; 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); + vec3 litColor = textureColor.rgb * lightIntensity; + float realDist = distance(worldPosition, uViewPos); 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) { - gl_FragColor = vec4(textureColor.rgb, alphaFactor); + gl_FragColor = vec4(litColor, alphaFactor); } else { @@ -67,11 +93,10 @@ void main() // Расчет фактора тумана 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); - - gl_FragColor = vec4(mixedColor, alphaFactor); + // Смешивание освещенного камня с динамическим туманом + vec3 mixedColor = mix(litColor, dynamicFogColor, fogFactor); + gl_FragColor = vec4(mixedColor, alphaFactor); } } \ No newline at end of file diff --git a/src/Game.cpp b/src/Game.cpp index 2534d1a..da44a12 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -139,7 +139,7 @@ namespace ZL #else 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("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("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); @@ -303,7 +303,7 @@ namespace ZL void Game::drawCubemap(float skyPercent) { 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 vTexCoordName = "vTexCoord"; static const std::string textureUniformName = "Texture"; @@ -320,6 +320,36 @@ namespace ZL renderer.LoadIdentity(); 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(); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture->getTexID()); @@ -509,17 +539,17 @@ namespace ZL float skyPercent = 0.0; float distance = planetObject.distanceToPlanetSurface(Environment::shipPosition); - if (distance > 1900.f) + if (distance > 1500.f) { skyPercent = 0.0f; } - else if (distance < 1000.f) + else if (distance < 800.f) { skyPercent = 1.0f; } else { - skyPercent = (1900.f - distance) / 900.f; + skyPercent = (1500.f - distance) / (1500.f - 800.f); } diff --git a/src/planet/PlanetObject.cpp b/src/planet/PlanetObject.cpp index 14c1d0f..711246d 100644 --- a/src/planet/PlanetObject.cpp +++ b/src/planet/PlanetObject.cpp @@ -275,7 +275,18 @@ namespace ZL { renderer.RenderUniform1f("uDistanceToPlanetSurface", dist); 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); glBindTexture(GL_TEXTURE_2D, stoneMapFB->getTextureID()); @@ -337,6 +348,14 @@ namespace ZL { renderer.RenderUniform1f("uCurrentZFar", currentZFar); 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); glCullFace(GL_BACK); glEnable(GL_BLEND); @@ -377,11 +396,16 @@ namespace ZL { renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vNormalName); - float dist = planetData.distanceToPlanetSurfaceFast(Environment::shipPosition); auto zRange = planetData.calculateZRange(dist); - const float currentZNear = zRange.first; - const float currentZFar = zRange.second; + float currentZNear = zRange.first; + float currentZFar = zRange.second; + + if (currentZNear < 200) + { + currentZNear = 200; + currentZFar = currentZNear * 100; + } // 2. Применяем динамическую матрицу проекции renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, @@ -405,6 +429,47 @@ namespace ZL { 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); glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения diff --git a/src/planet/StoneObject.cpp b/src/planet/StoneObject.cpp index 5b30152..ece3ddc 100644 --- a/src/planet/StoneObject.cpp +++ b/src/planet/StoneObject.cpp @@ -10,8 +10,7 @@ namespace ZL { - - const float StoneParams::BASE_SCALE = 15.0f; // Общий размер камня + const float StoneParams::BASE_SCALE = 10.0f; // Общий размер камня const float StoneParams::MIN_AXIS_SCALE = 1.0f; // Минимальное растяжение/сжатие по оси const float StoneParams::MAX_AXIS_SCALE = 1.0f; // Максимальное растяжение/сжатие по оси const float StoneParams::MIN_PERTURBATION = 0.0f; // Минимальное радиальное возмущение вершины @@ -22,6 +21,7 @@ namespace ZL { const float StoneParams::MAX_PERTURBATION = 0.25f; // Максимальное радиальное возмущение вершины */ const int StoneParams::STONES_PER_TRIANGLE = 40; + //const int StoneParams::STONES_PER_TRIANGLE = 1; // Вспомогательная функция для получения случайного числа в диапазоне [min, max] float getRandomFloat(std::mt19937& gen, float min, float max) {