lod 0 and 1

This commit is contained in:
Vladislav Khorev 2025-12-14 15:32:39 +03:00
parent 7a1961b5d1
commit 16ba536481
7 changed files with 373 additions and 7 deletions

View File

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

View File

@ -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<float>(delta));
planetObject.update(static_cast<float>(delta));
if (Environment::tapDownHold) {

View File

@ -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<Texture>(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<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);
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<Triangle> PlanetObject::subdivideTriangles(const std::vector<Triangle>& inputTriangles) {
@ -578,5 +631,162 @@ namespace ZL {
}
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<int> 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<int> 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<int> PlanetObject::triangleUnderCamera(int lod)
{
std::vector<int> 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<int> 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<int> 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

View File

@ -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<Triangle> subdivideTriangles(const std::vector<Triangle>& inputTriangles);
Vector3f calculateSurfaceNormal(Vector3f p_sphere);
VertexDataStruct trianglesToVertices(const std::vector<Triangle>& triangles);
VertexDataStruct generateSphere(int subdivisions);
std::vector<int> triangleUnderCamera(int lod);
};
} // namespace ZL

View File

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

View File

@ -0,0 +1,35 @@
// Вершинный шейдер (Vertex Shader)
attribute vec3 vPosition;
attribute vec3 vColor;
attribute vec3 vNormal;
attribute vec2 vTexCoord;
varying vec3 color;
varying vec3 normal;
varying vec2 TexCoord;
varying float viewZ; // <--- НОВОЕ: Z-координата в пространстве вида (View Space)
varying vec3 pos;
uniform mat4 ProjectionModelViewMatrix;
uniform mat4 ModelViewMatrix; // Нужна для преобразования vPosition в View Space
uniform vec3 uLightDirection;
void main()
{
// Преобразование позиции в пространство вида (View Space)
vec4 viewPosition = ModelViewMatrix * vec4(vPosition.xyz, 1.0);
// Сохраняем отрицательную Z-координату. В OpenGL Z-координата (глубина)
// в пространстве вида обычно отрицательна, но для расчета тумана
// удобнее использовать положительное значение.
viewZ = -viewPosition.z;
pos = vPosition.xyz;
gl_Position = ProjectionModelViewMatrix * vec4(vPosition.xyz, 1.0);
normal = normalize((ModelViewMatrix * vec4(vNormal, 0.0)).xyz);
color = vColor;
TexCoord = vTexCoord;
}

View File

@ -0,0 +1,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 <DIST_FOG_MAX) {
// Нормализация: 0.0f при DIST_FOG_MAX, 1.0f при DIST_FOG_MIN
float distRange = DIST_FOG_MAX - DIST_FOG_MIN;
float distNormalized = clamp((uDistanceToPlanetSurface - DIST_FOG_MIN) / distRange, 0.0, 1.0);
// alpha = 0.0 (Далеко) ... 1.0 (Близко к 1000.f)
fogFactor = 1.0 - distNormalized;
}
if (uDistanceToPlanetSurface < DIST_FOG_MIN)
{
fogFactor = 1.0;
}
// Если uDistanceToPlanetSurface < 1000.0f, то fogFactor = 0.0f (пока не пересчитан ниже),
// что позволяет Z-Far Fog взять контроль.
// --- 3.2. Расчет коэффициента тумана на основе Z-глубины (Z-Far Fog) ---
// Этот туман работает всегда, но его эффект усиливается при уменьшении uCurrentZFar,
// гарантируя, что все, что подходит к границе uCurrentZFar, будет скрыто.
// Точка начала тумана: 90% от uCurrentZFar
float farFogStart = 2000.f;//uCurrentZFar * 0.7;
// Длина перехода тумана: 10% от uCurrentZFar
float depthRange = 2000.f;//uCurrentZFar * 0.25;
if (abs(uDistanceToPlanetSurface) < 410.f)
{
farFogStart = 1800.f * abs(uDistanceToPlanetSurface-10.f) * 0.0025 + 200.f;
depthRange = farFogStart;
}
if (abs(uDistanceToPlanetSurface) < 10.f)
{
farFogStart = 200.f;
depthRange = 200.f;
}
float farFogFactor = 0.0;
if (depthRange > 0.0) {
// Нормализация Z-глубины: 0.0f при farFogStart, 1.0f при farFogStart + depthRange
farFogFactor = clamp((abs(viewZ) - farFogStart) / depthRange, 0.0, 1.0);
}
// --- 3.3. Объединение ---
// Когда мы далеко (fogFactor > 0.0), Distance Fog доминирует.
// Когда мы близко (fogFactor = 0.0), Z-Far Fog берет управление.
// Если оба активны, берем максимум (например, когда фрагмент далек от игрока, но игрок все еще в зоне Distance Fog).
//fogFactor = max(fogFactor, farFogFactor);
fogFactor = fogFactor * farFogFactor;
// 4. Смешивание цвета с туманом
//vec3 mountainColor = vec3((length(pos+vec3(0.0,0.0,45000.0))-20000.0)/100.0, 0.5,0.0);
gl_FragColor = mix(vec4(finalColor.rgb, 0.5), FOG_COLOR, fogFactor);
//gl_FragColor = vec4((length(pos+vec3(0.0,0.0,45000.0))-20000.0)/100.0, 0.0,0.0, 1.0);
//gl_FragColor = vec4(fogFactor, 0.5,0.5, 1.0);
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}