OpenGL: странный нормальный рендеринг

У меня проблема с нормалями OpenGL. Я визуализирую модель дракона, но у меня есть странные паттерны нормалей.

Вот скриншот моего рендера: render_screen

это мои методы создания буфера:

glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    if (has_position) {
        glGenBuffers(1, &vertex_buffer);
        glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer);
        glBufferData(GL_ARRAY_BUFFER, vertices.size() * 3 * sizeof(float), vertices.data(), GL_STATIC_DRAW);
        glEnableVertexAttribArray(kVertexArray);
        glVertexAttribPointer(kVertexArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    }

    if (has_normal) {
        glGenBuffers(1, &normal_buffer);
        glBindBuffer(GL_ARRAY_BUFFER, normal_buffer);
        glBufferData(GL_ARRAY_BUFFER, normals.size() * 3 * sizeof(float), normals.data(), GL_STATIC_DRAW);
        glEnableVertexAttribArray(kNormalArray);
        glVertexAttribPointer(kNormalArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    }

    if (has_tex_coord) {
        glGenBuffers(1, &tex_coord_buffer);
        glBindBuffer(GL_ARRAY_BUFFER, tex_coord_buffer);
        glBufferData(GL_ARRAY_BUFFER, tex_coords.size() * 3 * sizeof(float), tex_coords.data(), GL_STATIC_DRAW);
        glEnableVertexAttribArray(kTexCoordArray);
        glVertexAttribPointer(kTexCoordArray, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
    }

    if (has_index) {
        glGenBuffers(1, &index_buffer);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * 3 * sizeof(unsigned short), indices.data(), GL_STATIC_DRAW);
        glBindVertexArray(0);
    }

и рисовать с помощью: glDrawElements(GL_TRIANGLES, indices.size() * 3, GL_UNSIGNED_SHORT, BUFFER_OFFSET(0));

Метод загрузчика объектов Wavefront:

bool Loadfs() {
    CVector3<float> vertex;
    CVector3<unsigned short> vindex, tindex, nindex;

    while(file_stream->offset_ < file_stream->size_) {
        if (file_stream->buffer_[file_stream->offset_] == '#') {
            char comment[512] = {0};
            file_stream->ReadLine(comment); // check return code -> "its local variable return address"
            file_stream->SkipLine();
        }
        else if (file_stream->buffer_[file_stream->offset_] == 'v') {
            file_stream->offset_++;

            if (file_stream->buffer_[file_stream->offset_] == ' ') {
                has_position = true;
                file_stream->offset_++;
                file_stream->SkipWhiteSpace();

                vertex.x = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.y = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.z = file_stream->ReadFloat();
                file_stream->SkipLine();

                vertices.push_back(vertex);
            }
            else if (file_stream->buffer_[file_stream->offset_] == 'n') {
                has_normal = true;
                file_stream->offset_++;
                file_stream->SkipWhiteSpace();

                vertex.x = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.y = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.z = file_stream->ReadFloat();
                file_stream->SkipLine();

                normals.push_back(vertex);
            }
            else if (file_stream->buffer_[file_stream->offset_] == 't') {
                has_tex_coord = true;
                file_stream->offset_++;
                file_stream->SkipWhiteSpace();

                vertex.x = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.y = file_stream->ReadFloat();
                file_stream->SkipWhiteSpace();

                vertex.z = file_stream->ReadFloat();
                file_stream->SkipLine();

                tex_coords.push_back(vertex);
            }
        }
        else if (file_stream->buffer_[file_stream->offset_] == 'f') {
            has_index = true;
            file_stream->offset_++;
            file_stream->SkipWhiteSpace();

            //
            if (has_position) {
                vindex.x = file_stream->ReadShort();
            }
            if (has_tex_coord) {
                file_stream->offset_++;
                tindex.x = file_stream->ReadShort();
            }
            if (has_normal) {
                file_stream->offset_++;
                nindex.x = file_stream->ReadShort();
            }
            file_stream->SkipWhile(' ');

            //
            if (has_position) {
                vindex.y = file_stream->ReadShort();
            }
            if (has_tex_coord) {
                file_stream->offset_++;
                tindex.y = file_stream->ReadShort();
            }
            if (has_normal) {
                file_stream->offset_++;
                nindex.y = file_stream->ReadShort();
            }
            file_stream->SkipWhile(' ');

            //
            if (has_position) {
                vindex.z = file_stream->ReadShort();
            }
            if (has_tex_coord) {
                file_stream->offset_++;
                tindex.z = file_stream->ReadShort();
            }
            if (has_normal) {
                file_stream->offset_++;
                nindex.z = file_stream->ReadShort();
            }
            vi.push_back(--vindex);
            ti.push_back(--tindex);
            ni.push_back(--nindex);
            //indices.push_back(--vindex);
        }
        else file_stream->SkipLine();
    }


    indices.insert(indices.end(), vi.begin(), vi.end());
    return true;
}

Здесь vs:glsl основные методы:

position_ = MV * vec4(vVertex, 1.0);
normal_ = normalize(N * vNormal);
texture_ = vTexture;
//shadow_ = S * M * vec4(vVertex, 1.0);

gl_Position = MVP * vec4(vVertex, 1.0);

и основные методы fs.glsl:

vec3 rgb = vec3(1.0, 1.0, 1.0);
rgb = PhongShade(g_light, g_material, position_, normal_);
_frag_color = vec4(rgb, 1.0);

//_frag_color = texel * vec4(ambient + diffuse + specular, 1.0);

Есть какие-нибудь мысли?


person Community    schedule 24.06.2015    source источник
comment
Это появлялось в подобной форме много раз. См., например: stackoverflow.com/questions/29718501/ или stackoverflow.com/questions/23349080/.   -  person Reto Koradi    schedule 24.06.2015
comment
Но один из них использует фиксированную функцию, а другой, я думаю, использует метод рисования массива. Я использую метод элемента, поэтому я должен оставить файл волнового фронта? Я также изучаю трассировку лучей. У вас есть предложение по типу файла модели для использования в моем коде? Спасибо.   -  person    schedule 25.06.2015
comment
Вы можете использовать алгоритм из здесь @RetoKoradi, если хотите использовать glDrawElements. Логика индексирования работает одинаково для фиксированной функции и программируемого конвейера, так что это не будет проблемой.   -  person samgak    schedule 26.06.2015


Ответы (1)


Проблема в том, что OpenGL использует один индексный буфер для индексации позиции вершины, текстурных координат и буферов нормалей, используя один и тот же индекс для каждого (3 индекса для треугольника), тогда как формат Wavefront obj позволяет каждой грани указывать отдельные индексы для положение вершины, координата текстуры и нормаль независимо друг от друга (всего 9 индексов для треугольника).

Это не ясно из вашего кода, но, скорее всего, вы на самом деле не используете индексные массивы ti и ni, которые вы читаете, а создаете свой index_buffer из vi, поэтому индексы вершин используются для индексации в normal_buffer и tex_coord_buffer, давая странные результаты.

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

В качестве альтернативы откажитесь от индексированного рендеринга с помощью glDrawElements и используйте вместо него glDrawArrays. Запишите по одной записи в каждый из массивов для каждой вершины грани на основе индексов вершины грани для положения, координаты текса и нормали, а затем визуализируйте это. Однако это менее эффективно.

Другим решением (если это возможно) является экспорт файла obj таким образом, чтобы каждая вершина грани использовала одни и те же индексы для позиции, координаты tex и нормали.

person samgak    schedule 24.06.2015
comment
Спасибо самгак. Я также вставил индекс ni и ti в индексы и отобразил то же самое. Я хочу использовать метод элемента для эффективности. У меня есть файл obj, который использует один и тот же индекс v, t, n (например, f 1/1/1 2/2/2 3/3/3 ......) и отображается правильно, но другие файлы имеют другой нормальный, теккорд размер . Что я должен делать? (Извините за мой английский:) Я думаю, что понимаю код лучше, чем английский) - person ; 25.06.2015
comment
Вы должны использовать формат с теми же индексами, например. 1/1/1 2/2/2 3/3/3, потому что OpenGL не поддерживает раздельную индексацию для position/tex/normal. Он менее эффективен в использовании памяти, чем файл .obj, но вы ничего не можете с этим поделать, потому что это ограничение OpenGL. Таким образом, вам нужно либо использовать формат файла с индексами в этом формате, либо преобразовать его во время загрузки или на этапе предварительной обработки. - person samgak; 26.06.2015