diff --git a/Environment.cpp b/Environment.cpp index 8d6479d..2ee0ed1 100644 --- a/Environment.cpp +++ b/Environment.cpp @@ -31,7 +31,7 @@ bool Environment::tapDownHold = false; Vector2f Environment::tapDownStartPos = { 0, 0 }; Vector2f Environment::tapDownCurrentPos = { 0, 0 }; -Vector3f Environment::shipPosition = {0,0,45000.f}; +Vector3f Environment::shipPosition = {100,100,45000.f}; float Environment::shipVelocity = 0.f; diff --git a/Game.cpp b/Game.cpp index dcc305c..658d41e 100755 --- a/Game.cpp +++ b/Game.cpp @@ -159,6 +159,7 @@ namespace ZL 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); + renderer.shaderManager.AddShaderFromFiles("defaultColor2", "./shaders/defaultColor_fog2.vertex", "./shaders/defaultColor_fog2_desktop.fragment", CONST_ZIP_FILE); #endif @@ -509,6 +510,7 @@ namespace ZL //gameObjects.updateScene(delta); sparkEmitter.update(static_cast(delta)); + planetObject.update(static_cast(delta)); if (Environment::tapDownHold) { diff --git a/PlanetObject.cpp b/PlanetObject.cpp index 22e368f..24a1aa5 100644 --- a/PlanetObject.cpp +++ b/PlanetObject.cpp @@ -210,12 +210,18 @@ namespace ZL { void PlanetObject::init() { - planetMesh = generateSphere(8); + planetMeshLods[0] = generateSphere(0); - planetMesh.Scale(PLANET_RADIUS); - planetMesh.Move(PLANET_CENTER_OFFSET); + planetMeshLods[0].Scale(PLANET_RADIUS); + planetMeshLods[0].Move(PLANET_CENTER_OFFSET); - planetRenderStruct.data = planetMesh; + planetMeshLods[1] = generateSphere(1); + + planetMeshLods[1].Scale(PLANET_RADIUS); + planetMeshLods[1].Move(PLANET_CENTER_OFFSET); + + + planetRenderStruct.data = planetMeshLods[1]; planetRenderStruct.RefreshVBO(); sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand.png", "")); @@ -239,6 +245,7 @@ namespace ZL { prepareDrawData(); static const std::string defaultShaderName = "defaultColor"; + static const std::string defaultShaderName2 = "defaultColor2"; static const std::string vPositionName = "vPosition"; static const std::string vColorName = "vColor"; static const std::string vNormalName = "vNormal"; @@ -311,7 +318,43 @@ namespace ZL { renderer.shaderManager.PopShader(); CheckGlError(); - drawAtmosphere(renderer); + glClear(GL_DEPTH_BUFFER_BIT); + + //-------------------------- + + if (planetRenderRedStruct.data.PositionData.size() > 0) + { + + renderer.shaderManager.PushShader(defaultShaderName2); + //renderer.RenderUniform1i(textureUniformName, 0); + renderer.EnableVertexAttribArray(vPositionName); + + + // 2. Применяем динамическую матрицу проекции + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + currentZNear, currentZFar); + + renderer.PushMatrix(); + renderer.LoadIdentity(); + renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); + renderer.RotateMatrix(Environment::inverseShipMatrix); + renderer.TranslateMatrix(-Environment::shipPosition); + + renderer.DrawVertexRenderStruct(planetRenderRedStruct); + //glDisable(GL_BLEND); + CheckGlError(); + + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + renderer.DisableVertexAttribArray(vPositionName); + renderer.shaderManager.PopShader(); + CheckGlError(); + + } + + //drawAtmosphere(renderer); } void PlanetObject::drawAtmosphere(Renderer& renderer) { @@ -371,6 +414,16 @@ namespace ZL { void PlanetObject::update(float deltaTimeMs) { + auto lr = triangleUnderCamera(currentLod); + planetRenderRedStruct.data.PositionData.clear(); + for (int i : lr) + { + planetRenderRedStruct.data.PositionData.push_back(planetMeshLods[currentLod].PositionData[i * 3]); + planetRenderRedStruct.data.PositionData.push_back(planetMeshLods[currentLod].PositionData[i * 3+1]); + planetRenderRedStruct.data.PositionData.push_back(planetMeshLods[currentLod].PositionData[i * 3+2]); + } + + planetRenderRedStruct.RefreshVBO(); } std::vector PlanetObject::subdivideTriangles(const std::vector& inputTriangles) { @@ -577,6 +630,163 @@ namespace ZL { return buffer; } + + float check_spherical_side(const Vector3f& V1, const Vector3f& V2, const Vector3f& P, const Vector3f& V3, float epsilon = 1e-6f) { + // Нормаль к плоскости, проходящей через O, V1 и V2 + Vector3f N_plane = V1.cross(V2); + + // Расстояние P до плоскости V1V2 (со знаком) + float sign_P = P.dot(N_plane); + + // Расстояние V3 до плоскости V1V2 (со знаком) + float sign_V3 = V3.dot(N_plane); + + // Если знаки совпадают, P находится на той же стороне, что и V3. + // Возвращаем произведение знаков. + return sign_P * sign_V3; + } + + /** + * @brief Проверяет, находится ли точка P внутри сферического треугольника (V1, V2, V3). + * @param P Проверяемая точка (нормализованная). + * @param V1, V2, V3 Нормализованные вершины треугольника. + * @return true, если внутри или на границе. + */ + bool is_inside_spherical_triangle(const Vector3f& P, const Vector3f& V1, const Vector3f& V2, const Vector3f& V3, float epsilon) { + // Точка P должна быть на одной стороне от всех трех граней относительно третьей вершины. + + // 1. Проверка относительно V1V2 (эталон V3) + if (check_spherical_side(V1, V2, P, V3, epsilon) < -epsilon) return false; + + // 2. Проверка относительно V2V3 (эталон V1) + if (check_spherical_side(V2, V3, P, V1, epsilon) < -epsilon) return false; + + // 3. Проверка относительно V3V1 (эталон V2) + if (check_spherical_side(V3, V1, P, V2, epsilon) < -epsilon) return false; + + return true; + } + + + std::vector find_sub_triangle_spherical(const Vector3f& a_orig, const Vector3f& b_orig, const Vector3f& c_orig, const Vector3f& px_orig) { + const float EPSILON = 1e-6f; + std::vector result; + + // 1. Нормализация всех вершин и проверяемой точки для работы на сфере (радиус 1) + const Vector3f a = a_orig.normalized(); + const Vector3f b = b_orig.normalized(); + const Vector3f c = c_orig.normalized(); + + // Точка pxx должна быть спроецирована на сферу. + const Vector3f pxx_normalized = px_orig.normalized(); + + // 2. Вычисляем нормализованные середины (вершины внутренних треугольников) + const Vector3f m_ab = ((a + b) * 0.5f).normalized(); + const Vector3f m_bc = ((b + c) * 0.5f).normalized(); + const Vector3f m_ac = ((a + c) * 0.5f).normalized(); + + // 3. Определяем 4 подтреугольника + + // Delta_0: (a, m_ab, m_ac) + if (is_inside_spherical_triangle(pxx_normalized, a, m_ab, m_ac, EPSILON)) { + result.push_back(0); + } + + // Delta_1: (m_ab, b, m_bc) + if (is_inside_spherical_triangle(pxx_normalized, m_ab, b, m_bc, EPSILON)) { + result.push_back(1); + } + + // Delta_2: (m_ac, m_bc, c) + if (is_inside_spherical_triangle(pxx_normalized, m_ac, m_bc, c, EPSILON)) { + result.push_back(2); + } + + // Delta_3: (m_ab, m_bc, m_ac) - Внутренний + if (is_inside_spherical_triangle(pxx_normalized, m_ab, m_bc, m_ac, EPSILON)) { + result.push_back(3); + } + + // Если исходный pxx точно лежит на луче из O(0,0,0) и внутри исходного треугольника, + // то pxx_normalized гарантированно попадет в один или несколько (на границе) + // из 4х подтреугольников. + + return result; + } + + std::vector PlanetObject::triangleUnderCamera(int lod) + { + std::vector r; + if (lod == 0) + { + if (Environment::shipPosition.v[1] >= 0) + { + if (Environment::shipPosition.v[0] >= 0 && Environment::shipPosition.v[2] >= 0) + { + r.push_back(0); + } + if (Environment::shipPosition.v[0] >= 0 && Environment::shipPosition.v[2] <= 0) + { + r.push_back(1); + } + if (Environment::shipPosition.v[0] <= 0 && Environment::shipPosition.v[2] <= 0) + { + r.push_back(2); + } + if (Environment::shipPosition.v[0] <= 0 && Environment::shipPosition.v[2] >= 0) + { + r.push_back(3); + } + } + if (Environment::shipPosition.v[1] <= 0) + { + if (Environment::shipPosition.v[0] >= 0 && Environment::shipPosition.v[2] >= 0) + { + r.push_back(4); + } + if (Environment::shipPosition.v[0] <= 0 && Environment::shipPosition.v[2] >= 0) + { + r.push_back(5); + } + if (Environment::shipPosition.v[0] <= 0 && Environment::shipPosition.v[2] <= 0) + { + r.push_back(6); + } + if (Environment::shipPosition.v[0] >= 0 && Environment::shipPosition.v[2] <= 0) + { + r.push_back(7); + } + + } + } + else if (lod == 1) + { + std::vector r0 = triangleUnderCamera(0); + + for (int tri0 : r0) + { + Vector3f a = planetMeshLods[0].PositionData[tri0 * 3]; + Vector3f b = planetMeshLods[0].PositionData[tri0 * 3+1]; + Vector3f c = planetMeshLods[0].PositionData[tri0 * 3+2]; + + std::vector result = find_sub_triangle_spherical(a, b, c, Environment::shipPosition); + + if (result.size() == 0) + { + std::cout << "???" << std::endl; + } + + for (int trix : result) + { + r.push_back(tri0 * 4 + trix); + } + + } + } + + return r; + } + } // namespace ZL \ No newline at end of file diff --git a/PlanetObject.h b/PlanetObject.h index 2c67bfe..5be0718 100644 --- a/PlanetObject.h +++ b/PlanetObject.h @@ -45,8 +45,11 @@ namespace ZL { PerlinNoise perlin; PerlinNoise colorPerlin; void prepareDrawData(); - VertexDataStruct planetMesh; + std::array< VertexDataStruct, 2> planetMeshLods; + //VertexDataStruct planetMeshLod0; + //VertexDataStruct planetMeshLod1; VertexRenderStruct planetRenderStruct; + VertexRenderStruct planetRenderRedStruct; VertexRenderStruct planetAtmosphere; @@ -70,10 +73,14 @@ namespace ZL { private: bool drawDataDirty = true; + int currentLod = 1; + std::vector subdivideTriangles(const std::vector& inputTriangles); Vector3f calculateSurfaceNormal(Vector3f p_sphere); VertexDataStruct trianglesToVertices(const std::vector& triangles); VertexDataStruct generateSphere(int subdivisions); + + std::vector triangleUnderCamera(int lod); }; } // namespace ZL \ No newline at end of file diff --git a/ZLMath.h b/ZLMath.h index 23b3017..42d9629 100755 --- a/ZLMath.h +++ b/ZLMath.h @@ -13,6 +13,9 @@ namespace ZL { Vector4f normalized() const { double norm = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2] + v[3] * v[3]); + if (norm <= 0.000001f) { + return { 0,0, 0, 0}; + } Vector4f r; r.v[0] = v[0] / norm; @@ -43,6 +46,9 @@ namespace ZL { Vector3f normalized() const { double norm = std::sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); + if (norm <= 0.000001f) { + return { 0,0,0 }; + } Vector3f r; r.v[0] = v[0] / norm; diff --git a/shaders/defaultColor_fog2.vertex b/shaders/defaultColor_fog2.vertex new file mode 100644 index 0000000..d34873f --- /dev/null +++ b/shaders/defaultColor_fog2.vertex @@ -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; +} \ No newline at end of file diff --git a/shaders/defaultColor_fog2_desktop.fragment b/shaders/defaultColor_fog2_desktop.fragment new file mode 100644 index 0000000..226583d --- /dev/null +++ b/shaders/defaultColor_fog2_desktop.fragment @@ -0,0 +1,106 @@ +// ---Фрагментный шейдер (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 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); + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); +} \ No newline at end of file