Compare commits

...

13 Commits
spark ... main

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
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
25 changed files with 1276 additions and 41 deletions

View File

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

View File

@ -8,7 +8,7 @@ namespace ZL {
int Environment::windowHeaderHeight = 0;
int Environment::width = 0;
int Environment::height = 0;
float Environment::zoom = 6.f;
float Environment::zoom = 18.f;
bool Environment::leftPressed = false;
bool Environment::rightPressed = false;
@ -31,9 +31,13 @@ bool Environment::tapDownHold = false;
Vector2f Environment::tapDownStartPos = { 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;
const float Environment::CONST_Z_NEAR = 5.f;
const float Environment::CONST_Z_FAR = 5000.f;
} // namespace ZL

View File

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

228
Game.cpp
View File

@ -152,11 +152,13 @@ namespace ZL
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("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
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("env", "./shaders/env.vertex", "./shaders/env_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("defaultAtmosphere", "./shaders/defaultAtmosphere.vertex", "./shaders/defaultAtmosphere.fragment", CONST_ZIP_FILE);
#endif
@ -202,24 +204,87 @@ namespace ZL
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();
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 envShaderName = "env";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
static const std::string skyPercentUniformName = "skyPercent";
renderer.shaderManager.PushShader(envShaderName);
renderer.RenderUniform1i(textureUniformName, 0);
renderer.RenderUniform1f(skyPercentUniformName, skyPercent);
renderer.EnableVertexAttribArray(vPositionName);
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
1, 1000);
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.LoadIdentity();
renderer.RotateMatrix(Environment::inverseShipMatrix);
@ -255,7 +320,7 @@ namespace ZL
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
static_cast<float>(Environment::width) / static_cast<float>(Environment::height),
1, 1000);
Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
renderer.PushMatrix();
renderer.LoadIdentity();
@ -264,7 +329,6 @@ namespace ZL
glBindTexture(GL_TEXTURE_2D, spaceshipTexture->getTexID());
renderer.DrawVertexRenderStruct(spaceship);
sparkEmitter.draw(renderer, Environment::zoom, Environment::width, Environment::height);
renderer.PopMatrix();
@ -291,7 +355,7 @@ namespace ZL
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5,
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++)
{
@ -316,6 +380,77 @@ namespace ZL
renderer.shaderManager.PopShader();
CheckGlError();
}
void Game::UpdateVolumeKnob() {
float musicVolumeBarButtonButtonCenterY = volumeBarMinY + musicVolume * (volumeBarMaxY - volumeBarMinY);
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 envShaderName = "env";
static const std::string vPositionName = "vPosition";
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
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";
@ -324,16 +459,38 @@ namespace ZL
static const std::string vTexCoordName = "vTexCoord";
static const std::string textureUniformName = "Texture";
glClearColor(0.0f, 0.5f, 1.0f, 1.0f);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glViewport(0, 0, Environment::width, Environment::height);
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();
drawBoxes();
//drawBoxes();
drawUI();
CheckGlError();
}
@ -419,20 +576,43 @@ namespace ZL
}
else if (event.type == SDL_MOUSEBUTTONDOWN) {
// 1. Îáðàáîòêà íàæàòèÿ êíîïêè ìûøè
Environment::tapDownHold = true;
// Êîîðäèíàòû íà÷àëüíîãî íàæàòèÿ
Environment::tapDownStartPos.v[0] = event.button.x;
Environment::tapDownStartPos.v[1] = event.button.y;
// Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé
Environment::tapDownCurrentPos.v[0] = event.button.x;
Environment::tapDownCurrentPos.v[1] = event.button.y;
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::tapDownStartPos.v[0] = event.button.x;
Environment::tapDownStartPos.v[1] = event.button.y;
// Íà÷àëüíàÿ ïîçèöèÿ òàêæå ñòàíîâèòñÿ òåêóùåé
Environment::tapDownCurrentPos.v[0] = event.button.x;
Environment::tapDownCurrentPos.v[1] = event.button.y;
}
}
else if (event.type == SDL_MOUSEBUTTONUP) {
// 2. Îáðàáîòêà îòïóñêàíèÿ êíîïêè ìûøè
isDraggingVolume = false;
Environment::tapDownHold = false;
}
else if (event.type == SDL_MOUSEMOTION) {
// 3. Îáðàáîòêà ïåðåìåùåíèÿ ìûøè
int mx = event.motion.x;
int my = event.motion.y;
if (isDraggingVolume) {
// Äâèãàåì ìûøü ïî ñëàéäåðó — ìåíÿåì ãðîìêîñòü è ïîçèöèþ êðóæêà
UpdateVolumeFromMouse(mx, my);
}
if (Environment::tapDownHold) {
// Îáíîâëåíèå òåêóùåé ïîçèöèè, åñëè êíîïêà óäåðæèâàåòñÿ
Environment::tapDownCurrentPos.v[0] = event.motion.x;
@ -455,15 +635,23 @@ namespace ZL
{
if (event.key.keysym.sym == SDLK_i)
{
Environment::shipVelocity += 1.f;
Environment::shipVelocity += 500.f;
}
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();
}
} // namespace ZL
} // namespace ZL

36
Game.h
View File

@ -5,6 +5,7 @@
#include "Environment.h"
#include "TextureManager.h"
#include "SparkEmitter.h"
#include "PlanetObject.h"
namespace ZL {
@ -25,14 +26,16 @@ namespace ZL {
void update();
void render();
bool shouldExit() const { return Environment::exitGameLoop; }
private:
void processTickCount();
void drawScene();
void drawCubemap();
void drawCubemap(float skyPercent);
void drawShip();
void drawBoxes();
void drawUI();
SDL_Window* window;
SDL_GLContext glContext;
@ -41,6 +44,32 @@ namespace ZL {
size_t newTickCount;
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_MAX_TIME_INTERVAL = 1000;
@ -55,10 +84,9 @@ namespace ZL {
std::shared_ptr<Texture> boxTexture;
VertexDataStruct boxBase;
std::vector<BoxCoords> boxCoordsArr;
std::vector<VertexRenderStruct> boxRenderArr;
SparkEmitter sparkEmitter;
PlanetObject planetObject;
};
} // 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/
Windows x64 Installer: cmake-4.2.0-windows-x86_64.msi

View File

@ -294,6 +294,18 @@ namespace ZL {
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)
{
@ -459,6 +471,12 @@ namespace ZL {
return ProjectionModelViewMatrix;
}
Matrix4f Renderer::GetCurrentModelViewMatrix()
{
return ModelviewMatrixStack.top();
}
void Renderer::SetMatrix()
{
if (ProjectionMatrixStack.size() <= 0)
@ -471,15 +489,16 @@ namespace ZL {
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 ProjectionMatrixName = "ProjectionMatrix";
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)
@ -733,6 +764,7 @@ namespace ZL {
static const std::string vBinormal("vBinormal");
static const std::string vColor("vColor");
static const std::string vTexCoord("vTexCoord");
static const std::string vTexCoord3("vTexCoord3");
static const std::string vPosition("vPosition");
//glBindVertexArray(VertexRenderStruct.vao->getBuffer());
@ -763,6 +795,11 @@ namespace ZL {
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoordVBO->getBuffer());
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());
VertexAttribPointer3fv(vPosition, 0, NULL);

View File

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

View File

@ -2,6 +2,8 @@
#include <random>
#include <cmath>
#include "OpenGlExtensions.h"
#include "Environment.h"
namespace ZL {
@ -125,7 +127,7 @@ namespace ZL {
renderer.EnableVertexAttribArray(vTexCoordName);
float aspectRatio = static_cast<float>(screenWidth) / static_cast<float>(screenHeight);
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, 1, 1000);
renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, aspectRatio, Environment::CONST_Z_NEAR, Environment::CONST_Z_FAR);
glBindTexture(GL_TEXTURE_2D, texture->getTexID());

View File

@ -56,6 +56,23 @@ namespace ZL {
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 {
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
{
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);

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.

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 vColor;
attribute vec3 vNormal;
attribute vec2 vTexCoord; // <-- Обычные UV (если используются)
varying vec3 color;
varying vec3 normal;
varying vec2 TexCoord; // <-- Передаем UV
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix;
uniform vec3 uLightDirection;
void main()
void main()
{
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
color = vColor;
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
normal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz);
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-координаты
void main()
uniform sampler2D Texture;
uniform vec3 uLightDirection;
void main()
{
//gl_FragColor = vec4(color, 1.0);
gl_FragColor = vec4(1.0, 1.0, 0.5, 1.0);
// 1. Получаем цвет из текстуры и смешиваем с цветовым шумом
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);
}

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;
}