space-game001/PlanetObject.cpp
Vladislav Khorev af550f56a7 Cool working
2025-12-13 23:17:03 +03:00

604 lines
24 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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, -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 = 200.f;
static constexpr float MIDDLE_Z_FAR = 20000.f;
// Дистанция, где ЗАВЕРШАЕТСЯ переход FAR -> MIDDLE и НАЧИНАЕТСЯ MIDDLE -> NEAR
static constexpr float TRANSITION_MIDDLE_START = 500.f;
// --- 3. Ближний диапазон (NEAR) ---
// Новые константы для максимальной точности
static constexpr float NEAR_Z_NEAR = 5.0f;
static constexpr float NEAR_Z_FAR = 5000.0f;
// Дистанция, где ЗАВЕРШАЕТСЯ переход MIDDLE -> NEAR
static constexpr float TRANSITION_NEAR_END = 40.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 {
// Полностью ближний диапазон (distanceToPlanetSurface <= TRANSITION_NEAR_END)
currentZNear = NEAR_Z_NEAR;
currentZFar = 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 + (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 = CreateRect2D({ 0.f, 0.f }, { 1.f, 1.f }, 0.f);
planetAtmosphere.data.TexCoordData.clear();
planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 });
planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 });
planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 });
planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 });
planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 });
planetAtmosphere.data.ColorData.push_back({ 0,0.5,1 });
planetAtmosphere.data.Scale(1000.f);
//planetAtmosphere.data.Scale(PLANET_RADIUS*1.25f);
//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);
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();
//drawAtmosphere(renderer);
}
void PlanetObject::drawAtmosphere(Renderer& renderer) {
static const std::string defaultShaderName = "defaultAtmosphere";
static const std::string vPositionName = "vPosition";
static const std::string vColorName = "vColor";
glClear(GL_DEPTH_BUFFER_BIT);
renderer.shaderManager.PushShader(defaultShaderName);
renderer.EnableVertexAttribArray(vPositionName);
renderer.EnableVertexAttribArray(vColorName);
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);
Matrix4f projMatrix = renderer.GetProjectionModelViewMatrix();
Matrix4f modelViewMatrix = renderer.GetCurrentModelViewMatrix();
renderer.PopMatrix();
renderer.PopProjectionMatrix();
renderer.PushProjectionMatrix((Environment::width), static_cast<float>(Environment::height), -1, 1);
renderer.PushMatrix();
renderer.LoadIdentity();
// Преобразуем центр планеты в экранные координаты
int screenX = 0;
int screenY = 0;
worldToScreenCoordinates(
PLANET_CENTER_OFFSET,
projMatrix,
Environment::width,
Environment::height,
screenX,
screenY
);
// Позиция центра в пикселях
Vector3f centerPos = Vector3f(static_cast<float>(screenX), static_cast<float>(screenY), 0.0f);
renderer.TranslateMatrix(centerPos);
renderer.RenderUniform1f("uCenterRadius", 0.f);
renderer.RenderUniform3fv("uCenterPos", &centerPos.v[0]);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
renderer.DrawVertexRenderStruct(planetAtmosphere);
glDisable(GL_BLEND);
renderer.PopMatrix();
renderer.PopProjectionMatrix();
/*
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);
float centerRadius = PLANET_RADIUS*1.2f;
Vector3f centerPos = PLANET_CENTER_OFFSET;
renderer.RenderUniform1f("uCenterRadius", centerRadius);
renderer.RenderUniform3fv("uCenterPos", &centerPos.v[0]);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);// Аддитивное смешивание для эффекта свечения
renderer.DrawVertexRenderStruct(planetAtmosphere);
glDisable(GL_BLEND);
renderer.PopMatrix();
renderer.PopProjectionMatrix();*/
renderer.DisableVertexAttribArray(vColorName);
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