From fe361885c0f39e89bc5e4614a195fb4d19a54c66 Mon Sep 17 00:00:00 2001 From: Vladislav Khorev Date: Sat, 13 Dec 2025 15:02:03 +0300 Subject: [PATCH] Working on planet --- Game.cpp | 4 +- PlanetObject.cpp | 408 ++++++++++++++++---------- PlanetObject.h | 79 ++--- Renderer.cpp | 33 ++- Renderer.h | 3 + ZLMath.h | 9 + resources/rock.png | 3 + resources/sand.png | 3 + shaders/defaultColor.vertex | 18 +- shaders/defaultColor_desktop.fragment | 25 +- 10 files changed, 358 insertions(+), 227 deletions(-) create mode 100644 resources/rock.png create mode 100644 resources/sand.png diff --git a/Game.cpp b/Game.cpp index 38f04d3..7d4365b 100755 --- a/Game.cpp +++ b/Game.cpp @@ -612,11 +612,11 @@ namespace ZL { if (event.key.keysym.sym == SDLK_i) { - Environment::shipVelocity += 1.f; + Environment::shipVelocity += 5.f; } if (event.key.keysym.sym == SDLK_k) { - Environment::shipVelocity -= 1.f; + Environment::shipVelocity -= 5.f; } } } diff --git a/PlanetObject.cpp b/PlanetObject.cpp index b428f3f..1fddf74 100644 --- a/PlanetObject.cpp +++ b/PlanetObject.cpp @@ -6,17 +6,174 @@ namespace ZL { - struct Triangle + + 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 + (noiseValue * 0.1f); // * 0.2 даст вариацию высоты + + return height; + } + + + + + PlanetObject::PlanetObject() + : perlin(77777) + , colorPerlin(123123) { - std::array data; - Triangle(Vector3f p1, Vector3f p2, Vector3f p3) - : data{ p1, p2, p3 } - { - } - }; + } - std::vector subdivideTriangles(const std::vector& inputTriangles, PerlinNoise& perlin) { + void PlanetObject::init() { + + planetMesh = generateSphere(7); + + planetMesh.Scale(300.f); + planetMesh.Move({ 0,0,-600 }); + + planetRenderStruct.data = planetMesh; + planetRenderStruct.RefreshVBO(); + + //sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/sand.png", "")); + sandTexture = std::make_unique(CreateTextureDataFromPng("./resources/rock.png", "")); + + } + + 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); + renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, + static_cast(Environment::width) / static_cast(Environment::height), + 1, 1000); + 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]); + + glBindTexture(GL_TEXTURE_2D, sandTexture->getTexID()); + renderer.DrawVertexRenderStruct(planetRenderStruct); + + CheckGlError(); + + + renderer.PopMatrix(); + renderer.PopProjectionMatrix(); + //renderer.DisableVertexAttribArray(vTexCoord3Name); + renderer.DisableVertexAttribArray(vTexCoordName); + renderer.DisableVertexAttribArray(vNormalName); + renderer.DisableVertexAttribArray(vColorName); + renderer.DisableVertexAttribArray(vPositionName); + renderer.shaderManager.PopShader(); + CheckGlError(); + + + } + + void PlanetObject::update(float deltaTimeMs) { + + } + + std::vector PlanetObject::subdivideTriangles(const std::vector& inputTriangles) { std::vector output; output.reserve(inputTriangles.size() * 4); @@ -50,7 +207,7 @@ namespace ZL { return output; } - Vector3f calculateSurfaceNormal(Vector3f p_sphere, PerlinNoise& perlin) { + Vector3f PlanetObject::calculateSurfaceNormal(Vector3f p_sphere) { // p_sphere - это нормализованный вектор (точка на идеальной сфере) float theta = 0.01f; // Шаг для "щупанья" соседей (epsilon) @@ -86,11 +243,27 @@ namespace ZL { // здесь подбираем так, чтобы нормаль смотрела наружу. return (-v2.cross(v1)).normalized(); } - - VertexDataStruct trianglesToVertices(const std::vector& triangles, PerlinNoise& perlin) { + + VertexDataStruct PlanetObject::trianglesToVertices(const std::vector& 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 triangleUVs = { + Vector2f(0.0f, 0.0f), + Vector2f(1.0f, 0.0f), + Vector2f(0.0f, 1.0f) + }; + /* + const std::array 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 вершинам треугольника @@ -104,187 +277,104 @@ namespace ZL { Vector3f p_dir = p_geometry.normalized(); // Считаем аналитическую нормаль для этой конкретной точки - Vector3f normal = calculateSurfaceNormal(p_dir, perlin); + Vector3f normal = calculateSurfaceNormal(p_dir); buffer.PositionData.push_back({ p_geometry }); - //buffer.NormalData.push_back({ normal }); - //buffer.TexCoordData.push_back({ 0.0f, 0.0f }); + buffer.NormalData.push_back({ normal }); + //buffer.TexCoord3Data.push_back(barycentricCoords[i]); + buffer.TexCoordData.push_back(triangleUVs[i]); + } } return buffer; } - // Генерация геометрии октаэдра с дублированием вершин для Flat Shading - VertexDataStruct generateOctahedron(PerlinNoise& perlin) { - VertexDataStruct buffer; - std::vector v = { - Triangle{ - { 0.0f, 1.0f, 0.0f}, // Top - { 0.0f, 0.0f, 1.0f}, // Front - { 1.0f, 0.0f, 0.0f}, // Right - }, - Triangle{ - { 0.0f, 1.0f, 0.0f}, // Top - { 1.0f, 0.0f, 0.0f}, // Right - { 0.0f, 0.0f, -1.0f}, // Back - }, - Triangle{ - { 0.0f, 1.0f, 0.0f}, // Top - { 0.0f, 0.0f, -1.0f}, // Back - {-1.0f, 0.0f, 0.0f}, // Left - }, - Triangle{ - { 0.0f, 1.0f, 0.0f}, // Top - {-1.0f, 0.0f, 0.0f}, // Left - { 0.0f, 0.0f, 1.0f}, // Front - }, - Triangle{ - { 0.0f, -1.0f, 0.0f}, // Bottom - { 1.0f, 0.0f, 0.0f}, // Right - { 0.0f, 0.0f, 1.0f}, // Front - }, - Triangle{ - { 0.0f, -1.0f, 0.0f}, // Bottom - { 0.0f, 0.0f, 1.0f}, // Front - {-1.0f, 0.0f, 0.0f}, // Left - }, - Triangle{ - { 0.0f, -1.0f, 0.0f}, // Bottom - {-1.0f, 0.0f, 0.0f}, // Left - { 0.0f, 0.0f, -1.0f}, // Back - }, - Triangle{ - { 0.0f, -1.0f, 0.0f}, // Bottom - { 0.0f, 0.0f, -1.0f}, // Back - { 1.0f, 0.0f, 0.0f}, // Right - } - }; - - v = subdivideTriangles(v, perlin); - v = subdivideTriangles(v, perlin); - v = subdivideTriangles(v, perlin); - v = subdivideTriangles(v, perlin); - v = subdivideTriangles(v, perlin); - - - for (int i = 0; i < v.size(); i++) { - Vector3f p1 = v[i].data[0]; - Vector3f p2 = v[i].data[1]; - Vector3f p3 = v[i].data[2]; - - // Считаем нормаль грани через векторное произведение - Vector3f edge1 = p2 - p1; - Vector3f edge2 = p3 - p1; - Vector3f normal = (edge1.cross(edge2)).normalized(); - - // Дублируем нормаль для всех трех вершин треугольника (Flat shading) - buffer.PositionData.push_back({ p1 }); - buffer.PositionData.push_back({ p2 }); - buffer.PositionData.push_back({ p3 }); - /*buffer.NormalData.push_back({normal}); - buffer.NormalData.push_back({ normal }); - buffer.NormalData.push_back({ normal }); - buffer.TexCoordData.push_back({ 0.0f, 0.0f }); - buffer.TexCoordData.push_back({ 0.0f, 0.0f }); - buffer.TexCoordData.push_back({ 0.0f, 0.0f });*/ - } - return buffer; - } - - VertexDataStruct generateSphere(int subdivisions, PerlinNoise& perlin) { + VertexDataStruct PlanetObject::generateSphere(int subdivisions) { // 1. Исходный октаэдр std::vector 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 + 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 раз (новые вершины будут получать шум внутри функции) + // 3. Разбиваем N раз for (int i = 0; i < subdivisions; i++) { - geometry = subdivideTriangles(geometry, perlin); + geometry = subdivideTriangles(geometry); } - // 4. Генерируем нормали - // Благодаря тому, что мы реально сдвигали вершины, Flat Shading нормали - // рассчитаются правильно относительно нового рельефа. - return trianglesToVertices(geometry, perlin); - } + // 4. Генерируем вершины И НОРМАЛИ с помощью trianglesToVertices + // ЭТО ЗАПОЛНИТ PositionData И NormalData + VertexDataStruct buffer = trianglesToVertices(geometry); - PlanetObject::PlanetObject() - { - } + // Теперь нам нужно заполнить ColorData, используя ту же логику, что и раньше. + // Сначала резервируем место, так как trianglesToVertices не заполняет ColorData + buffer.ColorData.reserve(geometry.size() * 3); - void PlanetObject::init() { - - planetMesh = generateSphere(7, perlin); - - planetMesh.Scale(100.f); - planetMesh.Move({ 0,0,-200 }); - - planetRenderStruct.data = planetMesh; - planetRenderStruct.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"; - - renderer.shaderManager.PushShader(defaultShaderName); - renderer.EnableVertexAttribArray(vPositionName); - renderer.PushPerspectiveProjectionMatrix(1.0 / 1.5, - static_cast(Environment::width) / static_cast(Environment::height), - 1, 1000); - renderer.PushMatrix(); - renderer.LoadIdentity(); - renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom }); - renderer.RotateMatrix(Environment::inverseShipMatrix); - renderer.TranslateMatrix(-Environment::shipPosition); + // Базовый цвет и параметры + 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 }; - renderer.DrawVertexRenderStruct(planetRenderStruct); + 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(); - CheckGlError(); + // Вычисление цветового шума (как вы делали) + 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 + }; - renderer.PopMatrix(); - renderer.PopProjectionMatrix(); - renderer.DisableVertexAttribArray(vPositionName); + Vector3f finalColor = baseColor + colorOffset; - renderer.shaderManager.PopShader(); - CheckGlError(); + 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); + } + } - } - - void PlanetObject::update(float deltaTimeMs) { - + return buffer; } diff --git a/PlanetObject.h b/PlanetObject.h index c7857a8..5f4741b 100644 --- a/PlanetObject.h +++ b/PlanetObject.h @@ -14,75 +14,42 @@ namespace ZL { + struct Triangle + { + std::array data; + + Triangle(Vector3f p1, Vector3f p2, Vector3f p3) + : data{ p1, p2, p3 } + { + } + }; + class PerlinNoise { std::vector p; public: - PerlinNoise() { - p.resize(256); - std::iota(p.begin(), p.end(), 0); - // Перемешиваем для случайности (можно задать seed) - std::default_random_engine engine(12345); - std::shuffle(p.begin(), p.end(), engine); - p.insert(p.end(), p.begin(), p.end()); // Дублируем для переполнения - } + PerlinNoise(); + PerlinNoise(uint64_t seed); - float fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); } - float lerp(float t, float a, float b) { return a + t * (b - a); } - float 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 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) { - int X = (int)floor(x) & 255; - int Y = (int)floor(y) & 255; - int Z = (int)floor(z) & 255; + float noise(float x, float y, float z); - 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 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 + (noiseValue * 0.1f); // * 0.2 даст вариацию высоты - - return height; - } + float getSurfaceHeight(Vector3f pos); }; class PlanetObject { private: PerlinNoise perlin; + PerlinNoise colorPerlin; void prepareDrawData(); VertexDataStruct planetMesh; VertexRenderStruct planetRenderStruct; + std::shared_ptr sandTexture; + public: PlanetObject(); @@ -95,6 +62,12 @@ namespace ZL { private: bool drawDataDirty = true; + + std::vector subdivideTriangles(const std::vector& inputTriangles); + Vector3f calculateSurfaceNormal(Vector3f p_sphere); + VertexDataStruct trianglesToVertices(const std::vector& triangles); + VertexDataStruct generateOctahedron(); + VertexDataStruct generateSphere(int subdivisions); }; } // namespace ZL \ No newline at end of file diff --git a/Renderer.cpp b/Renderer.cpp index e39c946..fd10526 100755 --- a/Renderer.cpp +++ b/Renderer.cpp @@ -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(); + } + + 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]); } @@ -733,6 +752,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 +783,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); diff --git a/Renderer.h b/Renderer.h index f3d0e54..39eca83 100755 --- a/Renderer.h +++ b/Renderer.h @@ -44,6 +44,7 @@ namespace ZL { { std::vector PositionData; std::vector TexCoordData; + std::vector TexCoord3Data; std::vector NormalData; std::vector TangentData; std::vector BinormalData; @@ -63,6 +64,7 @@ namespace ZL { std::shared_ptr vao; std::shared_ptr positionVBO; std::shared_ptr texCoordVBO; + std::shared_ptr texCoord3VBO; std::shared_ptr normalVBO; std::shared_ptr tangentVBO; std::shared_ptr binormalVBO; @@ -108,6 +110,7 @@ namespace ZL { Matrix4f GetProjectionModelViewMatrix(); + Matrix4f GetCurrentModelViewMatrix(); void SetMatrix(); diff --git a/ZLMath.h b/ZLMath.h index d8168df..e1e5dd6 100755 --- a/ZLMath.h +++ b/ZLMath.h @@ -79,6 +79,15 @@ namespace ZL { struct Vector2f { std::array v = {0.f, 0.f}; + + Vector2f() + { + } + + Vector2f(float x, float y) + : v{ x,y } + { + } }; Vector2f operator+(const Vector2f& x, const Vector2f& y); diff --git a/resources/rock.png b/resources/rock.png new file mode 100644 index 0000000..536f006 --- /dev/null +++ b/resources/rock.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:26617e84a76c46d1bf5af664e89fa05cc93a2d17a691779d6ba5ef4ac4c1366f +size 638291 diff --git a/resources/sand.png b/resources/sand.png new file mode 100644 index 0000000..3224ac7 --- /dev/null +++ b/resources/sand.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:caec35b4ea51d32f361b73419d101485a10fd82e3643ea5d4fc63da25e5457b6 +size 160927 diff --git a/shaders/defaultColor.vertex b/shaders/defaultColor.vertex index 5155c3e..7e9da68 100644 --- a/shaders/defaultColor.vertex +++ b/shaders/defaultColor.vertex @@ -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; } \ No newline at end of file diff --git a/shaders/defaultColor_desktop.fragment b/shaders/defaultColor_desktop.fragment index 3f3571a..f0aeb97 100644 --- a/shaders/defaultColor_desktop.fragment +++ b/shaders/defaultColor_desktop.fragment @@ -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); + } \ No newline at end of file