Working on planet
This commit is contained in:
parent
3887208b8d
commit
fe361885c0
4
Game.cpp
4
Game.cpp
@ -612,11 +612,11 @@ namespace ZL
|
|||||||
{
|
{
|
||||||
if (event.key.keysym.sym == SDLK_i)
|
if (event.key.keysym.sym == SDLK_i)
|
||||||
{
|
{
|
||||||
Environment::shipVelocity += 1.f;
|
Environment::shipVelocity += 5.f;
|
||||||
}
|
}
|
||||||
if (event.key.keysym.sym == SDLK_k)
|
if (event.key.keysym.sym == SDLK_k)
|
||||||
{
|
{
|
||||||
Environment::shipVelocity -= 1.f;
|
Environment::shipVelocity -= 5.f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
406
PlanetObject.cpp
406
PlanetObject.cpp
@ -6,17 +6,174 @@
|
|||||||
|
|
||||||
namespace ZL {
|
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<Vector3f, 3> data;
|
|
||||||
|
|
||||||
Triangle(Vector3f p1, Vector3f p2, Vector3f p3)
|
}
|
||||||
: data{ p1, p2, p3 }
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Triangle> subdivideTriangles(const std::vector<Triangle>& 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<Texture>(CreateTextureDataFromPng("./resources/sand.png", ""));
|
||||||
|
sandTexture = std::make_unique<Texture>(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<float>(Environment::width) / static_cast<float>(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<Triangle> PlanetObject::subdivideTriangles(const std::vector<Triangle>& inputTriangles) {
|
||||||
std::vector<Triangle> output;
|
std::vector<Triangle> output;
|
||||||
output.reserve(inputTriangles.size() * 4);
|
output.reserve(inputTriangles.size() * 4);
|
||||||
|
|
||||||
@ -50,7 +207,7 @@ namespace ZL {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3f calculateSurfaceNormal(Vector3f p_sphere, PerlinNoise& perlin) {
|
Vector3f PlanetObject::calculateSurfaceNormal(Vector3f p_sphere) {
|
||||||
// p_sphere - это нормализованный вектор (точка на идеальной сфере)
|
// p_sphere - это нормализованный вектор (точка на идеальной сфере)
|
||||||
|
|
||||||
float theta = 0.01f; // Шаг для "щупанья" соседей (epsilon)
|
float theta = 0.01f; // Шаг для "щупанья" соседей (epsilon)
|
||||||
@ -87,10 +244,26 @@ namespace ZL {
|
|||||||
return (-v2.cross(v1)).normalized();
|
return (-v2.cross(v1)).normalized();
|
||||||
}
|
}
|
||||||
|
|
||||||
VertexDataStruct trianglesToVertices(const std::vector<Triangle>& triangles, PerlinNoise& perlin) {
|
VertexDataStruct PlanetObject::trianglesToVertices(const std::vector<Triangle>& triangles) {
|
||||||
VertexDataStruct buffer;
|
VertexDataStruct buffer;
|
||||||
buffer.PositionData.reserve(triangles.size() * 3);
|
buffer.PositionData.reserve(triangles.size() * 3);
|
||||||
buffer.NormalData.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) {
|
for (const auto& t : triangles) {
|
||||||
// Проходим по всем 3 вершинам треугольника
|
// Проходим по всем 3 вершинам треугольника
|
||||||
@ -104,187 +277,104 @@ namespace ZL {
|
|||||||
Vector3f p_dir = p_geometry.normalized();
|
Vector3f p_dir = p_geometry.normalized();
|
||||||
|
|
||||||
// Считаем аналитическую нормаль для этой конкретной точки
|
// Считаем аналитическую нормаль для этой конкретной точки
|
||||||
Vector3f normal = calculateSurfaceNormal(p_dir, perlin);
|
Vector3f normal = calculateSurfaceNormal(p_dir);
|
||||||
|
|
||||||
buffer.PositionData.push_back({ p_geometry });
|
buffer.PositionData.push_back({ p_geometry });
|
||||||
//buffer.NormalData.push_back({ normal });
|
buffer.NormalData.push_back({ normal });
|
||||||
//buffer.TexCoordData.push_back({ 0.0f, 0.0f });
|
//buffer.TexCoord3Data.push_back(barycentricCoords[i]);
|
||||||
|
buffer.TexCoordData.push_back(triangleUVs[i]);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Генерация геометрии октаэдра с дублированием вершин для Flat Shading
|
|
||||||
VertexDataStruct generateOctahedron(PerlinNoise& perlin) {
|
|
||||||
VertexDataStruct buffer;
|
|
||||||
|
|
||||||
std::vector<Triangle> v = {
|
VertexDataStruct PlanetObject::generateSphere(int subdivisions) {
|
||||||
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) {
|
|
||||||
// 1. Исходный октаэдр
|
// 1. Исходный октаэдр
|
||||||
std::vector<Triangle> geometry = {
|
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}, { 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}, { 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}, { 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}}, // 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}, { 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}, { 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}, {-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}} // Bottom-Back-Right
|
||||||
};
|
};
|
||||||
|
|
||||||
// 2. ПРИМЕНЯЕМ ШУМ К ИСХОДНЫМ ВЕРШИНАМ
|
// 2. ПРИМЕНЯЕМ ШУМ К ИСХОДНЫМ ВЕРШИНАМ
|
||||||
// Проходимся по всем треугольникам и всем вершинам в них
|
|
||||||
for (auto& t : geometry) {
|
for (auto& t : geometry) {
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
// Нормализуем (на всякий случай, хотя у октаэдра они и так норм)
|
|
||||||
Vector3f dir = t.data[i].normalized();
|
Vector3f dir = t.data[i].normalized();
|
||||||
// Применяем высоту
|
|
||||||
t.data[i] = dir * perlin.getSurfaceHeight(dir);
|
t.data[i] = dir * perlin.getSurfaceHeight(dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Разбиваем N раз (новые вершины будут получать шум внутри функции)
|
// 3. Разбиваем N раз
|
||||||
for (int i = 0; i < subdivisions; i++) {
|
for (int i = 0; i < subdivisions; i++) {
|
||||||
geometry = subdivideTriangles(geometry, perlin);
|
geometry = subdivideTriangles(geometry);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. Генерируем нормали
|
// 4. Генерируем вершины И НОРМАЛИ с помощью trianglesToVertices
|
||||||
// Благодаря тому, что мы реально сдвигали вершины, Flat Shading нормали
|
// ЭТО ЗАПОЛНИТ PositionData И NormalData
|
||||||
// рассчитаются правильно относительно нового рельефа.
|
VertexDataStruct buffer = trianglesToVertices(geometry);
|
||||||
return trianglesToVertices(geometry, perlin);
|
|
||||||
}
|
|
||||||
|
|
||||||
PlanetObject::PlanetObject()
|
// Теперь нам нужно заполнить ColorData, используя ту же логику, что и раньше.
|
||||||
{
|
// Сначала резервируем место, так как trianglesToVertices не заполняет ColorData
|
||||||
}
|
buffer.ColorData.reserve(geometry.size() * 3);
|
||||||
|
|
||||||
void PlanetObject::init() {
|
// Базовый цвет и параметры
|
||||||
|
const Vector3f baseColor = { 0.498f, 0.416f, 0.0f };
|
||||||
planetMesh = generateSphere(7, perlin);
|
const float colorFrequency = 5.0f;
|
||||||
|
const float colorAmplitude = 0.2f;
|
||||||
planetMesh.Scale(100.f);
|
const Vector3f offsetR = { 0.1f, 0.2f, 0.3f };
|
||||||
planetMesh.Move({ 0,0,-200 });
|
const Vector3f offsetG = { 0.5f, 0.4f, 0.6f };
|
||||||
|
const Vector3f offsetB = { 0.9f, 0.8f, 0.7f };
|
||||||
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<float>(Environment::width) / static_cast<float>(Environment::height),
|
|
||||||
1, 1000);
|
|
||||||
renderer.PushMatrix();
|
|
||||||
renderer.LoadIdentity();
|
|
||||||
renderer.TranslateMatrix({ 0,0, -1.0f * Environment::zoom });
|
|
||||||
renderer.RotateMatrix(Environment::inverseShipMatrix);
|
|
||||||
renderer.TranslateMatrix(-Environment::shipPosition);
|
|
||||||
|
|
||||||
|
|
||||||
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();
|
Vector3f finalColor = baseColor + colorOffset;
|
||||||
renderer.PopProjectionMatrix();
|
|
||||||
renderer.DisableVertexAttribArray(vPositionName);
|
|
||||||
|
|
||||||
renderer.shaderManager.PopShader();
|
finalColor.v[0] = max(0.0f, min(1.0f, finalColor.v[0]));
|
||||||
CheckGlError();
|
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;
|
||||||
|
|
||||||
void PlanetObject::update(float deltaTimeMs) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -14,75 +14,42 @@
|
|||||||
|
|
||||||
namespace ZL {
|
namespace ZL {
|
||||||
|
|
||||||
|
struct Triangle
|
||||||
|
{
|
||||||
|
std::array<Vector3f, 3> data;
|
||||||
|
|
||||||
|
Triangle(Vector3f p1, Vector3f p2, Vector3f p3)
|
||||||
|
: data{ p1, p2, p3 }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class PerlinNoise {
|
class PerlinNoise {
|
||||||
std::vector<int> p;
|
std::vector<int> p;
|
||||||
public:
|
public:
|
||||||
PerlinNoise() {
|
PerlinNoise();
|
||||||
p.resize(256);
|
PerlinNoise(uint64_t seed);
|
||||||
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()); // Дублируем для переполнения
|
|
||||||
}
|
|
||||||
|
|
||||||
float fade(float t) { return t * t * t * (t * (t * 6 - 15) + 10); }
|
float fade(float t);
|
||||||
float lerp(float t, float a, float b) { return a + t * (b - a); }
|
float lerp(float t, float a, float b);
|
||||||
float grad(int hash, float x, float y, float z) {
|
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 noise(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;
|
|
||||||
|
|
||||||
x -= floor(x);
|
float getSurfaceHeight(Vector3f pos);
|
||||||
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;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class PlanetObject {
|
class PlanetObject {
|
||||||
private:
|
private:
|
||||||
PerlinNoise perlin;
|
PerlinNoise perlin;
|
||||||
|
PerlinNoise colorPerlin;
|
||||||
void prepareDrawData();
|
void prepareDrawData();
|
||||||
VertexDataStruct planetMesh;
|
VertexDataStruct planetMesh;
|
||||||
VertexRenderStruct planetRenderStruct;
|
VertexRenderStruct planetRenderStruct;
|
||||||
|
|
||||||
|
std::shared_ptr<Texture> sandTexture;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
PlanetObject();
|
PlanetObject();
|
||||||
|
|
||||||
@ -95,6 +62,12 @@ namespace ZL {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool drawDataDirty = true;
|
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 generateOctahedron();
|
||||||
|
VertexDataStruct generateSphere(int subdivisions);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ZL
|
} // namespace ZL
|
||||||
33
Renderer.cpp
33
Renderer.cpp
@ -294,6 +294,18 @@ namespace ZL {
|
|||||||
glBufferData(GL_ARRAY_BUFFER, data.TexCoordData.size() * 8, &data.TexCoordData[0], GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, data.TexCoordData.size() * 8, &data.TexCoordData[0], GL_STATIC_DRAW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.TexCoord3Data.size() > 0)
|
||||||
|
{
|
||||||
|
if (!texCoord3VBO)
|
||||||
|
{
|
||||||
|
texCoord3VBO = std::make_shared<VBOHolder>();
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, texCoord3VBO->getBuffer());
|
||||||
|
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, data.TexCoord3Data.size() * 12, &data.TexCoord3Data[0], GL_STATIC_DRAW);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (data.NormalData.size() > 0)
|
if (data.NormalData.size() > 0)
|
||||||
{
|
{
|
||||||
@ -459,6 +471,12 @@ namespace ZL {
|
|||||||
return ProjectionModelViewMatrix;
|
return ProjectionModelViewMatrix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Matrix4f Renderer::GetCurrentModelViewMatrix()
|
||||||
|
{
|
||||||
|
return ModelviewMatrixStack.top();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void Renderer::SetMatrix()
|
void Renderer::SetMatrix()
|
||||||
{
|
{
|
||||||
if (ProjectionMatrixStack.size() <= 0)
|
if (ProjectionMatrixStack.size() <= 0)
|
||||||
@ -471,15 +489,16 @@ namespace ZL {
|
|||||||
throw std::runtime_error("Modelview matrix stack out!");
|
throw std::runtime_error("Modelview matrix stack out!");
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectionModelViewMatrix = ProjectionMatrixStack.top() * ModelviewMatrixStack.top();
|
Matrix4f& modelViewMatrix = ModelviewMatrixStack.top();
|
||||||
|
ProjectionModelViewMatrix = ProjectionMatrixStack.top() * modelViewMatrix;
|
||||||
|
|
||||||
static const std::string ProjectionModelViewMatrixName = "ProjectionModelViewMatrix";
|
static const std::string ProjectionModelViewMatrixName = "ProjectionModelViewMatrix";
|
||||||
|
|
||||||
//static const std::string ProjectionMatrixName = "ProjectionMatrix";
|
|
||||||
|
|
||||||
RenderUniformMatrix4fv(ProjectionModelViewMatrixName, false, &ProjectionModelViewMatrix.m[0]);
|
RenderUniformMatrix4fv(ProjectionModelViewMatrixName, false, &ProjectionModelViewMatrix.m[0]);
|
||||||
|
|
||||||
//RenderUniformMatrix4fv(ProjectionMatrixName, false, &ProjectionMatrixStack.top().m[0]);
|
static const std::string ModelViewMatrixName = "ModelViewMatrix";
|
||||||
|
|
||||||
|
RenderUniformMatrix4fv(ModelViewMatrixName, false, &modelViewMatrix.m[0]);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -733,6 +752,7 @@ namespace ZL {
|
|||||||
static const std::string vBinormal("vBinormal");
|
static const std::string vBinormal("vBinormal");
|
||||||
static const std::string vColor("vColor");
|
static const std::string vColor("vColor");
|
||||||
static const std::string vTexCoord("vTexCoord");
|
static const std::string vTexCoord("vTexCoord");
|
||||||
|
static const std::string vTexCoord3("vTexCoord3");
|
||||||
static const std::string vPosition("vPosition");
|
static const std::string vPosition("vPosition");
|
||||||
|
|
||||||
//glBindVertexArray(VertexRenderStruct.vao->getBuffer());
|
//glBindVertexArray(VertexRenderStruct.vao->getBuffer());
|
||||||
@ -763,6 +783,11 @@ namespace ZL {
|
|||||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoordVBO->getBuffer());
|
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoordVBO->getBuffer());
|
||||||
VertexAttribPointer2fv(vTexCoord, 0, NULL);
|
VertexAttribPointer2fv(vTexCoord, 0, NULL);
|
||||||
}
|
}
|
||||||
|
if (VertexRenderStruct.data.TexCoord3Data.size() > 0)
|
||||||
|
{
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.texCoord3VBO->getBuffer());
|
||||||
|
VertexAttribPointer2fv(vTexCoord3, 0, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.positionVBO->getBuffer());
|
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.positionVBO->getBuffer());
|
||||||
VertexAttribPointer3fv(vPosition, 0, NULL);
|
VertexAttribPointer3fv(vPosition, 0, NULL);
|
||||||
|
|||||||
@ -44,6 +44,7 @@ namespace ZL {
|
|||||||
{
|
{
|
||||||
std::vector<Vector3f> PositionData;
|
std::vector<Vector3f> PositionData;
|
||||||
std::vector<Vector2f> TexCoordData;
|
std::vector<Vector2f> TexCoordData;
|
||||||
|
std::vector<Vector3f> TexCoord3Data;
|
||||||
std::vector<Vector3f> NormalData;
|
std::vector<Vector3f> NormalData;
|
||||||
std::vector<Vector3f> TangentData;
|
std::vector<Vector3f> TangentData;
|
||||||
std::vector<Vector3f> BinormalData;
|
std::vector<Vector3f> BinormalData;
|
||||||
@ -63,6 +64,7 @@ namespace ZL {
|
|||||||
std::shared_ptr<VAOHolder> vao;
|
std::shared_ptr<VAOHolder> vao;
|
||||||
std::shared_ptr<VBOHolder> positionVBO;
|
std::shared_ptr<VBOHolder> positionVBO;
|
||||||
std::shared_ptr<VBOHolder> texCoordVBO;
|
std::shared_ptr<VBOHolder> texCoordVBO;
|
||||||
|
std::shared_ptr<VBOHolder> texCoord3VBO;
|
||||||
std::shared_ptr<VBOHolder> normalVBO;
|
std::shared_ptr<VBOHolder> normalVBO;
|
||||||
std::shared_ptr<VBOHolder> tangentVBO;
|
std::shared_ptr<VBOHolder> tangentVBO;
|
||||||
std::shared_ptr<VBOHolder> binormalVBO;
|
std::shared_ptr<VBOHolder> binormalVBO;
|
||||||
@ -108,6 +110,7 @@ namespace ZL {
|
|||||||
|
|
||||||
|
|
||||||
Matrix4f GetProjectionModelViewMatrix();
|
Matrix4f GetProjectionModelViewMatrix();
|
||||||
|
Matrix4f GetCurrentModelViewMatrix();
|
||||||
|
|
||||||
void SetMatrix();
|
void SetMatrix();
|
||||||
|
|
||||||
|
|||||||
9
ZLMath.h
9
ZLMath.h
@ -79,6 +79,15 @@ namespace ZL {
|
|||||||
struct Vector2f
|
struct Vector2f
|
||||||
{
|
{
|
||||||
std::array<float, 2> v = {0.f, 0.f};
|
std::array<float, 2> v = {0.f, 0.f};
|
||||||
|
|
||||||
|
Vector2f()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector2f(float x, float y)
|
||||||
|
: v{ x,y }
|
||||||
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Vector2f operator+(const Vector2f& x, const Vector2f& y);
|
Vector2f operator+(const Vector2f& x, const Vector2f& y);
|
||||||
|
|||||||
BIN
resources/rock.png
(Stored with Git LFS)
Normal file
BIN
resources/rock.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
resources/sand.png
(Stored with Git LFS)
Normal file
BIN
resources/sand.png
(Stored with Git LFS)
Normal file
Binary file not shown.
@ -1,11 +1,23 @@
|
|||||||
attribute vec3 vPosition;
|
attribute vec3 vPosition;
|
||||||
attribute vec3 vColor;
|
attribute vec3 vColor;
|
||||||
|
attribute vec3 vNormal;
|
||||||
|
attribute vec2 vTexCoord; // <-- Обычные UV (если используются)
|
||||||
|
|
||||||
varying vec3 color;
|
varying vec3 color;
|
||||||
|
varying vec3 normal;
|
||||||
|
|
||||||
|
varying vec2 TexCoord; // <-- Передаем UV
|
||||||
|
|
||||||
uniform mat4 ProjectionModelViewMatrix;
|
uniform mat4 ProjectionModelViewMatrix;
|
||||||
|
uniform mat4 ModelViewMatrix;
|
||||||
|
uniform vec3 uLightDirection;
|
||||||
|
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
|
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
|
||||||
color = vColor;
|
|
||||||
|
normal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz);
|
||||||
|
|
||||||
|
color = vColor;
|
||||||
|
TexCoord = vTexCoord;
|
||||||
}
|
}
|
||||||
@ -1,9 +1,22 @@
|
|||||||
//precision mediump float;
|
varying vec3 color; // Цвет вершины (с шумом)
|
||||||
varying vec3 color;
|
varying vec3 normal;
|
||||||
|
varying vec3 lightDirection_VS;
|
||||||
|
varying vec2 TexCoord; // UV-координаты
|
||||||
|
|
||||||
|
uniform sampler2D Texture;
|
||||||
|
uniform vec3 uLightDirection;
|
||||||
void main()
|
void main()
|
||||||
{
|
{
|
||||||
//gl_FragColor = vec4(color, 1.0);
|
// 1. Получаем цвет из текстуры и смешиваем с цветовым шумом
|
||||||
gl_FragColor = vec4(1.0, 1.0, 0.5, 1.0);
|
vec4 textureColor = texture2D(Texture, TexCoord);
|
||||||
|
vec3 finalColor = textureColor.rgb * color;
|
||||||
|
|
||||||
|
// 3. Расчет освещения
|
||||||
|
float diffuse = max(0.0, dot(normal, uLightDirection));
|
||||||
|
float ambient = 0.2;
|
||||||
|
float lightingFactor = min(1.0, ambient + diffuse);
|
||||||
|
|
||||||
|
|
||||||
|
gl_FragColor = vec4(finalColor * lightingFactor, 1.0);
|
||||||
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user