Пересчет нормалей регулярной поверхности сетки (C++/OpenGL)

Я пытаюсь вычислить нормали моей поверхности сетки. Карта имеет размер 29952 x 19968 пикселей, а размер каждой ячейки составляет 128 x 128 пикселей. Итак, у меня 36895 вершин.

Сетка:
Сетка

Мой плоский массив карт отправляется шейдерам со следующей структурой:

float vertices[368950] = {

  //  x     y     z    znoise   xTex  yTex  xNorm yNorm zNorm Type
    16384,16256,-16256,   0,    0.54, 0.45,   0,    0,   1,    1,
    16256,16384,-16384,   0,    0.54, 0.45,   0,    0,   1,    1,
    ......
} 

Я вычисляю zNoise с помощью функции

float noise(float x, float y){};

И это работает (я добавляю его к y и z в вершинном шейдере).

Способ 1

Если я вычисляю нормали, используя метод конечных разностей, я получаю хороший результат. Псевдокод:

  vec3 off = vec3(1.0, 1.0, 0.0);
  float hL = noise(P.xy - off.xz);
  float hR = noise(P.xy + off.xz);
  float hD = noise(P.xy - off.zy);
  float hU = noise(P.xy + off.zy);
  N.x = hL - hR;
  N.y = hD - hU;
  N.z = 2.0;
  N = normalize(N);

Но в случае, если мне нужно отредактировать карту вручную, например, в контексте редактора, где вы устанавливаете zNoise с помощью инструмента для создания гор, как вы хотите, этот метод не поможет.

Я получаю этот хороший результат (виден на миникарте) (нормали намеренно довольно темные):
отличный результат

Способ 2

  |    |    |
--6----1----+-
  |\   |\   |      Y
  | \  | \  |      ^
  |  \ |  \ |      |
  |   \|   \|      |
--5----+----2--    +-----> X
  |\   |\   |
  | \  | \  |
  |  \ |  \ |
  |   \|   \|
--+----4----3--
  |    |    |

Итак, я пытаюсь вычислить нормаль, используя соседние треугольники, но результат сильно отличается (кажется, где-то есть ошибка):

Код.

getVertex() — это функция, которая принимает x и y и возвращает информацию о вершине, связанную с этой вершиной. VerticesPos — это массив 1d, который содержит положение каждой вершины, чтобы можно было получить информацию от vertices (той, которую я описал выше, с 10 значениями для каждой вершины). я решил отредактировать y и z в вершинном шейдере, чтобы оставить нетронутыми x и y и использовать их для индексации вершин до VerticesPos. (надеюсь понятно).

glm::vec3 getVertex(int x, int y) {

    int j = VerticesPos[(int)(y/128 * 29952 / 128 + x/128)];

    float zNoise = vertices[j * 10 + 3] * 2;
    float x1 = vertices[j * 10];
    float y1 = vertices[j * 10 + 1] + zNoise;
    float z1 = vertices[j * 10 + 2] + zNoise;

    return glm::vec3(x1, y1, z1);
}

getAdjacentVertices() – это функция, которая принимает vec2d (координаты x и y) и возвращает 6 смежных вершин, упорядоченных по часовой стрелке.

std::array<glm::vec3, 6> getAdjacentVertices(glm::vec2 pos) {
    std::array<glm::vec3, 6> output;
    output = {
        getVertex(pos.x, pos.y + 128), // up
        getVertex(pos.x + 128, pos.y), // right 
        getVertex(pos.x + 128, pos.y - 128), // down-right
        getVertex(pos.x, pos.y - 128), // down
        getVertex(pos.x - 128, pos.y), // left
        getVertex(pos.x - 128, pos.y + 128), // up-left

    };
    return output;
}

И последняя функция, которая выполняет эту работу:

glm::vec3 mapgen::updatedNormals(glm::vec2 pos) {

    bool notBorderLineX = pos.x > 128 && pos.x < 29952 - 128;
    bool notBorderLineY = pos.y > 128 && pos.y < 19968 - 128;

    if (notBorderLineX && notBorderLineY) {

        glm::vec3 a = getVertex(pos.x, pos.y);

        std::array<glm::vec3, 6> adjVertices = getAdjacentVertices(pos);
        glm::vec3 sum(0.f);

        for (int i = 0; i < 6; i++) {
            int j;
            (i == 0) ? j = 5 : j = i - 1;

            glm::vec3 side1 = adjVertices[i] - a;
            glm::vec3 side2 = adjVertices[j] - a;

            sum += glm::cross(side1, side2);
        }
        return glm::normalize(sum);
    }

    else {
        return glm::vec3(0.3333f);
    }
}

К сожалению, я получаю этот плохой результат (видно на миникарте):
плохой результат

Примечание. Здания находятся в разных позициях, но поверхность имеет одно и то же начальное число при использовании двух методов.

Может ли кто-нибудь помочь? :-)


РЕДАКТИРОВАТЬ:

Я добавляю больше изображений, чтобы помочь понять проблему. Способ 1: Image

Способ 2: Image2


person fabian7    schedule 01.10.2019    source источник
comment
Я понимаю, что вы пытаетесь сделать, и я могу следовать вашему коду. Мне кажется это правильным. Возможно ли, что проблема в getVertex? getVertex, похоже, считывает высоту, связанную с двумерной координатой, и возвращает трехмерную координату. Возможно ли, что поиск (высота) смещается на 1 на строку (строку)?   -  person Rabbid76    schedule 01.10.2019
comment
@ Rabbid76 Я отредактировал пост, добавив функцию getVertex.   -  person fabian7    schedule 01.10.2019
comment
Вы уверены насчет (int)(y/128 * 29952 / 128 + x/128)? Если текстура имеет 29952 пикселя подряд, то должно быть (int)(y/128 * 29952 + x/128)? Я ошибаюсь?   -  person Rabbid76    schedule 01.10.2019
comment
Да, потому что 29952 — это ширина карты, но имейте в виду, что у нас есть одна вершина через каждые 128 пикселей, поэтому у нас есть 234 вершины по оси X и 156 по оси Y.   -  person fabian7    schedule 01.10.2019
comment
Есть ли 234 плитки или вершины вдоль оси Y?   -  person Rabbid76    schedule 01.10.2019
comment
@Rabbid76 156. Размер карты 29952 x 19968 пикселей. Теперь я постараюсь добавить больше информации с некоторыми дополнительными скриншотами.   -  person fabian7    schedule 01.10.2019
comment
Не просматривал ваш код вообще, но последнее изображение выглядит так, как будто некоторые из ваших нормалей изменили направление, и рендеринг интерполирует между противоположными нормалями, создавая волнистый узор (например, вывод поверхности 2-й или более высокой степени), не уверен, как вы пересчитываете, предполагая проверка перекрестного произведения, если ваш порядок операндов создает нормаль на одной стороне для всех узлов вашей сетки, другой вариант может быть неправильным суммированием соседних нормалей, см. Как добиться гладких нормалей касательного пространства? на обычной сетке это просто. Но я просто думаю здесь   -  person Spektre    schedule 02.10.2019
comment
@Spektre в методе 2 я пытаюсь выполнить ваш алгоритм, вычисляя нормали граней для каждого соседнего треугольника, суммируя их и нормализуя. взять данные из vertices легко, потому что индексация всегда одинакова. тот факт, что метод 1 работает хорошо, доказывает, что вершины берутся правильно.... проблема в вычислении нормалей методом 2, но я не могу понять это. если вам нужна дополнительная информация, просто скажите мне... заранее спасибо   -  person fabian7    schedule 03.10.2019
comment
@ fabian7 Я думаю, что самый простой способ (опровергнуть) доказать - это отобразить все суммированные субнормальные значения (в среднем) в виде строк, чтобы вы могли видеть, не перевернуты ли некоторые из них ... если они просто меняют местами операнды перекрестного произведения порядок для рассматриваемого субнормального... Без простого MCVE мы не можем проверить, просто догадайтесь...   -  person Spektre    schedule 03.10.2019


Ответы (1)


РЕШЕНО!

Я рассчитывал нормали одновременно с вычислением zNoise. Таким образом, возможно, что некоторые вершины были не на правильном Z.

Я решил вычислить перед всеми Z, а затем все нормали. Я просто хочу добавить код:

struct Triangle {
    glm::vec3 a, b, c;
};
std::array<Triangle, 6> getAdjacentTriangles(glm::ivec2 pos) {
    int gap = 128;
    std::array<Triangle, 6> triangles;  
    triangles[0] = {
        getVertex(pos.x, pos.y), getVertex(pos.x - gap, pos.y),getVertex(pos.x - gap, pos.y + gap),
    };
    triangles[1] = {
        getVertex(pos.x, pos.y), getVertex(pos.x - gap, pos.y + gap), getVertex(pos.x, pos.y + gap)
    };
    triangles[2] = {
        getVertex(pos.x, pos.y), getVertex(pos.x, pos.y + gap), getVertex(pos.x + gap, pos.y)
    };
    triangles[3] = {
        getVertex(pos.x, pos.y), getVertex(pos.x + gap, pos.y), getVertex(pos.x + gap, pos.y - gap)
    };
    triangles[4] = {
        getVertex(pos.x, pos.y), getVertex(pos.x + gap, pos.y - gap),getVertex(pos.x, pos.y - gap),
    };
    triangles[5] = {
        getVertex(pos.x, pos.y), getVertex(pos.x, pos.y - gap), getVertex(pos.x - gap, pos.y)
    };
    return triangles;
}
glm::vec3 calculateTriangleNormal(Triangle T) {
    glm::vec3 N = glm::cross(T.c - T.a, T.b - T.a);
    return N;
}
void mapgen::updateNormals() {
    for (int i = 0; i < nVertices * 10; i += 10) {
        float xCoord = mapgen::MapVertices()[i];
        float yCoord = mapgen::MapVertices()[i + 1];
        glm::ivec2 pos = glm::ivec2(int(xCoord), int(yCoord));  
        if (pos.x > 128 && pos.x < 29952 - 128 && pos.y > 128 && pos.y < 19968 - 128) {

            std::array<Triangle, 6> adjacentTriangles = getAdjacentTriangles(pos);
            glm::vec3 sum(0.f);

            for (int i = 0; i < 6; i++) {
                glm::vec3 normal = calculateTriangleNormal(adjacentTriangles[i]);
                sum += normal;
            }
            glm::vec3 N = glm::normalize(sum);

            mapgen::MapVertices()[i + 6] = N.x;
            mapgen::MapVertices()[i + 7] = N.y;
            mapgen::MapVertices()[i + 8] = N.z;
        }
        else {
            mapgen::MapVertices()[i + 6] = 0.f;
            mapgen::MapVertices()[i + 7] = 0.f;
            mapgen::MapVertices()[i + 8] = 1.f;
        }
    }
}

Яааааа!

person fabian7    schedule 05.10.2019