Генерация гладких нормалей для рельефа в OpenGL - PullRequest
1 голос
/ 15 февраля 2020

Я внедряю систему для генерации ландшафта с использованием перлин-шума. Вот как я генерирую вершины:

int arrayIdx = 0;

    for(float x = offset.x - CHUNK_WIDTH / 2.0f; x < float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f; x += TRIANGLE_WIDTH) {
        for(float y = offset.y - CHUNK_WIDTH / 2.0f; y < float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f; y += TRIANGLE_WIDTH) {
            float height0 = noise->octaveNoise(x + 0.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH),
            height1 = noise->octaveNoise(x + 1.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH),
            height2 = noise->octaveNoise(x + 0.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH),
            height3 = noise->octaveNoise(x + 1.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH);

            mapVertices[arrayIdx + 0] = glm::vec3(x + 0.0f * TRIANGLE_WIDTH, height0, y + 0.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 1] = glm::vec3(x + 1.0f * TRIANGLE_WIDTH, height1, y + 0.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 2] = glm::vec3(x + 0.0f * TRIANGLE_WIDTH, height2, y + 1.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 3] = glm::vec3(x + 1.0f * TRIANGLE_WIDTH, height3, y + 1.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 4] = glm::vec3(x + 1.0f * TRIANGLE_WIDTH, height1, y + 0.0f * TRIANGLE_WIDTH);
            mapVertices[arrayIdx + 5] = glm::vec3(x + 0.0f * TRIANGLE_WIDTH, height2, y + 1.0f * TRIANGLE_WIDTH);

            mapUVs[arrayIdx + 0] = glm::vec2(x + 0.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 1] = glm::vec2(x + 1.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 2] = glm::vec2(x + 0.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 3] = glm::vec2(x + 1.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 4] = glm::vec2(x + 1.0f * TRIANGLE_WIDTH, y + 0.0f * TRIANGLE_WIDTH);
            mapUVs[arrayIdx + 5] = glm::vec2(x + 0.0f * TRIANGLE_WIDTH, y + 1.0f * TRIANGLE_WIDTH);



            glm::vec3 normal0 = -1.0f * glm::triangleNormal(mapVertices[arrayIdx + 0], mapVertices[arrayIdx + 1], mapVertices[arrayIdx + 2]),
            normal1 = +1.0f * glm::triangleNormal(mapVertices[arrayIdx + 3], mapVertices[arrayIdx + 4], mapVertices[arrayIdx + 5]);

            mapNormals[arrayIdx + 0] = normal0;
            mapNormals[arrayIdx + 1] = (normal0 + normal1) / 2.0f;
            mapNormals[arrayIdx + 2] = (normal0 + normal1) / 2.0f;
            mapNormals[arrayIdx + 3] = normal1;
            mapNormals[arrayIdx + 4] = (normal0 + normal1) / 2.0f;
            mapNormals[arrayIdx + 5] = (normal0 + normal1) / 2.0f;


            arrayIdx += 6;
        }
    }

Неиспользование освещения дает эти довольно плавные результаты,

enter image description here

Единственное Осталось сделать, чтобы сгенерировать нормали для треугольников, что сделает ландшафт более гладким. Простое использование glm::triangleNormal дает этот результат,

enter image description here

Как видите, освещение действительно разрушает иллюзию гладкой поверхности.

Я попытался использовать среднее значение нормалей на сталкивающихся вершинах треугольников, как это:

arrayIdx = 0;

    for(float x = offset.x - CHUNK_WIDTH / 2.0f; x < float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f; x += TRIANGLE_WIDTH) {
        for(float y = offset.y - CHUNK_WIDTH / 2.0f; y < float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f; y += TRIANGLE_WIDTH) {

            if((x == offset.x - CHUNK_WIDTH / 2.0f && y == offset.y - CHUNK_WIDTH / 2.0f) ||
               (x == float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH && y == offset.y - CHUNK_WIDTH / 2.0f) ||
               (x == offset.x - CHUNK_WIDTH / 2.0f && y ==  float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH) ||
               (x == float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH && y ==  float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH)) {
                //Special case
            }
            else if(x ==  float(CHUNK_WIDTH) + offset.x - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH ||
                    y ==  float(CHUNK_WIDTH) + offset.y - CHUNK_WIDTH / 2.0f - TRIANGLE_WIDTH) {
                 //Special case
            }
            else {
                glm::vec3 averageNormals = (mapNormals[arrayIdx + 3 + 0] + //This triangle
                                            mapNormals[arrayIdx + 0 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6 + 6] + //Triangle after and this one
                                            mapNormals[arrayIdx + 2 + 6] + //Triangle in the right
                                            mapNormals[arrayIdx + 5 + 6] + //Triangle in the right
                                            mapNormals[arrayIdx + 1 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6] + //Triangle after this one
                                            mapNormals[arrayIdx + 4 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6])  //Triangle after this one
                                            / 6.0f;

                mapNormals[arrayIdx + 3 + 0] = averageNormals;
                mapNormals[arrayIdx + 2 + 6] = mapNormals[arrayIdx + 3 + 0];
                mapNormals[arrayIdx + 5 + 6] = mapNormals[arrayIdx + 3 + 0];
                mapNormals[arrayIdx + 1 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6] = mapNormals[arrayIdx + 3 + 0];
                mapNormals[arrayIdx + 4 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6] = mapNormals[arrayIdx + 3 + 0];
                mapNormals[arrayIdx + 0 + int(CHUNK_WIDTH * (1.0f / TRIANGLE_WIDTH)) * 6 + 6] = mapNormals[arrayIdx + 3 + 0];
            }

            arrayIdx += 6;
        }
    }

, который дал этот результат,

enter image description here

но это выглядит не намного лучше.

Использование нормалей в качестве цвета фрагмента дает такой результат:

enter image description here

Рендеринг нормалей в виде строк дает это, это до оптимизации и с большими треугольниками, чтобы уменьшить количество линий:

enter image description here

Это с моей оптимизацией:

enter image description here

Каким-то образом две нормали не устанавливаются.

Синие линии - это средние нормали, зеленые - индивидуальные нормали до оптимизации, они выглядят хорошо:

enter image description here

Это с каркасом:

enter image description here

Возможно, некоторые нормальные установить среднее значение?

Как создать гладкие нормали?

Ответы [ 2 ]

1 голос
/ 16 февраля 2020

Я вижу запрошенные изображения ...

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

Затем проверьте, нормализованы ли ваши нормали (того же размера), но, на первый взгляд, это выглядит нормально для меня.

Если вы отредактируете свою усредненную нормаль (с другим цветом), она должна быть заключена в другие нормали (посередине)

В любом случае это не выглядит правильным для меня:

img

выглядит так, как будто одна из нормалей движется в отраженном направлении. Вы уверены, что вычисляете нормаль по правильным вершинам?

Ваша геометрия выглядит как триангулированная сетка QUAD, поэтому вы можете просто использовать неправильную диагональ ...

1 голос
/ 15 февраля 2020

Способ вычисления нормали для каждой вершины заключается в следующем: необходимо учитывать каждый многоугольник, которому также принадлежит вершина, например:

Per-vertex normal computation

Как вы упомянули, проблема, похоже, связана с освещением. Получив правильную нормаль для каждой вершины, вы должны также использовать правильную технику шейдинга, которая в этом случае будет шейдингом Фонга или Гуро вместо плоского шейдинга, как кажется, вы используете.

...