Compare commits

...

18 Commits

Author SHA1 Message Date
Vladislav Khorev
7a1961b5d1 Somehow planet is working 2025-12-14 13:00:39 +03:00
Vladislav Khorev
af550f56a7 Cool working 2025-12-13 23:17:03 +03:00
Vladislav Khorev
27fb7a448c planet is closed 2025-12-13 20:34:47 +03:00
Vladislav Khorev
2b6af91992 Good constants 2025-12-13 20:16:40 +03:00
Vladislav Khorev
4afb9d6f4f Somehow planet is working 2025-12-13 20:11:48 +03:00
Vladislav Khorev
fe361885c0 Working on planet 2025-12-13 15:02:03 +03:00
Vladislav Khorev
3887208b8d Working on planet 2025-12-12 23:53:34 +03:00
Vladislav Khorev
4c837eff0a merge 2025-12-12 23:07:25 +03:00
Vladislav Khorev
8f9a18d960 Merge branch 'main' into spark 2025-12-12 22:45:57 +03:00
Vlad
bc49540a95 add blend 2025-12-12 17:12:03 +06:00
Vlad
d2605d9108 moved drawEffect() to SparkEmitter 2025-12-12 16:58:23 +06:00
Vlad
115cbbb7fa fix spark 2025-12-11 20:09:41 +06:00
ae1ca71a20 test 2025-12-11 06:42:02 +00:00
c02273d29d added ship velocity using dragging volume 2025-12-10 20:58:10 +06:00
fec7e08c2b added dragging scroll button for music volume 2025-12-10 20:39:09 +06:00
0f937e2e02 added music volume bar, without logic 2025-12-09 21:12:54 +06:00
Vlad
b855cff0e6 added sparks on the back of the spaceship 2025-12-08 19:28:47 +06:00
Vladislav Khorev
b768b27d49 update readme 2025-12-08 00:15:11 +03:00
28 changed files with 2046 additions and 427 deletions

View File

@ -436,6 +436,10 @@ add_executable(space-game001
OpenGlExtensions.h OpenGlExtensions.h
Utils.cpp Utils.cpp
Utils.h Utils.h
SparkEmitter.cpp
SparkEmitter.h
PlanetObject.cpp
PlanetObject.h
) )
# Установка проекта по умолчанию для Visual Studio # Установка проекта по умолчанию для Visual Studio

View File

@ -8,7 +8,7 @@ namespace ZL {
int Environment::windowHeaderHeight = 0; int Environment::windowHeaderHeight = 0;
int Environment::width = 0; int Environment::width = 0;
int Environment::height = 0; int Environment::height = 0;
float Environment::zoom = 6.f; float Environment::zoom = 18.f;
bool Environment::leftPressed = false; bool Environment::leftPressed = false;
bool Environment::rightPressed = false; bool Environment::rightPressed = false;
@ -31,9 +31,13 @@ bool Environment::tapDownHold = false;
Vector2f Environment::tapDownStartPos = { 0, 0 }; Vector2f Environment::tapDownStartPos = { 0, 0 };
Vector2f Environment::tapDownCurrentPos = { 0, 0 }; Vector2f Environment::tapDownCurrentPos = { 0, 0 };
Vector3f Environment::shipPosition = {0,0,0}; Vector3f Environment::shipPosition = {0,0,45000.f};
float Environment::shipVelocity = 0.f; float Environment::shipVelocity = 0.f;
const float Environment::CONST_Z_NEAR = 5.f;
const float Environment::CONST_Z_FAR = 5000.f;
} // namespace ZL } // namespace ZL

View File

@ -37,6 +37,10 @@ public:
static Vector3f shipPosition; static Vector3f shipPosition;
static float shipVelocity; static float shipVelocity;
static const float CONST_Z_NEAR;
static const float CONST_Z_FAR;
}; };

337
Game.cpp
View File

@ -1,4 +1,4 @@
#include "Game.h" #include "Game.h"
#include "AnimatedModel.h" #include "AnimatedModel.h"
#include "BoneAnimatedModel.h" #include "BoneAnimatedModel.h"
#include "Utils.h" #include "Utils.h"
@ -19,11 +19,11 @@ namespace ZL
Vector4f generateRandomQuaternion(std::mt19937& gen) Vector4f generateRandomQuaternion(std::mt19937& gen)
{ {
// Распределение для генерации случайных координат кватерниона // Ðàñïðåäåëåíèå äëÿ ãåíåðàöèè ñëó÷àéíûõ êîîðäèíàò êâàòåðíèîíà
std::normal_distribution<> distrib(0.0, 1.0); std::normal_distribution<> distrib(0.0, 1.0);
// Генерируем четыре случайных числа из нормального распределения N(0, 1). // Ãåíåðèðóåì ÷åòûðå ñëó÷àéíûõ ÷èñëà èç íîðìàëüíîãî ðàñïðåäåëåíèÿ N(0, 1).
// Нормализация этого вектора дает равномерное распределение по 4D-сфере (т.е. кватернион единичной длины). // Íîðìàëèçàöèÿ ýòîãî âåêòîðà äàåò ðàâíîìåðíîå ðàñïðåäåëåíèå ïî 4D-ñôåðå (ò.å. êâàòåðíèîí åäèíè÷íîé äëèíû).
Vector4f randomQuat = { Vector4f randomQuat = {
(float)distrib(gen), (float)distrib(gen),
(float)distrib(gen), (float)distrib(gen),
@ -35,25 +35,25 @@ namespace ZL
} }
// --- Основная функция генерации --- // --- Îñíîâíàÿ ôóíêöèÿ ãåíåðàöèè ---
std::vector<BoxCoords> generateRandomBoxCoords(int N) std::vector<BoxCoords> generateRandomBoxCoords(int N)
{ {
// Константы // Êîíñòàíòû
const float MIN_DISTANCE = 3.0f; const float MIN_DISTANCE = 3.0f;
const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE; // Работаем с квадратом расстояния const float MIN_DISTANCE_SQUARED = MIN_DISTANCE * MIN_DISTANCE; // Ðàáîòàåì ñ êâàäðàòîì ðàññòîÿíèÿ
const float MIN_COORD = -100.0f; const float MIN_COORD = -100.0f;
const float MAX_COORD = 100.0f; const float MAX_COORD = 100.0f;
const int MAX_ATTEMPTS = 1000; // Ограничение на количество попыток, чтобы избежать бесконечного цикла const int MAX_ATTEMPTS = 1000; // Îãðàíè÷åíèå íà êîëè÷åñòâî ïîïûòîê, ÷òîáû èçáåæàòü áåñêîíå÷íîãî öèêëà
std::vector<BoxCoords> boxCoordsArr; std::vector<BoxCoords> boxCoordsArr;
boxCoordsArr.reserve(N); // Резервируем память boxCoordsArr.reserve(N); // Ðåçåðâèðóåì ïàìÿòü
// 1. Инициализация генератора псевдослучайных чисел // 1. Èíèöèàëèçàöèÿ ãåíåðàòîðà ïñåâäîñëó÷àéíûõ ÷èñåë
// Используем Mersenne Twister (mt19937) как высококачественный генератор // Èñïîëüçóåì Mersenne Twister (mt19937) êàê âûñîêîêà÷åñòâåííûé ãåíåðàòîð
std::random_device rd; std::random_device rd;
std::mt19937 gen(rd()); std::mt19937 gen(rd());
// 2. Определение равномерного распределения для координат [MIN_COORD, MAX_COORD] // 2. Îïðåäåëåíèå ðàâíîìåðíîãî ðàñïðåäåëåíèÿ äëÿ êîîðäèíàò [MIN_COORD, MAX_COORD]
std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD); std::uniform_real_distribution<> distrib(MIN_COORD, MAX_COORD);
int generatedCount = 0; int generatedCount = 0;
@ -63,53 +63,53 @@ namespace ZL
bool accepted = false; bool accepted = false;
int attempts = 0; int attempts = 0;
// Попытка найти подходящие координаты // Ïîïûòêà íàéòè ïîäõîäÿùèå êîîðäèíàòû
while (!accepted && attempts < MAX_ATTEMPTS) while (!accepted && attempts < MAX_ATTEMPTS)
{ {
// Генерируем новые случайные координаты // Ãåíåðèðóåì íîâûå ñëó÷àéíûå êîîðäèíàòû
Vector3f newPos( Vector3f newPos(
(float)distrib(gen), (float)distrib(gen),
(float)distrib(gen), (float)distrib(gen),
(float)distrib(gen) (float)distrib(gen)
); );
// Проверка расстояния до всех уже существующих объектов // Ïðîâåðêà ðàññòîÿíèÿ äî âñåõ óæå ñóùåñòâóþùèõ îáúåêòîâ
accepted = true; // Предполагаем, что подходит, пока не доказано обратное accepted = true; // Ïðåäïîëàãàåì, ÷òî ïîäõîäèò, ïîêà íå äîêàçàíî îáðàòíîå
for (const auto& existingBox : boxCoordsArr) for (const auto& existingBox : boxCoordsArr)
{ {
// Расчет вектора разности // Ðàñ÷åò âåêòîðà ðàçíîñòè
Vector3f diff = newPos - existingBox.pos; Vector3f diff = newPos - existingBox.pos;
// Расчет квадрата расстояния // Ðàñ÷åò êâàäðàòà ðàññòîÿíèÿ
float distanceSquared = diff.squaredNorm(); float distanceSquared = diff.squaredNorm();
// Если квадрат расстояния меньше квадрата минимального расстояния // Åñëè êâàäðàò ðàññòîÿíèÿ ìåíüøå êâàäðàòà ìèíèìàëüíîãî ðàññòîÿíèÿ
if (distanceSquared < MIN_DISTANCE_SQUARED) if (distanceSquared < MIN_DISTANCE_SQUARED)
{ {
accepted = false; // Отклоняем, слишком близко accepted = false; // Îòêëîíÿåì, ñëèøêîì áëèçêî
break; // Нет смысла проверять дальше, если одно нарушение найдено break; // Íåò ñìûñëà ïðîâåðÿòü äàëüøå, åñëè îäíî íàðóøåíèå íàéäåíî
} }
} }
if (accepted) if (accepted)
{ {
// 2. Генерируем случайный кватернион // 2. Ãåíåðèðóåì ñëó÷àéíûé êâàòåðíèîí
Vector4f randomQuat = generateRandomQuaternion(gen); Vector4f randomQuat = generateRandomQuaternion(gen);
// 3. Преобразуем его в матрицу вращения // 3. Ïðåîáðàçóåì åãî â ìàòðèöó âðàùåíèÿ
Matrix3f randomMatrix = QuatToMatrix(randomQuat); Matrix3f randomMatrix = QuatToMatrix(randomQuat);
// 4. Добавляем объект с новой случайной матрицей // 4. Äîáàâëÿåì îáúåêò ñ íîâîé ñëó÷àéíîé ìàòðèöåé
boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix }); boxCoordsArr.emplace_back(BoxCoords{ newPos, randomMatrix });
generatedCount++; generatedCount++;
} }
attempts++; attempts++;
} }
// Если превышено максимальное количество попыток, выходим из цикла, // Åñëè ïðåâûøåíî ìàêñèìàëüíîå êîëè÷åñòâî ïîïûòîê, âûõîäèì èç öèêëà,
// чтобы избежать зависания, если N слишком велико или диапазон слишком мал. // ÷òîáû èçáåæàòü çàâèñàíèÿ, åñëè N ñëèøêîì âåëèêî èëè äèàïàçîí ñëèøêîì ìàë.
if (!accepted) { if (!accepted) {
std::cerr << "Предупреждение: Не удалось сгенерировать " << N << " объектов. Сгенерировано: " << generatedCount << std::endl; std::cerr << "Ïðåäóïðåæäåíèå: Íå óäàëîñü ñãåíåðèðîâàòü " << N << " îáúåêòîâ. Ñãåíåðèðîâàíî: " << generatedCount << std::endl;
break; break;
} }
} }
@ -117,15 +117,20 @@ namespace ZL
return boxCoordsArr; return boxCoordsArr;
} }
Game::Game() Game::Game()
: window(nullptr) : window(nullptr)
, glContext(nullptr) , glContext(nullptr)
, newTickCount(0) , newTickCount(0)
, lastTickCount(0) , lastTickCount(0)
{ {
} std::vector<Vector3f> emissionPoints = {
Vector3f{-2.1f, 0.9f, 5.0f},
Vector3f{2.1f, 0.9f, 5.0f}
};
sparkEmitter = SparkEmitter(emissionPoints, 100.0f);
}
Game::~Game() { Game::~Game() {
if (glContext) { if (glContext) {
SDL_GL_DeleteContext(glContext); SDL_GL_DeleteContext(glContext);
} }
@ -133,9 +138,9 @@ Game::~Game() {
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
} }
SDL_Quit(); SDL_Quit();
} }
void Game::setup() { void Game::setup() {
glContext = SDL_GL_CreateContext(ZL::Environment::window); glContext = SDL_GL_CreateContext(ZL::Environment::window);
ZL::BindOpenGlFunctions(); ZL::BindOpenGlFunctions();
@ -147,11 +152,13 @@ void Game::setup() {
renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("default", "./shaders/default.vertex", "./shaders/default_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("defaultColor", "./shaders/defaultColor.vertex", "./shaders/defaultColor_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_web.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env", "./shaders/env.vertex", "./shaders/env_web.fragment", CONST_ZIP_FILE);
renderer.shaderManager.AddShaderFromFiles("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE);
#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.vertex", "./shaders/defaultColor_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.vertex", "./shaders/env_desktop.fragment", CONST_ZIP_FILE); renderer.shaderManager.AddShaderFromFiles("env", "./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);
#endif #endif
@ -193,24 +200,91 @@ void Game::setup() {
boxRenderArr[i].RefreshVBO(); boxRenderArr[i].RefreshVBO();
} }
sparkTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/spark.png", CONST_ZIP_FILE));
sparkEmitter.setTexture(sparkTexture);
buttonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/button.png", CONST_ZIP_FILE));
button.data.PositionData.push_back({ 100, 100, 0 });
button.data.PositionData.push_back({ 100, 150, 0 });
button.data.PositionData.push_back({ 300, 150, 0 });
button.data.PositionData.push_back({ 100, 100, 0 });
button.data.PositionData.push_back({ 300, 150, 0 });
button.data.PositionData.push_back({ 300, 100, 0 });
button.data.TexCoordData.push_back({ 0,0 });
button.data.TexCoordData.push_back({ 0,1 });
button.data.TexCoordData.push_back({ 1,1 });
button.data.TexCoordData.push_back({ 0,0 });
button.data.TexCoordData.push_back({ 1,1 });
button.data.TexCoordData.push_back({ 1,0 });
button.RefreshVBO();
musicVolumeBarTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarTexture.png", CONST_ZIP_FILE));
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
musicVolumeBar.data.PositionData.push_back({ 1190, 600, 0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
musicVolumeBar.data.PositionData.push_back({ 1190, 100, 0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 600, 0 });
musicVolumeBar.data.PositionData.push_back({ 1200, 100, 0 });
musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
musicVolumeBar.data.TexCoordData.push_back({ 0,1 });
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
musicVolumeBar.data.TexCoordData.push_back({ 0,0 });
musicVolumeBar.data.TexCoordData.push_back({ 1,1 });
musicVolumeBar.data.TexCoordData.push_back({ 1,0 });
musicVolumeBar.RefreshVBO();
musicVolumeBarButtonTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/musicVolumeBarButton.png", CONST_ZIP_FILE));
float musicVolumeBarButtonButtonCenterY = 350.0f;
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.PositionData.push_back({ musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 });
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
musicVolumeBarButton.data.TexCoordData.push_back({ 0,1 });
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
musicVolumeBarButton.data.TexCoordData.push_back({ 0,0 });
musicVolumeBarButton.data.TexCoordData.push_back({ 1,1 });
musicVolumeBarButton.data.TexCoordData.push_back({ 1,0 });
musicVolumeBarButton.RefreshVBO();
renderer.InitOpenGL(); renderer.InitOpenGL();
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
} planetObject.init();
void Game::drawCubemap() }
{
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";
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";
static const std::string skyPercentUniformName = "skyPercent";
renderer.shaderManager.PushShader(envShaderName); renderer.shaderManager.PushShader(envShaderName);
renderer.RenderUniform1i(textureUniformName, 0); renderer.RenderUniform1i(textureUniformName, 0);
renderer.RenderUniform1f(skyPercentUniformName, skyPercent);
renderer.EnableVertexAttribArray(vPositionName); renderer.EnableVertexAttribArray(vPositionName);
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height), static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
1, 1000); Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix(); renderer.PushMatrix();
renderer.LoadIdentity(); renderer.LoadIdentity();
renderer.RotateMatrix(Environment::inverseShipMatrix); renderer.RotateMatrix(Environment::inverseShipMatrix);
@ -229,9 +303,10 @@ void Game::drawCubemap()
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();
CheckGlError(); CheckGlError();
} }
void Game::drawShip()
{ void Game::drawShip()
{
static const std::string defaultShaderName = "default"; static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env"; static const std::string envShaderName = "env";
static const std::string vPositionName = "vPosition"; static const std::string vPositionName = "vPosition";
@ -245,7 +320,7 @@ void Game::drawShip()
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height), static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
1, 1000); Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix(); renderer.PushMatrix();
renderer.LoadIdentity(); renderer.LoadIdentity();
@ -254,6 +329,7 @@ void Game::drawShip()
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID()); glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
renderer.DrawVertexRenderStruct(spaceship); renderer.DrawVertexRenderStruct(spaceship);
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
renderer.PopMatrix(); renderer.PopMatrix();
renderer.PopProjectionMatrix(); renderer.PopProjectionMatrix();
@ -262,10 +338,10 @@ void Game::drawShip()
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();
CheckGlError(); CheckGlError();
} }
void Game::drawBoxes() void Game::drawBoxes()
{ {
static const std::string defaultShaderName = "default"; static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env"; static const std::string envShaderName = "env";
static const std::string vPositionName = "vPosition"; static const std::string vPositionName = "vPosition";
@ -279,7 +355,7 @@ void Game::drawBoxes()
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height), static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
1, 1000); Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
for (int i = 0; i < boxCoordsArr.size(); i++) for (int i = 0; i < boxCoordsArr.size(); i++)
{ {
@ -303,30 +379,123 @@ void Game::drawBoxes()
renderer.shaderManager.PopShader(); renderer.shaderManager.PopShader();
CheckGlError(); CheckGlError();
} }
void Game::UpdateVolumeKnob() {
float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY);
void Game::drawScene() { auto& pos = musicVolumeBarButton.data.PositionData;
pos[0] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
pos[1] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
pos[2] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
pos[3] = { musicVolumeBarButtonButtonCenterX - musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
pos[4] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY + musicVolumeBarButtonButtonRadius, 0 };
pos[5] = { musicVolumeBarButtonButtonCenterX + musicVolumeBarButtonButtonRadius, musicVolumeBarButtonButtonCenterY - musicVolumeBarButtonButtonRadius, 0 };
musicVolumeBarButton.RefreshVBO();
}
void Game::UpdateVolumeFromMouse(int mouseX, int mouseY) {
int uiX = mouseX;
int uiY = Environment::height - mouseY;
Environment::shipVelocity = (musicVolume) * (20.0);
if (uiY < volumeBarMinY || uiY > volumeBarMaxY)
{
return;
}
float t = (uiY - volumeBarMinY) / (volumeBarMaxY - volumeBarMinY);
if (t < 0.0f) t = 0.0f;
if (t > 1.0f) t = 1.0f;
musicVolume = t;
UpdateVolumeKnob();
}
void Game::drawUI()
{
static const std::string defaultShaderName = "default"; static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env"; static const std::string envShaderName = "env";
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";
glClearColor(0.0f, 0.5f, 1.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT);
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
renderer.PushProjectionMatrix(Environment::width, Environment::height, -1, 1);
renderer.PushMatrix();
renderer.LoadIdentity();
glBindTexture(GL_TEXTURE_2D, buttonTexture->getTexID());
renderer.DrawVertexRenderStruct(button);
glBindTexture(GL_TEXTURE_2D, musicVolumeBarTexture->getTexID());
renderer.DrawVertexRenderStruct(musicVolumeBar);
glBindTexture(GL_TEXTURE_2D, musicVolumeBarButtonTexture->getTexID());
renderer.DrawVertexRenderStruct(musicVolumeBarButton);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.shaderManager.PopShader();
CheckGlError();
}
void Game::drawScene() {
static const std::string defaultShaderName = "default";
static const std::string envShaderName = "env";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(0, 0, Environment::width, Environment::height); glViewport(0, 0, Environment::width, Environment::height);
CheckGlError(); CheckGlError();
drawCubemap(); float skyPercent = 0.0;
float distance = planetObject.distanceToPlanetSurface();
if (distance > 2000.f)
{
skyPercent = 0.0f;
}
else if (distance < 1000.f)
{
skyPercent = 1.0f;
}
else
{
skyPercent = (2000.f - distance) / 1000.f;
}
drawCubemap(skyPercent);
planetObject.draw(renderer);
if (planetObject.planetIsFar())
{
glClear(GL_DEPTH_BUFFER_BIT);
}
drawShip(); drawShip();
drawBoxes(); //drawBoxes();
drawUI();
CheckGlError(); CheckGlError();
} }
void Game::processTickCount() { void Game::processTickCount() {
if (lastTickCount == 0) { if (lastTickCount == 0) {
lastTickCount = SDL_GetTicks64(); lastTickCount = SDL_GetTicks64();
@ -339,6 +508,7 @@ void Game::processTickCount() {
CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount; CONST_MAX_TIME_INTERVAL : newTickCount - lastTickCount;
//gameObjects.updateScene(delta); //gameObjects.updateScene(delta);
sparkEmitter.update(static_cast<float>(delta));
if (Environment::tapDownHold) { if (Environment::tapDownHold) {
@ -375,7 +545,7 @@ void Game::processTickCount() {
if (fabs(Environment::shipVelocity) > 0.01f) if (fabs(Environment::shipVelocity) > 0.01f)
{ {
Vector3f velocityDirection = { 0,0, -Environment::shipVelocity*delta / 1000.f }; Vector3f velocityDirection = { 0,0, -Environment::shipVelocity * delta / 1000.f };
Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection); Vector3f velocityDirectionAdjusted = MultMatrixVector(Environment::shipMatrix, velocityDirection);
Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted; Environment::shipPosition = Environment::shipPosition + velocityDirectionAdjusted;
@ -383,9 +553,9 @@ void Game::processTickCount() {
lastTickCount = newTickCount; lastTickCount = newTickCount;
} }
} }
void Game::render() { void Game::render() {
SDL_GL_MakeCurrent(ZL::Environment::window, glContext); SDL_GL_MakeCurrent(ZL::Environment::window, glContext);
ZL::CheckGlError(); ZL::CheckGlError();
@ -396,8 +566,8 @@ void Game::render() {
processTickCount(); processTickCount();
SDL_GL_SwapWindow(ZL::Environment::window); SDL_GL_SwapWindow(ZL::Environment::window);
} }
void Game::update() { void Game::update() {
SDL_Event event; SDL_Event event;
while (SDL_PollEvent(&event)) { while (SDL_PollEvent(&event)) {
if (event.type == SDL_QUIT) { if (event.type == SDL_QUIT) {
@ -405,23 +575,46 @@ void Game::update() {
} }
else if (event.type == SDL_MOUSEBUTTONDOWN) { else if (event.type == SDL_MOUSEBUTTONDOWN) {
// 1. Обработка нажатия кнопки мыши // 1. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè
int mx = event.button.x;
int my = event.button.y;
std::cout << mx << " " << my << '\n';
int uiX = mx;
int uiY = Environment::height - my;
if (uiX >= volumeBarMinX - 40 && uiX <= volumeBarMaxX + 40 &&
uiY >= volumeBarMinY - 40 && uiY <= volumeBarMaxY + 40) {
isDraggingVolume = true;
UpdateVolumeFromMouse(mx, my);
}
else {
Environment::tapDownHold = true; Environment::tapDownHold = true;
// Координаты начального нажатия // Êîîðäèíàòû íà÷àëüíîãî íàæàòèÿ
Environment::tapDownStartPos.v[0] = event.button.x; Environment::tapDownStartPos.v[0] = event.button.x;
Environment::tapDownStartPos.v[1] = event.button.y; Environment::tapDownStartPos.v[1] = event.button.y;
// Начальная позиция также становится текущей // Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé
Environment::tapDownCurrentPos.v[0] = event.button.x; Environment::tapDownCurrentPos.v[0] = event.button.x;
Environment::tapDownCurrentPos.v[1] = event.button.y; Environment::tapDownCurrentPos.v[1] = event.button.y;
} }
}
else if (event.type == SDL_MOUSEBUTTONUP) { else if (event.type == SDL_MOUSEBUTTONUP) {
// 2. Обработка отпускания кнопки мыши // 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè
isDraggingVolume = false;
Environment::tapDownHold = false; Environment::tapDownHold = false;
} }
else if (event.type == SDL_MOUSEMOTION) { else if (event.type == SDL_MOUSEMOTION) {
// 3. Обработка перемещения мыши // 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè
int mx = event.motion.x;
int my = event.motion.y;
if (isDraggingVolume) {
// Äâèãàåì ìûøü ïî ñëàéäåðó — ìåíÿåì ãðîìêîñòü è ïîçèöèþ êðóæêà
UpdateVolumeFromMouse(mx, my);
}
if (Environment::tapDownHold) { if (Environment::tapDownHold) {
// Обновление текущей позиции, если кнопка удерживается // Îáíîâëåíèå òåêóùåé ïîçèöèè, åñëè êíîïêà óäåðæèâàåòñÿ
Environment::tapDownCurrentPos.v[0] = event.motion.x; Environment::tapDownCurrentPos.v[0] = event.motion.x;
Environment::tapDownCurrentPos.v[1] = event.motion.y; Environment::tapDownCurrentPos.v[1] = event.motion.y;
} }
@ -442,15 +635,23 @@ void Game::update() {
{ {
if (event.key.keysym.sym == SDLK_i) if (event.key.keysym.sym == SDLK_i)
{ {
Environment::shipVelocity += 1.f; Environment::shipVelocity += 500.f;
} }
if (event.key.keysym.sym == SDLK_k) if (event.key.keysym.sym == SDLK_k)
{ {
Environment::shipVelocity -= 1.f; Environment::shipVelocity -= 500.f;
}
if (event.key.keysym.sym == SDLK_o)
{
Environment::shipVelocity += 50.f;
}
if (event.key.keysym.sym == SDLK_l)
{
Environment::shipVelocity -= 50.f;
} }
} }
} }
render(); render();
} }
} // namespace ZL } // namespace ZL

47
Game.h
View File

@ -1,9 +1,11 @@
#pragma once #pragma once
#include "OpenGlExtensions.h" #include "OpenGlExtensions.h"
#include "Renderer.h" #include "Renderer.h"
#include "Environment.h" #include "Environment.h"
#include "TextureManager.h" #include "TextureManager.h"
#include "SparkEmitter.h"
#include "PlanetObject.h"
namespace ZL { namespace ZL {
@ -15,8 +17,8 @@ namespace ZL {
}; };
class Game { class Game {
public: public:
Game(); Game();
~Game(); ~Game();
@ -24,14 +26,16 @@ public:
void update(); void update();
void render(); void render();
bool shouldExit() const { return Environment::exitGameLoop; } bool shouldExit() const { return Environment::exitGameLoop; }
private: private:
void processTickCount(); void processTickCount();
void drawScene(); void drawScene();
void drawCubemap(); void drawCubemap(float skyPercent);
void drawShip(); void drawShip();
void drawBoxes(); void drawBoxes();
void drawUI();
SDL_Window* window; SDL_Window* window;
SDL_GLContext glContext; SDL_GLContext glContext;
@ -40,9 +44,36 @@ private:
size_t newTickCount; size_t newTickCount;
size_t lastTickCount; size_t lastTickCount;
std::vector<BoxCoords> boxCoordsArr;
std::vector<VertexRenderStruct> boxRenderArr;
std::shared_ptr<Texture> buttonTexture;
VertexRenderStruct button;
std::shared_ptr<Texture> musicVolumeBarTexture;
VertexRenderStruct musicVolumeBar;
std::shared_ptr<Texture> musicVolumeBarButtonTexture;
VertexRenderStruct musicVolumeBarButton;
bool isDraggingVolume = false;
float musicVolume = 0.0f;
float volumeBarMinX = 1190.0f;
float volumeBarMaxX = 1200.0f;
float volumeBarMinY = 100.0f;
float volumeBarMaxY = 600.0f;
float musicVolumeBarButtonButtonCenterX = 1195.0f;
float musicVolumeBarButtonButtonRadius = 25.0f;
void UpdateVolumeFromMouse(int mouseX, int mouseY);
void UpdateVolumeKnob();
static const size_t CONST_TIMER_INTERVAL = 10; static const size_t CONST_TIMER_INTERVAL = 10;
static const size_t CONST_MAX_TIME_INTERVAL = 1000; static const size_t CONST_MAX_TIME_INTERVAL = 1000;
std::shared_ptr<Texture> sparkTexture;
std::shared_ptr<Texture> spaceshipTexture; std::shared_ptr<Texture> spaceshipTexture;
std::shared_ptr<Texture> cubemapTexture; std::shared_ptr<Texture> cubemapTexture;
VertexDataStruct spaceshipBase; VertexDataStruct spaceshipBase;
@ -53,9 +84,9 @@ private:
std::shared_ptr<Texture> boxTexture; std::shared_ptr<Texture> boxTexture;
VertexDataStruct boxBase; VertexDataStruct boxBase;
std::vector<BoxCoords> boxCoordsArr; SparkEmitter sparkEmitter;
std::vector<VertexRenderStruct> boxRenderArr; PlanetObject planetObject;
};
};
} // namespace ZL } // namespace ZL

582
PlanetObject.cpp Normal file
View File

@ -0,0 +1,582 @@
#include "PlanetObject.h"
#include <random>
#include <cmath>
#include "OpenGlExtensions.h"
#include "Environment.h"
namespace ZL {
static constexpr float PLANET_RADIUS = 20000.f;
static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, 0.0f };
//static Vector3f PLANET_CENTER_OFFSET = Vector3f{ 0.f, 0.f, -45000.f };
// --- 1. Дальний диапазон (FAR) ---
static constexpr float FAR_Z_NEAR = 2000.0f;
static constexpr float FAR_Z_FAR = 200000.0f;
// Дистанция, где НАЧИНАЕТСЯ переход FAR -> MIDDLE
static constexpr float TRANSITION_FAR_START = 3000.0f;
// --- 2. Средний диапазон (MIDDLE) ---
static constexpr float MIDDLE_Z_NEAR = 500.f;
static constexpr float MIDDLE_Z_FAR = 50000.f;
// Дистанция, где ЗАВЕРШАЕТСЯ переход FAR -> MIDDLE и НАЧИНАЕТСЯ MIDDLE -> NEAR
static constexpr float TRANSITION_MIDDLE_START = 500.f;
// --- 3. Ближний диапазон (NEAR) ---
// Новые константы для максимальной точности
static constexpr float NEAR_Z_NEAR = 100.0f;
static constexpr float NEAR_Z_FAR = 10000.0f;
// Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR
static constexpr float TRANSITION_NEAR_END = 100.f;
// --- 3. Ближний диапазон (NEAR) ---
// Новые константы для максимальной точности
static constexpr float SUPER_NEAR_Z_NEAR = 5.0f;
static constexpr float SUPER_NEAR_Z_FAR = 5000.0f;
// Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR
static constexpr float TRANSITION_SUPER_NEAR_END = 10.f;
std::pair<float, float> calculateZRange(const Vector3f& shipPosition) {
// 1. Вычисление расстояния до поверхности планеты
const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET;
const float distanceToPlanetCenter = (planetWorldPosition - shipPosition).length();
const float distanceToPlanetSurface = distanceToPlanetCenter - PLANET_RADIUS;
std::cout << "distanceToPlanetSurface " << distanceToPlanetSurface << std::endl;
float currentZNear;
float currentZFar;
float alpha; // Коэффициент интерполяции для текущего сегмента
// Диапазон I: Далеко (FAR) -> Средне (MIDDLE)
if (distanceToPlanetSurface >= TRANSITION_FAR_START) {
// Полностью дальний диапазон
currentZNear = FAR_Z_NEAR;
currentZFar = FAR_Z_FAR;
}
else if (distanceToPlanetSurface > TRANSITION_MIDDLE_START) {
// Плавный переход от FAR к MIDDLE
const float transitionLength = TRANSITION_FAR_START - TRANSITION_MIDDLE_START;
// Нормализация расстояния, 0 при TRANSITION_FAR_START (Далеко), 1 при TRANSITION_MIDDLE_START (Близко)
float normalizedDist = (distanceToPlanetSurface - TRANSITION_MIDDLE_START) / transitionLength;
alpha = 1.0f - normalizedDist; // alpha = 0 (Далеко) ... 1 (Близко)
// Интерполяция: FAR * (1-alpha) + MIDDLE * alpha
currentZNear = FAR_Z_NEAR * (1.0f - alpha) + MIDDLE_Z_NEAR * alpha;
currentZFar = FAR_Z_FAR * (1.0f - alpha) + MIDDLE_Z_FAR * alpha;
// Диапазон II: Средне (MIDDLE) -> Близко (NEAR)
}
else if (distanceToPlanetSurface > TRANSITION_NEAR_END) {
// Плавный переход от MIDDLE к NEAR
const float transitionLength = TRANSITION_MIDDLE_START - TRANSITION_NEAR_END;
// Нормализация расстояния, 0 при TRANSITION_MIDDLE_START (Далеко), 1 при TRANSITION_NEAR_END (Близко)
float normalizedDist = (distanceToPlanetSurface - TRANSITION_NEAR_END) / transitionLength;
alpha = 1.0f - normalizedDist; // alpha = 0 (Далеко) ... 1 (Близко)
// Интерполяция: MIDDLE * (1-alpha) + NEAR * alpha
currentZNear = MIDDLE_Z_NEAR * (1.0f - alpha) + NEAR_Z_NEAR * alpha;
currentZFar = MIDDLE_Z_FAR * (1.0f - alpha) + NEAR_Z_FAR * alpha;
}
else if (distanceToPlanetSurface > TRANSITION_SUPER_NEAR_END) {
// Плавный переход от MIDDLE к NEAR
const float transitionLength = TRANSITION_NEAR_END - TRANSITION_SUPER_NEAR_END;
// Нормализация расстояния, 0 при TRANSITION_MIDDLE_START (Далеко), 1 при TRANSITION_NEAR_END (Близко)
float normalizedDist = (distanceToPlanetSurface - TRANSITION_SUPER_NEAR_END) / transitionLength;
alpha = 1.0f - normalizedDist; // alpha = 0 (Далеко) ... 1 (Близко)
// Интерполяция: MIDDLE * (1-alpha) + NEAR * alpha
currentZNear = NEAR_Z_NEAR * (1.0f - alpha) + SUPER_NEAR_Z_NEAR * alpha;
currentZFar = NEAR_Z_FAR * (1.0f - alpha) + SUPER_NEAR_Z_FAR * alpha;
}
else {
// Полностью ближний диапазон (distanceToPlanetSurface <= TRANSITION_NEAR_END)
currentZNear = SUPER_NEAR_Z_NEAR;
currentZFar = SUPER_NEAR_Z_FAR;
}
return { currentZNear, currentZFar };
}
PerlinNoise::PerlinNoise() {
p.resize(256);
std::iota(p.begin(), p.end(), 0);
// Перемешиваем для случайности (можно задать seed)
std::default_random_engine engine(77777);
std::shuffle(p.begin(), p.end(), engine);
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
}
PerlinNoise::PerlinNoise(uint64_t seed) {
p.resize(256);
std::iota(p.begin(), p.end(), 0);
// Перемешиваем для случайности (используем переданный seed)
std::default_random_engine engine(seed); // <-- Использование сида
std::shuffle(p.begin(), p.end(), engine);
p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения
}
float PerlinNoise::fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
float PerlinNoise::lerp(float t, float a, float b) { return a + t * (b - a); }
float PerlinNoise::grad(int hash, float x, float y, float z) {
int h = hash & 15;
float u = h < 8 ? x : y;
float v = h < 4 ? y : (h == 12 || h == 14 ? x : z);
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
}
float PerlinNoise::noise(float x, float y, float z) {
int X = (int)floor(x) & 255;
int Y = (int)floor(y) & 255;
int Z = (int)floor(z) & 255;
x -= floor(x);
y -= floor(y);
z -= floor(z);
float u = fade(x);
float v = fade(y);
float w = fade(z);
int A = p[X] + Y, AA = p[A] + Z, AB = p[A + 1] + Z;
int B = p[X + 1] + Y, BA = p[B] + Z, BB = p[B + 1] + Z;
return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x - 1, y, z)),
lerp(u, grad(p[AB], x, y - 1, z), grad(p[BB], x - 1, y - 1, z))),
lerp(v, lerp(u, grad(p[AA + 1], x, y, z - 1), grad(p[BA + 1], x - 1, y, z - 1)),
lerp(u, grad(p[AB + 1], x, y - 1, z - 1), grad(p[BB + 1], x - 1, y - 1, z - 1))));
}
float PerlinNoise::getSurfaceHeight(Vector3f pos) {
// Частота шума (чем больше, тем больше "холмов")
float frequency = 7.0f;
// Получаем значение шума (обычно от -1 до 1)
float noiseValue = noise(pos.v[0] * frequency, pos.v[1] * frequency, pos.v[2] * frequency);
// Переводим из диапазона [-1, 1] в [0, 1]
//noiseValue = (noiseValue + 1.0f) * 0.5f;
// Масштабируем: хотим отклонение от 1.0 до 1.1
// Значит амплитуда = 0.1
float height = 1.0f; // * 0.2 даст вариацию высоты
//float height = 1.0f - (noiseValue * 0.01f); // * 0.2 даст вариацию высоты
return height;
}
bool PlanetObject::planetIsFar()
{
const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET;
const float distanceToPlanetCenter = (planetWorldPosition - Environment::shipPosition).length();
const float distanceToPlanetSurface = distanceToPlanetCenter - PLANET_RADIUS;
return distanceToPlanetSurface > 0.1*TRANSITION_MIDDLE_START;
}
float PlanetObject::distanceToPlanetSurface()
{
const Vector3f planetWorldPosition = PLANET_CENTER_OFFSET;
const float distanceToPlanetCenter = (planetWorldPosition - Environment::shipPosition).length();
const float distanceToPlanetSurface = distanceToPlanetCenter - PLANET_RADIUS;
return distanceToPlanetSurface;
}
PlanetObject::PlanetObject()
: perlin(77777)
, colorPerlin(123123)
{
}
void PlanetObject::init() {
planetMesh = generateSphere(8);
planetMesh.Scale(PLANET_RADIUS);
planetMesh.Move(PLANET_CENTER_OFFSET);
planetRenderStruct.data = planetMesh;
planetRenderStruct.RefreshVBO();
sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/sand.png", ""));
//sandTexture = std::make_unique<Texture>(CreateTextureDataFromPng("./resources/rock.png", ""));
planetAtmosphere.data = generateSphere(5);
planetAtmosphere.data.Scale(PLANET_RADIUS*1.03);
planetAtmosphere.data.Move(PLANET_CENTER_OFFSET);
planetAtmosphere.RefreshVBO();
}
void PlanetObject::prepareDrawData() {
if (!drawDataDirty) return;
drawDataDirty = false;
}
void PlanetObject::draw(Renderer& renderer) {
prepareDrawData();
static const std::string defaultShaderName = "defaultColor";
static const std::string vPositionName = "vPosition";
static const std::string vColorName = "vColor";
static const std::string vNormalName = "vNormal";
static const std::string vTexCoordName = "vTexCoord";
//static const std::string vTexCoord3Name = "vTexCoord3";
static const std::string textureUniformName = "Texture";
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vColorName);
renderer.EnableVertexAttribArray(vNormalName);
renderer.EnableVertexAttribArray(vTexCoordName);
//renderer.EnableVertexAttribArray(vTexCoord3Name);
const auto zRange = calculateZRange(Environment::shipPosition);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
// 2. Применяем динамическую матрицу проекции
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
currentZNear, currentZFar);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
renderer.TranslateMatrix(-Environment::shipPosition);
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
Vector3f lightDir_World = Vector3f(1.0f, 0.0f, -1.0f).normalized();
// В OpenGL/шейдерах удобнее работать с вектором, указывающим ОТ источника к поверхности.
Vector3f lightDirection_World = -lightDir_World; // Вектор, направленный от источника
Vector3f lightDirection_View;
lightDirection_View.v[0] = viewMatrix.m[0] * lightDirection_World.v[0] + viewMatrix.m[4] * lightDirection_World.v[1] + viewMatrix.m[8] * lightDirection_World.v[2];
lightDirection_View.v[1] = viewMatrix.m[1] * lightDirection_World.v[0] + viewMatrix.m[5] * lightDirection_World.v[1] + viewMatrix.m[9] * lightDirection_World.v[2];
lightDirection_View.v[2] = viewMatrix.m[2] * lightDirection_World.v[0] + viewMatrix.m[6] * lightDirection_World.v[1] + viewMatrix.m[10] * lightDirection_World.v[2];
lightDirection_View = lightDirection_View.normalized(); // Нормализуем на всякий случай
// Установка uniform-переменной
// Предполагается, что RenderUniform3fv определена в Renderer.h
renderer.RenderUniform3fv("uLightDirection", &lightDirection_View.v[0]);
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]);
float dist = distanceToPlanetSurface();
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
renderer.RenderUniform1f("uCurrentZFar", currentZFar);
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID());
renderer.DrawVertexRenderStruct(planetRenderStruct);
//glDisable(GL_BLEND);
CheckGlError();
renderer.PopMatrix();
renderer.PopProjectionMatrix();
//renderer.DisableVertexAttribArray(vTexCoord3Name);
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.DisableVertexAttribArray(vNormalName);
renderer.DisableVertexAttribArray(vColorName);
renderer.DisableVertexAttribArray(vPositionName);
renderer.shaderManager.PopShader();
CheckGlError();
drawAtmosphere(renderer);
}
void PlanetObject::drawAtmosphere(Renderer& renderer) {
static const std::string defaultShaderName = "defaultAtmosphere";
static const std::string vPositionName = "vPosition";
static const std::string vNormalName = "vNormal";
//glClear(GL_DEPTH_BUFFER_BIT);
glDepthMask(GL_FALSE);
renderer.shaderManager.PushShader(defaultShaderName);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vNormalName);
const auto zRange = calculateZRange(Environment::shipPosition);
const float currentZNear = zRange.first;
const float currentZFar = zRange.second;
// 2. Применяем динамическую матрицу проекции
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
currentZNear, currentZFar);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
renderer.RotateMatrix(Environment::inverseShipMatrix);
renderer.TranslateMatrix(-Environment::shipPosition);
const Matrix4f viewMatrix = renderer.GetCurrentModelViewMatrix();
Vector3f color = { 0.0, 0.5, 1.0 };
renderer.RenderUniform3fv("uColor", &color.v[0]);
renderer.RenderUniformMatrix4fv("ModelViewMatrix", false, &viewMatrix.m[0]);
float dist = distanceToPlanetSurface();
renderer.RenderUniform1f("uDistanceToPlanetSurface", dist);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
renderer.DrawVertexRenderStruct(planetAtmosphere);
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.DisableVertexAttribArray(vNormalName);
renderer.DisableVertexAttribArray(vPositionName);
renderer.shaderManager.PopShader();
CheckGlError();
}
void PlanetObject::update(float deltaTimeMs) {
}
std::vector<Triangle> PlanetObject::subdivideTriangles(const std::vector<Triangle>& inputTriangles) {
std::vector<Triangle> output;
output.reserve(inputTriangles.size() * 4);
for (const auto& t : inputTriangles) {
Vector3f a = t.data[0];
Vector3f b = t.data[1];
Vector3f c = t.data[2];
// 1. Вычисляем "сырые" середины
Vector3f m_ab = (a + b) * 0.5f;
Vector3f m_bc = (b + c) * 0.5f;
Vector3f m_ac = (a + c) * 0.5f;
// 2. Нормализуем их (получаем идеальную сферу радиуса 1)
m_ab = m_ab.normalized();
m_bc = m_bc.normalized();
m_ac = m_ac.normalized();
// 3. ПРИМЕНЯЕМ ШУМ: Смещаем точку по радиусу
m_ab = m_ab * perlin.getSurfaceHeight(m_ab);
m_bc = m_bc * perlin.getSurfaceHeight(m_bc);
m_ac = m_ac * perlin.getSurfaceHeight(m_ac);
// 4. Формируем новые треугольники
output.emplace_back(a, m_ab, m_ac);
output.emplace_back(m_ab, b, m_bc);
output.emplace_back(m_ac, m_bc, c);
output.emplace_back(m_ab, m_bc, m_ac);
}
return output;
}
Vector3f PlanetObject::calculateSurfaceNormal(Vector3f p_sphere) {
// p_sphere - это нормализованный вектор (точка на идеальной сфере)
float theta = 0.01f; // Шаг для "щупанья" соседей (epsilon)
// Нам нужно найти два вектора, касательных к сфере в точке p_sphere.
// Для этого берем любой вектор (например UP), делаем Cross Product, чтобы получить касательную.
// Если p_sphere совпадает с UP, берем RIGHT.
Vector3f up = Vector3f(0.0f, 1.0f, 0.0f);
if (abs(p_sphere.dot(up)) > 0.99f) {
up = Vector3f(1.0f, 0.0f, 0.0f);
}
Vector3f tangentX = (up.cross(p_sphere)).normalized();
Vector3f tangentY = (p_sphere.cross(tangentX)).normalized();
// Точки на идеальной сфере со смещением
Vector3f p0_dir = p_sphere;
Vector3f p1_dir = (p_sphere + tangentX * theta).normalized();
Vector3f p2_dir = (p_sphere + tangentY * theta).normalized();
// Реальные точки на искаженной поверхности
// p = dir * height(dir)
Vector3f p0 = p0_dir * perlin.getSurfaceHeight(p0_dir);
Vector3f p1 = p1_dir * perlin.getSurfaceHeight(p1_dir);
Vector3f p2 = p2_dir * perlin.getSurfaceHeight(p2_dir);
// Вектора от центральной точки к соседям
Vector3f v1 = p1 - p0;
Vector3f v2 = p2 - p0;
// Нормаль - это перпендикуляр к этим двум векторам
// Порядок (v2, v1) или (v1, v2) зависит от системы координат,
// здесь подбираем так, чтобы нормаль смотрела наружу.
return (-v2.cross(v1)).normalized();
}
VertexDataStruct PlanetObject::trianglesToVertices(const std::vector<Triangle>& triangles) {
VertexDataStruct buffer;
buffer.PositionData.reserve(triangles.size() * 3);
buffer.NormalData.reserve(triangles.size() * 3);
buffer.TexCoordData.reserve(triangles.size() * 3); // <-- РЕЗЕРВИРУЕМ
//buffer.TexCoord3Data.reserve(triangles.size() * 3); // <-- РЕЗЕРВИРУЕМ
// Стандартные UV-координаты для покрытия одного треугольника
// Покрывает текстурой всю грань.
const std::array<Vector2f, 3> triangleUVs = {
Vector2f(0.0f, 0.0f),
Vector2f(1.0f, 0.0f),
Vector2f(0.0f, 1.0f)
};
/*
const std::array<Vector3f, 3> barycentricCoords = {
Vector3f(1.0f, 0.0f, 0.0f), // Вершина 1
Vector3f(0.0f, 1.0f, 0.0f), // Вершина 2
Vector3f(0.0f, 0.0f, 1.0f) // Вершина 3
};*/
for (const auto& t : triangles) {
// Проходим по всем 3 вершинам треугольника
for (int i = 0; i < 3; i++) {
// p_geometry - это уже точка на поверхности (с шумом)
Vector3f p_geometry = t.data[i];
// Нам нужно восстановить направление от центра к этой точке,
// чтобы передать его в функцию расчета нормали.
// Так как (0,0,0) - центр, то normalize(p) даст нам направление.
Vector3f p_dir = p_geometry.normalized();
// Считаем аналитическую нормаль для этой конкретной точки
Vector3f normal = calculateSurfaceNormal(p_dir);
buffer.PositionData.push_back({ p_geometry });
buffer.NormalData.push_back({ normal });
//buffer.TexCoord3Data.push_back(barycentricCoords[i]);
buffer.TexCoordData.push_back(triangleUVs[i]);
}
}
return buffer;
}
VertexDataStruct PlanetObject::generateSphere(int subdivisions) {
// 1. Исходный октаэдр
std::vector<Triangle> geometry = {
Triangle{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, { 1.0f, 0.0f, 0.0f}}, // Top-Front-Right
Triangle{{ 0.0f, 1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // Top-Right-Back
Triangle{{ 0.0f, 1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, {-1.0f, 0.0f, 0.0f}}, // Top-Back-Left
Triangle{{ 0.0f, 1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // Top-Left-Front
Triangle{{ 0.0f, -1.0f, 0.0f}, { 1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}}, // Bottom-Right-Front
Triangle{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 1.0f}, {-1.0f, 0.0f, 0.0f}}, // Bottom-Front-Left
Triangle{{ 0.0f, -1.0f, 0.0f}, {-1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}}, // Bottom-Left-Back
Triangle{{ 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f}, { 1.0f, 0.0f, 0.0f}} // Bottom-Back-Right
};
// 2. ПРИМЕНЯЕМ ШУМ К ИСХОДНЫМ ВЕРШИНАМ
for (auto& t : geometry) {
for (int i = 0; i < 3; i++) {
Vector3f dir = t.data[i].normalized();
t.data[i] = dir * perlin.getSurfaceHeight(dir);
}
}
// 3. Разбиваем N раз
for (int i = 0; i < subdivisions; i++) {
geometry = subdivideTriangles(geometry);
}
// 4. Генерируем вершины И НОРМАЛИ с помощью trianglesToVertices
// ЭТО ЗАПОЛНИТ PositionData И NormalData
VertexDataStruct buffer = trianglesToVertices(geometry);
// Теперь нам нужно заполнить ColorData, используя ту же логику, что и раньше.
// Сначала резервируем место, так как trianglesToVertices не заполняет ColorData
buffer.ColorData.reserve(geometry.size() * 3);
// Базовый цвет и параметры
const Vector3f baseColor = { 0.498f, 0.416f, 0.0f };
const float colorFrequency = 5.0f;
const float colorAmplitude = 0.2f;
const Vector3f offsetR = { 0.1f, 0.2f, 0.3f };
const Vector3f offsetG = { 0.5f, 0.4f, 0.6f };
const Vector3f offsetB = { 0.9f, 0.8f, 0.7f };
for (const auto& t : geometry) {
for (int i = 0; i < 3; i++) {
// ВАЖНО: Мы используем геометрию из t.data[i] для получения направления,
// а не PositionData из buffer, поскольку там может не быть нужного порядка.
Vector3f p_geometry = t.data[i];
Vector3f dir = p_geometry.normalized();
// Вычисление цветового шума (как вы делали)
float noiseR = colorPerlin.noise(
(dir.v[0] + offsetR.v[0]) * colorFrequency,
(dir.v[1] + offsetR.v[1]) * colorFrequency,
(dir.v[2] + offsetR.v[2]) * colorFrequency
);
float noiseG = colorPerlin.noise(
(dir.v[0] + offsetG.v[0]) * colorFrequency,
(dir.v[1] + offsetG.v[1]) * colorFrequency,
(dir.v[2] + offsetG.v[2]) * colorFrequency
);
float noiseB = colorPerlin.noise(
(dir.v[0] + offsetB.v[0]) * colorFrequency,
(dir.v[1] + offsetB.v[1]) * colorFrequency,
(dir.v[2] + offsetB.v[2]) * colorFrequency
);
Vector3f colorOffset = {
noiseR * colorAmplitude,
noiseG * colorAmplitude,
noiseB * colorAmplitude
};
Vector3f finalColor = baseColor + colorOffset;
finalColor.v[0] = max(0.0f, min(1.0f, finalColor.v[0]));
finalColor.v[1] = max(0.0f, min(1.0f, finalColor.v[1]));
finalColor.v[2] = max(0.0f, min(1.0f, finalColor.v[2]));
// ДОБАВЛЯЕМ ТОЛЬКО ЦВЕТ, так как PositionData и NormalData уже заполнены!
buffer.ColorData.push_back(finalColor);
}
}
return buffer;
}
} // namespace ZL

79
PlanetObject.h Normal file
View File

@ -0,0 +1,79 @@
#pragma once
#include "ZLMath.h"
#include "Renderer.h"
#include "TextureManager.h"
#include <vector>
#include <chrono>
#include <iostream>
#include <string>
#include <array>
#include <numeric>
#include <random>
#include <algorithm>
namespace ZL {
struct Triangle
{
std::array<Vector3f, 3> data;
Triangle(Vector3f p1, Vector3f p2, Vector3f p3)
: data{ p1, p2, p3 }
{
}
};
class PerlinNoise {
std::vector<int> p;
public:
PerlinNoise();
PerlinNoise(uint64_t seed);
float fade(float t);
float lerp(float t, float a, float b);
float grad(int hash, float x, float y, float z);
float noise(float x, float y, float z);
float getSurfaceHeight(Vector3f pos);
};
class PlanetObject {
private:
PerlinNoise perlin;
PerlinNoise colorPerlin;
void prepareDrawData();
VertexDataStruct planetMesh;
VertexRenderStruct planetRenderStruct;
VertexRenderStruct planetAtmosphere;
std::shared_ptr<Texture> sandTexture;
public:
PlanetObject();
void init();
void update(float deltaTimeMs);
void draw(Renderer& renderer);
void drawAtmosphere(Renderer& renderer);
bool planetIsFar();
float distanceToPlanetSurface();
private:
bool drawDataDirty = true;
std::vector<Triangle> subdivideTriangles(const std::vector<Triangle>& inputTriangles);
Vector3f calculateSurfaceNormal(Vector3f p_sphere);
VertexDataStruct trianglesToVertices(const std::vector<Triangle>& triangles);
VertexDataStruct generateSphere(int subdivisions);
};
} // namespace ZL

View File

@ -2,6 +2,7 @@
download from https://cmake.org/download/ download from https://cmake.org/download/
Windows x64 Installer: cmake-4.2.0-windows-x86_64.msi Windows x64 Installer: cmake-4.2.0-windows-x86_64.msi
@ -148,7 +149,7 @@ embuilder build sdl2 sdl2_ttf sdl2_image sdl2_image_jpg sdl2_image_png
emcc main.cpp Game.cpp Environment.cpp BoneAnimatedModel.cpp Math.cpp Renderer.cpp TextModel.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -pthread -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sTOTAL_MEMORY=4294967296 -sINITIAL_MEMORY=3221225472 -sMAXIMUM_MEMORY=4294967296 -sALLOW_MEMORY_GROWTH=1 -I./thirdparty/libzip-1.11.3/build-emcmake/install/include -L./thirdparty/libzip-1.11.3/build-emcmake/install/lib -lzip -lz -sUSE_SDL_IMAGE=2 -sUSE_SDL=2 -sUSE_LIBPNG=1 --preload-file space-game001.zip -o space-game001.html emcc main.cpp Game.cpp Environment.cpp BoneAnimatedModel.cpp ZLMath.cpp Renderer.cpp TextModel.cpp ShaderManager.cpp TextureManager.cpp Utils.cpp OpenGlExtensions.cpp -O2 -std=c++14 -pthread -sUSE_PTHREADS=1 -sPTHREAD_POOL_SIZE=4 -sTOTAL_MEMORY=4294967296 -sINITIAL_MEMORY=3221225472 -sMAXIMUM_MEMORY=4294967296 -sALLOW_MEMORY_GROWTH=1 -I./thirdparty/libzip-1.11.3/build-emcmake/install/include -L./thirdparty/libzip-1.11.3/build-emcmake/install/lib -lzip -lz -sUSE_SDL_IMAGE=2 -sUSE_SDL=2 -sUSE_LIBPNG=1 --preload-file space-game001.zip -o space-game001.html
emrun --no_browser --port 8080 . emrun --no_browser --port 8080 .
``` ```

View File

@ -294,6 +294,18 @@ namespace ZL {
glBufferData(GL_ARRAY_BUFFER, data.TexCoordData.size() * 8, &data.TexCoordData[0], GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, data.TexCoordData.size() * 8, &data.TexCoordData[0], GL_STATIC_DRAW);
} }
if (data.TexCoord3Data.size() > 0)
{
if (!texCoord3VBO)
{
texCoord3VBO = std::make_shared<VBOHolder>();
}
glBindBuffer(GL_ARRAY_BUFFER, texCoord3VBO->getBuffer());
glBufferData(GL_ARRAY_BUFFER, data.TexCoord3Data.size() * 12, &data.TexCoord3Data[0], GL_STATIC_DRAW);
}
if (data.NormalData.size() > 0) if (data.NormalData.size() > 0)
{ {
@ -459,6 +471,12 @@ namespace ZL {
return ProjectionModelViewMatrix; return ProjectionModelViewMatrix;
} }
Matrix4f Renderer::GetCurrentModelViewMatrix()
{
return ModelviewMatrixStack.top();
}
void Renderer::SetMatrix() void Renderer::SetMatrix()
{ {
if (ProjectionMatrixStack.size() <= 0) if (ProjectionMatrixStack.size() <= 0)
@ -471,15 +489,16 @@ namespace ZL {
throw std::runtime_error("Modelview matrix stack out!"); throw std::runtime_error("Modelview matrix stack out!");
} }
ProjectionModelViewMatrix = ProjectionMatrixStack.top() * ModelviewMatrixStack.top(); Matrix4f& modelViewMatrix = ModelviewMatrixStack.top();
ProjectionModelViewMatrix = ProjectionMatrixStack.top() * modelViewMatrix;
static const std::string ProjectionModelViewMatrixName = "ProjectionModelViewMatrix"; static const std::string ProjectionModelViewMatrixName = "ProjectionModelViewMatrix";
//static const std::string ProjectionMatrixName = "ProjectionMatrix";
RenderUniformMatrix4fv(ProjectionModelViewMatrixName, false, &ProjectionModelViewMatrix.m[0]); RenderUniformMatrix4fv(ProjectionModelViewMatrixName, false, &ProjectionModelViewMatrix.m[0]);
//RenderUniformMatrix4fv(ProjectionMatrixName, false, &ProjectionMatrixStack.top().m[0]); static const std::string ModelViewMatrixName = "ModelViewMatrix";
RenderUniformMatrix4fv(ModelViewMatrixName, false, &modelViewMatrix.m[0]);
} }
@ -708,6 +727,18 @@ namespace ZL {
} }
void Renderer::RenderUniform1f(const std::string& uniformName, float value)
{
auto shader = shaderManager.GetCurrentShader();
auto uniform = shader->uniformList.find(uniformName);
if (uniform != shader->uniformList.end())
{
glUniform1fv(uniform->second, 1, &value);
}
}
void Renderer::VertexAttribPointer2fv(const std::string& attribName, int stride, const char* pointer) void Renderer::VertexAttribPointer2fv(const std::string& attribName, int stride, const char* pointer)
@ -733,6 +764,7 @@ namespace ZL {
static const std::string vBinormal("vBinormal"); static const std::string vBinormal("vBinormal");
static const std::string vColor("vColor"); static const std::string vColor("vColor");
static const std::string vTexCoord("vTexCoord"); static const std::string vTexCoord("vTexCoord");
static const std::string vTexCoord3("vTexCoord3");
static const std::string vPosition("vPosition"); static const std::string vPosition("vPosition");
//glBindVertexArray(VertexRenderStruct.vao->getBuffer()); //glBindVertexArray(VertexRenderStruct.vao->getBuffer());
@ -763,6 +795,11 @@ namespace ZL {
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoordVBO->getBuffer()); glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoordVBO->getBuffer());
VertexAttribPointer2fv(vTexCoord, 0, NULL); VertexAttribPointer2fv(vTexCoord, 0, NULL);
} }
if (VertexRenderStruct.data.TexCoord3Data.size() > 0)
{
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoord3VBO->getBuffer());
VertexAttribPointer2fv(vTexCoord3, 0, NULL);
}
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.positionVBO->getBuffer()); glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.positionVBO->getBuffer());
VertexAttribPointer3fv(vPosition, 0, NULL); VertexAttribPointer3fv(vPosition, 0, NULL);

View File

@ -44,6 +44,7 @@ namespace ZL {
{ {
std::vector<Vector3f> PositionData; std::vector<Vector3f> PositionData;
std::vector<Vector2f> TexCoordData; std::vector<Vector2f> TexCoordData;
std::vector<Vector3f> TexCoord3Data;
std::vector<Vector3f> NormalData; std::vector<Vector3f> NormalData;
std::vector<Vector3f> TangentData; std::vector<Vector3f> TangentData;
std::vector<Vector3f> BinormalData; std::vector<Vector3f> BinormalData;
@ -63,6 +64,7 @@ namespace ZL {
std::shared_ptr<VAOHolder> vao; std::shared_ptr<VAOHolder> vao;
std::shared_ptr<VBOHolder> positionVBO; std::shared_ptr<VBOHolder> positionVBO;
std::shared_ptr<VBOHolder> texCoordVBO; std::shared_ptr<VBOHolder> texCoordVBO;
std::shared_ptr<VBOHolder> texCoord3VBO;
std::shared_ptr<VBOHolder> normalVBO; std::shared_ptr<VBOHolder> normalVBO;
std::shared_ptr<VBOHolder> tangentVBO; std::shared_ptr<VBOHolder> tangentVBO;
std::shared_ptr<VBOHolder> binormalVBO; std::shared_ptr<VBOHolder> binormalVBO;
@ -108,6 +110,7 @@ namespace ZL {
Matrix4f GetProjectionModelViewMatrix(); Matrix4f GetProjectionModelViewMatrix();
Matrix4f GetCurrentModelViewMatrix();
void SetMatrix(); void SetMatrix();
@ -121,7 +124,7 @@ namespace ZL {
void RenderUniformMatrix4fv(const std::string& uniformName, bool transpose, const float* value); void RenderUniformMatrix4fv(const std::string& uniformName, bool transpose, const float* value);
void RenderUniform1i(const std::string& uniformName, const int value); void RenderUniform1i(const std::string& uniformName, const int value);
void RenderUniform3fv(const std::string& uniformName, const float* value); void RenderUniform3fv(const std::string& uniformName, const float* value);
void RenderUniform1f(const std::string& uniformName, float value);
void VertexAttribPointer2fv(const std::string& attribName, int stride, const char* pointer); void VertexAttribPointer2fv(const std::string& attribName, int stride, const char* pointer);

299
SparkEmitter.cpp Normal file
View File

@ -0,0 +1,299 @@
#include "SparkEmitter.h"
#include <random>
#include <cmath>
#include "OpenGlExtensions.h"
#include "Environment.h"
namespace ZL {
SparkEmitter::SparkEmitter()
: emissionRate(100.0f), isActive(true), drawDataDirty(true), maxParticles(200) {
particles.resize(maxParticles);
drawPositions.reserve(maxParticles * 6);
drawTexCoords.reserve(maxParticles * 6);
lastEmissionTime = std::chrono::steady_clock::now();
sparkQuad.data = VertexDataStruct();
}
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions, float rate)
: emissionPoints(positions), emissionRate(rate), isActive(true),
drawDataDirty(true), maxParticles(positions.size() * 100) {
particles.resize(maxParticles);
drawPositions.reserve(maxParticles * 6);
drawTexCoords.reserve(maxParticles * 6);
lastEmissionTime = std::chrono::steady_clock::now();
sparkQuad.data = VertexDataStruct();
}
SparkEmitter::SparkEmitter(const std::vector<Vector3f>& positions,
std::shared_ptr<Texture> tex,
float rate)
: emissionPoints(positions), texture(tex), emissionRate(rate),
isActive(true), drawDataDirty(true), maxParticles(positions.size() * 100) {
particles.resize(maxParticles);
drawPositions.reserve(maxParticles * 6);
drawTexCoords.reserve(maxParticles * 6);
lastEmissionTime = std::chrono::steady_clock::now();
sparkQuad.data = VertexDataStruct();
}
void SparkEmitter::setTexture(std::shared_ptr<Texture> tex) {
texture = tex;
}
void SparkEmitter::prepareDrawData() {
if (!drawDataDirty) return;
drawPositions.clear();
drawTexCoords.clear();
if (getActiveParticleCount() == 0) {
drawDataDirty = false;
return;
}
std::vector<std::pair<const SparkParticle*, float>> sortedParticles;
sortedParticles.reserve(getActiveParticleCount());
for (const auto& particle : particles) {
if (particle.active) {
sortedParticles.push_back({ &particle, particle.position.v[2] });
}
}
std::sort(sortedParticles.begin(), sortedParticles.end(),
[](const auto& a, const auto& b) {
return a.second > b.second;
});
for (const auto& [particlePtr, depth] : sortedParticles) {
const auto& particle = *particlePtr;
Vector3f pos = particle.position;
float size = 0.04f * particle.scale;
drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] });
drawTexCoords.push_back({ 0.0f, 0.0f });
drawPositions.push_back({ pos.v[0] - size, pos.v[1] + size, pos.v[2] });
drawTexCoords.push_back({ 0.0f, 1.0f });
drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] });
drawTexCoords.push_back({ 1.0f, 1.0f });
drawPositions.push_back({ pos.v[0] - size, pos.v[1] - size, pos.v[2] });
drawTexCoords.push_back({ 0.0f, 0.0f });
drawPositions.push_back({ pos.v[0] + size, pos.v[1] + size, pos.v[2] });
drawTexCoords.push_back({ 1.0f, 1.0f });
drawPositions.push_back({ pos.v[0] + size, pos.v[1] - size, pos.v[2] });
drawTexCoords.push_back({ 1.0f, 0.0f });
}
drawDataDirty = false;
}
void SparkEmitter::draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight) {
if (getActiveParticleCount() == 0) {
return;
}
if (!texture) {
return;
}
prepareDrawData();
if (drawPositions.empty()) {
return;
}
sparkQuad.data.PositionData = drawPositions;
sparkQuad.data.TexCoordData = drawTexCoords;
sparkQuad.RefreshVBO();
static const std::string defaultShaderName = "default";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
renderer.shaderManager.PushShader(defaultShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vTexCoordName);
float aspectRatio = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.TranslateMatrix({ 0, 0, -1.0f * zoom });
renderer.DrawVertexRenderStruct(sparkQuad);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
glDisable(GL_BLEND);
renderer.DisableVertexAttribArray(vPositionName);
renderer.DisableVertexAttribArray(vTexCoordName);
renderer.shaderManager.PopShader();
}
void SparkEmitter::update(float deltaTimeMs) {
auto currentTime = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
currentTime - lastEmissionTime).count();
if (isActive && elapsed >= emissionRate) {
emit();
lastEmissionTime = currentTime;
drawDataDirty = true;
}
bool anyChanged = false;
for (auto& particle : particles) {
if (particle.active) {
Vector3f oldPosition = particle.position;
float oldScale = particle.scale;
particle.position.v[0] += particle.velocity.v[0] * deltaTimeMs / 1000.0f;
particle.position.v[1] += particle.velocity.v[1] * deltaTimeMs / 1000.0f;
particle.position.v[2] += particle.velocity.v[2] * deltaTimeMs / 1000.0f;
particle.lifeTime += deltaTimeMs;
if (particle.lifeTime >= particle.maxLifeTime) {
particle.active = false;
anyChanged = true;
}
else {
float lifeRatio = particle.lifeTime / particle.maxLifeTime;
particle.scale = 1.0f - lifeRatio * 0.8f;
if (oldPosition.v[0] != particle.position.v[0] ||
oldPosition.v[1] != particle.position.v[1] ||
oldPosition.v[2] != particle.position.v[2] ||
oldScale != particle.scale) {
anyChanged = true;
}
}
}
}
if (anyChanged) {
drawDataDirty = true;
}
}
void SparkEmitter::emit() {
if (emissionPoints.empty()) return;
bool emitted = false;
for (int i = 0; i < emissionPoints.size(); ++i) {
bool particleFound = false;
for (auto& particle : particles) {
if (!particle.active) {
initParticle(particle, i);
particle.active = true;
particle.lifeTime = 0;
particle.position = emissionPoints[i];
particle.emitterIndex = i;
particleFound = true;
emitted = true;
break;
}
}
if (!particleFound && !particles.empty()) {
size_t oldestIndex = 0;
float maxLifeTime = 0;
for (size_t j = 0; j < particles.size(); ++j) {
if (particles[j].lifeTime > maxLifeTime) {
maxLifeTime = particles[j].lifeTime;
oldestIndex = j;
}
}
initParticle(particles[oldestIndex], i);
particles[oldestIndex].active = true;
particles[oldestIndex].lifeTime = 0;
particles[oldestIndex].position = emissionPoints[i];
particles[oldestIndex].emitterIndex = i;
emitted = true;
}
}
if (emitted) {
drawDataDirty = true;
}
}
void SparkEmitter::setEmissionPoints(const std::vector<Vector3f>& positions) {
emissionPoints = positions;
maxParticles = positions.size() * 100;
particles.resize(maxParticles);
drawDataDirty = true;
}
void SparkEmitter::initParticle(SparkParticle& particle, int emitterIndex) {
particle.velocity = getRandomVelocity(emitterIndex);
particle.scale = 1.0f;
particle.maxLifeTime = 800.0f + (rand() % 400);
particle.emitterIndex = emitterIndex;
}
Vector3f SparkEmitter::getRandomVelocity(int emitterIndex) {
static std::random_device rd;
static std::mt19937 gen(rd());
static std::uniform_real_distribution<> angleDist(0, 2 * M_PI);
static std::uniform_real_distribution<> speedDist(0.5f, 2.0f);
static std::uniform_real_distribution<> zSpeedDist(1.0f, 3.0f);
float angle = angleDist(gen);
float speed = speedDist(gen);
float zSpeed = zSpeedDist(gen);
if (emitterIndex == 0) {
return Vector3f{
cosf(angle) * speed - 0.3f,
sinf(angle) * speed,
zSpeed
};
}
else {
return Vector3f{
cosf(angle) * speed + 0.3f,
sinf(angle) * speed,
zSpeed
};
}
}
const std::vector<SparkParticle>& SparkEmitter::getParticles() const {
return particles;
}
size_t SparkEmitter::getActiveParticleCount() const {
size_t count = 0;
for (const auto& particle : particles) {
if (particle.active) {
count++;
}
}
return count;
}
} // namespace ZL

66
SparkEmitter.h Normal file
View File

@ -0,0 +1,66 @@
#pragma once
#include "ZLMath.h"
#include "Renderer.h"
#include "TextureManager.h"
#include <vector>
#include <chrono>
namespace ZL {
struct SparkParticle {
Vector3f position;
Vector3f velocity;
float scale;
float lifeTime;
float maxLifeTime;
bool active;
int emitterIndex;
SparkParticle() : position({ 0,0,0 }), velocity({ 0,0,0 }), scale(1.0f),
lifeTime(0), maxLifeTime(1000.0f), active(false), emitterIndex(0) {
}
};
class SparkEmitter {
private:
std::vector<SparkParticle> particles;
std::vector<Vector3f> emissionPoints;
std::chrono::steady_clock::time_point lastEmissionTime;
float emissionRate;
bool isActive;
std::vector<Vector3f> drawPositions;
std::vector<Vector2f> drawTexCoords;
bool drawDataDirty;
VertexRenderStruct sparkQuad;
std::shared_ptr<Texture> texture;
int maxParticles;
void prepareDrawData();
public:
SparkEmitter();
SparkEmitter(const std::vector<Vector3f>& positions, float rate = 100.0f);
SparkEmitter(const std::vector<Vector3f>& positions,
std::shared_ptr<Texture> tex,
float rate = 100.0f);
void setEmissionPoints(const std::vector<Vector3f>& positions);
void setTexture(std::shared_ptr<Texture> tex);
void update(float deltaTimeMs);
void emit();
void draw(Renderer& renderer, float zoom, int screenWidth, int screenHeight);
const std::vector<SparkParticle>& getParticles() const;
size_t getActiveParticleCount() const;
private:
void initParticle(SparkParticle& particle, int emitterIndex);
Vector3f getRandomVelocity(int emitterIndex);
};
} // namespace ZL

View File

@ -56,6 +56,23 @@ namespace ZL {
return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; return v[0] * v[0] + v[1] * v[1] + v[2] * v[2];
} }
float length() const
{
return sqrt(squaredNorm());
}
float dot(const Vector3f& other) const {
return v[0] * other.v[0] + v[1] * other.v[1] + v[2] * other.v[2];
}
Vector3f cross(const Vector3f& other) const {
return Vector3f(
v[1] * other.v[2] - v[2] * other.v[1],
v[2] * other.v[0] - v[0] * other.v[2],
v[0] * other.v[1] - v[1] * other.v[0]
);
}
// Îïåðàòîð âû÷èòàíèÿ // Îïåðàòîð âû÷èòàíèÿ
/*Vector3f operator-(const Vector3f& other) const { /*Vector3f operator-(const Vector3f& other) const {
return Vector3f(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]); return Vector3f(v[0] - other.v[0], v[1] - other.v[1], v[2] - other.v[2]);
@ -67,6 +84,15 @@ namespace ZL {
struct Vector2f struct Vector2f
{ {
std::array<float, 2> v = {0.f, 0.f}; std::array<float, 2> v = {0.f, 0.f};
Vector2f()
{
}
Vector2f(float x, float y)
: v{ x,y }
{
}
}; };
Vector2f operator+(const Vector2f& x, const Vector2f& y); Vector2f operator+(const Vector2f& x, const Vector2f& y);

BIN
resources/button.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/musicVolumeBarButton.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/musicVolumeBarTexture.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/rock.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/sand.png (Stored with Git LFS) Normal file

Binary file not shown.

BIN
resources/spark.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,48 @@
// Фрагментный шейдер:
uniform vec3 uColor;
uniform float uDistanceToPlanetSurface; // Расстояние корабля до поверхности
// Константы затухания, определенные прямо в шейдере
const float DIST_FOG_MAX = 2000.0;
const float DIST_FOG_MIN = 1000.0;
varying vec3 vViewNormal;
varying vec3 vViewPosition;
void main()
{
// --- 1. Расчет плотности по Френелю (краевой эффект) ---
vec3 viewVector = normalize(-vViewPosition);
float NdotV = dot(vViewNormal, viewVector);
float factor = 1.0 - abs(NdotV);
float atmosphereDensity = pow(factor, 5.0);
// --- 2. Расчет коэффициента затухания по дистанции ---
float distanceFactor = 1.0;
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);
}
// --- 3. Финальный цвет и прозрачность ---
float finalAlpha = atmosphereDensity * distanceFactor;
gl_FragColor = vec4(uColor, finalAlpha);
}

View File

@ -0,0 +1,28 @@
// Вершинный шейдер:
attribute vec3 vPosition;
attribute vec3 vNormal; // <-- Новый атрибут
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
// Выходные переменные (передаются во фрагментный шейдер)
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);
}

View File

@ -1,11 +1,23 @@
attribute vec3 vPosition; attribute vec3 vPosition;
attribute vec3 vColor; attribute vec3 vColor;
attribute vec3 vNormal;
attribute vec2 vTexCoord; // <-- Обычные UV (если используются)
varying vec3 color; varying vec3 color;
varying vec3 normal;
varying vec2 TexCoord; // <-- Передаем UV
uniform mat4 ProjectionModelViewMatrix; uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform vec3 uLightDirection;
void main() void main()
{ {
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0); gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
normal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz);
color = vColor; color = vColor;
TexCoord = vTexCoord;
} }

View File

@ -1,9 +1,22 @@
//precision mediump float; varying vec3 color; // Цвет вершины (с шумом)
varying vec3 color; varying vec3 normal;
varying vec3 lightDirection_VS;
varying vec2 TexCoord; // UV-координаты
uniform sampler2D Texture;
uniform vec3 uLightDirection;
void main() void main()
{ {
//gl_FragColor = vec4(color, 1.0); // 1. Получаем цвет из текстуры и смешиваем с цветовым шумом
gl_FragColor = vec4(1.0, 1.0, 0.5, 1.0); vec4 textureColor = texture2D(Texture, TexCoord);
vec3 finalColor = textureColor.rgb * color;
// 3. Расчет освещения
float diffuse = max(0.0, dot(normal, uLightDirection));
float ambient = 0.2;
float lightingFactor = min(1.0, ambient + diffuse);
gl_FragColor = vec4(finalColor * lightingFactor, 1.0);
} }

View File

@ -0,0 +1,35 @@
// Вершинный шейдер (Vertex Shader)
attribute vec3 vPosition;
attribute vec3 vColor;
attribute vec3 vNormal;
attribute vec2 vTexCoord;
varying vec3 color;
varying vec3 normal;
varying vec2 TexCoord;
varying float viewZ; // <--- НОВОЕ: Z-координата в пространстве вида (View Space)
varying vec3 pos;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix; // Нужна для преобразования vPosition в View Space
uniform vec3 uLightDirection;
void main()
{
// Преобразование позиции в пространство вида (View Space)
vec4 viewPosition = ModelViewMatrix * vec4(vPosition.xyz, 1.0);
// Сохраняем отрицательную Z-координату. В OpenGL Z-координата (глубина)
// в пространстве вида обычно отрицательна, но для расчета тумана
// удобнее использовать положительное значение.
viewZ = -viewPosition.z;
pos = vPosition.xyz;
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
normal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz);
color = vColor;
TexCoord = vTexCoord;
}

View File

@ -0,0 +1,105 @@
// ---Фрагментный шейдер (Fragment Shader)
varying vec3 color;
varying vec3 normal;
varying vec2 TexCoord;
varying float viewZ;
varying vec3 pos;
uniform sampler2D Texture;
uniform vec3 uLightDirection;
uniform float uDistanceToPlanetSurface;
uniform float uCurrentZFar;
// Константы для тумана:
const vec4 FOG_COLOR = vec4(0.0, 0.5, 1.0, 1.0); // Синий туман
// Параметры "Distance Fog"
const float DIST_FOG_MAX = 2000.0;
const float DIST_FOG_MIN = 1000.0;
// Параметры "Z-Far Fog"
// Насколько близко к Zfar должен начинаться туман (например, 10% от Zfar)
const float Z_FOG_START_RATIO = 0.9;
// Какую долю от Zfar должен покрывать туман (например, 10% от Zfar)
const float Z_FOG_RANGE_RATIO = 0.1;
void main()
{
// ... (1. Получаем цвет и 2. Расчет освещения)
vec4 textureColor = texture2D(Texture, TexCoord);
vec3 finalColor = textureColor.rgb * color;
float diffuse = max(0.0, dot(normal, uLightDirection));
float ambient = 0.2;
float lightingFactor = min(1.0, ambient + diffuse);
vec3 litColor = finalColor * lightingFactor;
// 3. Расчет коэффициента тумана (fogFactor)
float fogFactor = 0.0;
// --- 3.1. Расчет коэффициента тумана на основе расстояния до поверхности (Distance Fog) ---
// Этот туман работает на больших расстояниях и постепенно исчезает,
// уступая место Z-Far Fog при приближении к планете.
if (uDistanceToPlanetSurface >= DIST_FOG_MIN && uDistanceToPlanetSurface <DIST_FOG_MAX) {
// Нормализация: 0.0f при DIST_FOG_MAX, 1.0f при DIST_FOG_MIN
float distRange = DIST_FOG_MAX - DIST_FOG_MIN;
float distNormalized = clamp((uDistanceToPlanetSurface - DIST_FOG_MIN) / distRange, 0.0, 1.0);
// alpha = 0.0 (Далеко) ... 1.0 (Близко к 1000.f)
fogFactor = 1.0 - distNormalized;
}
if (uDistanceToPlanetSurface < DIST_FOG_MIN)
{
fogFactor = 1.0;
}
// Если uDistanceToPlanetSurface < 1000.0f, то fogFactor = 0.0f (пока не пересчитан ниже),
// что позволяет Z-Far Fog взять контроль.
// --- 3.2. Расчет коэффициента тумана на основе Z-глубины (Z-Far Fog) ---
// Этот туман работает всегда, но его эффект усиливается при уменьшении uCurrentZFar,
// гарантируя, что все, что подходит к границе uCurrentZFar, будет скрыто.
// Точка начала тумана: 90% от uCurrentZFar
float farFogStart = 2000.f;//uCurrentZFar * 0.7;
// Длина перехода тумана: 10% от uCurrentZFar
float depthRange = 2000.f;//uCurrentZFar * 0.25;
if (abs(uDistanceToPlanetSurface) < 410.f)
{
farFogStart = 1800.f * abs(uDistanceToPlanetSurface-10.f) * 0.0025 + 200.f;
depthRange = farFogStart;
}
if (abs(uDistanceToPlanetSurface) < 10.f)
{
farFogStart = 200.f;
depthRange = 200.f;
}
float farFogFactor = 0.0;
if (depthRange > 0.0) {
// Нормализация Z-глубины: 0.0f при farFogStart, 1.0f при farFogStart + depthRange
farFogFactor = clamp((abs(viewZ) - farFogStart) / depthRange, 0.0, 1.0);
}
// --- 3.3. Объединение ---
// Когда мы далеко (fogFactor > 0.0), Distance Fog доминирует.
// Когда мы близко (fogFactor = 0.0), Z-Far Fog берет управление.
// Если оба активны, берем максимум (например, когда фрагмент далек от игрока, но игрок все еще в зоне Distance Fog).
//fogFactor = max(fogFactor, farFogFactor);
fogFactor = fogFactor * farFogFactor;
// 4. Смешивание цвета с туманом
//vec3 mountainColor = vec3((length(pos+vec3(0.0,0.0,45000.0))-20000.0)/100.0, 0.5,0.0);
gl_FragColor = mix(vec4(finalColor.rgb, 0.5), FOG_COLOR, fogFactor);
//gl_FragColor = vec4((length(pos+vec3(0.0,0.0,45000.0))-20000.0)/100.0, 0.0,0.0, 1.0);
//gl_FragColor = vec4(fogFactor, 0.5,0.5, 1.0);
}

View File

@ -5,8 +5,8 @@ varying vec2 texCoord;
void main() void main()
{ {
vec4 color = texture2D(Texture,texCoord).rgba; vec4 color = texture2D(Texture,texCoord).rgba;
gl_FragColor = vec4(color.rgb*0.9 + vec3(0.1, 0.1, 0.1), 1.0); //gl_FragColor = vec4(color.rgb*0.9 + vec3(0.1, 0.1, 0.1), 1.0);
//gl_FragColor = color; gl_FragColor = color;
} }

12
shaders/env_sky.vertex Normal file
View File

@ -0,0 +1,12 @@
attribute vec3 vPosition;
uniform mat4 ProjectionModelViewMatrix;
varying vec3 dir;
void main(){
vec4 realVertexPos = vec4(vPosition.xyz, 1.0);
gl_Position = ProjectionModelViewMatrix * realVertexPos;
dir = vPosition;
}

View File

@ -0,0 +1,11 @@
uniform samplerCube Texture;
uniform float skyPercent;
varying vec3 dir;
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;
}