Невозможно визуализировать простую сетку с гладкой поверхностью в OpenGL (C ), как в любом другом программном обеспечении для 3D-графики?

#c #opengl #glsl #assimp

#c #opengl #glsl #assimp

Вопрос:

Я использую современный OpenGL для визуализации сетки head, импортированной из файла .obj, нормальные векторы которого были созданы Blender. Когда я загружаю эту модель в любом средстве просмотра 3D-объектов, все выглядит нормально

введите описание изображения здесь

но когда я пытаюсь визуализировать это сам, используя OpenGL, поверхность не выглядит гладкой (выглядит неровной), а также свет появляется не с той стороны головы. Чтобы протестировать свой код рендеринга света, я применил его к коробке, и он работает отлично, но на лицевой сетке он не работает (положение источника света визуализируется маленьким белым прямоугольником прямо за сеткой головы)

введите описание изображения здесь

введите описание изображения здесь

Для окна я создаю три экземпляра для каждой вершины, но каждый раз с другой нормалью, соответствующей соответствующей стороне. Сначала я применил тот же метод для головной сетки, имея несколько экземпляров вершины с разными нормалями для предполагаемого квадрата. Когда сетка выглядела неровной, я заподозрил, что наличие разных нормалей, связанных с одной и той же вершиной, может быть не очень хорошей идеей, особенно на гладкой поверхности, такой как сетчатая сетка. Потому что это может привести к резкому изменению направления света. Поэтому я использовал Assimp для загрузки .Obj надеялся, что он переставит вершины таким образом, чтобы каждая вершина имела уникальные характеристики (координаты текстуры, нормали, позиции и т. Д.), И Использовал связанные переставленные индексы для загрузки в мой индексный буфер. Но ничего не изменилось. Я не уверен, что я делаю ошибку при создании буфера вершин или в своих шейдерах, но я напрямую импортировал их из того, что я написал, чтобы визуализировать куб следующим образом.

 struct Vertex {
    glm::vec3 position;
    glm::vec2 texCoords;
    glm::vec3 normal;
};

struct Mesh {
    vector<Vertex>       vertices;
    vector<unsigned int> indices;
};

unsigned int createMeshVertexArray() {

    unsigned int posComponents = 3;          // xyz
    unsigned int uvComponents = 2;           // uv
    unsigned int normComponents = 3;         // ijk
    
    unsigned int vao;
    glGenVertexArrays(1, amp;vao);
    glBindVertexArray(vao);

    unsigned int vbo;
    glGenBuffers(1, amp;vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, _meshes[0].vertices.size() * sizeof(Vertex), amp;_meshes[0].vertices[0], GL_STATIC_DRAW); // perhaps gl_dynamic_draw is better

    unsigned int posAttribId = 0;
    unsigned int uvAttribId = 1;
    unsigned int normAttribId = 2;
    
    int posOffset = 0 * sizeof(float);
    int uvOffset = (posOffset   posComponents) * sizeof(float);
    int normOffset = (uvOffset   uvComponents) * sizeof(float);
    
    size_t stride = (posComponents   uvComponents   normComponents) * sizeof(float);
    
    glEnableVertexAttribArray(posAttribId);     
    glEnableVertexAttribArray(uvAttribId);
    glEnableVertexAttribArray(normAttribId);

    glVertexAttribPointer(posAttribId, posComponents, GL_FLOAT, GL_FALSE, stride, (const void*)posOffset);
    glVertexAttribPointer(uvAttribId, uvComponents, GL_FLOAT, GL_FALSE, stride, (const void*)uvOffset);
    glVertexAttribPointer(normAttribId, normComponents, GL_FLOAT, GL_FALSE, stride, (const void*)normOffset);

    return vao;

}

unsigned int createMeshIndexBuffer() {

    unsigned int ibo;
    glGenBuffers(1, amp;ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _meshes[0].indices.size() * sizeof(unsigned int), amp;_meshes[0].indices[0], GL_STATIC_DRAW);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); // unbind to make sure nothing else is accidentally added to this index buffer later

    return ibo;
}

 

Основная функция и цикл рендеринга.

 
int main(void) {
    //=============================================================================
    //------------------ light source and camera position
    //=============================================================================
    float cameraOffsetY = 1.5;
    float radius = 8.0;
    glm::vec3 lightPos(0.0, 3.0, 1.0);
    glm::vec3 cameraPos(0.0, cameraOffsetY, radius);
    glm::vec3 cameraFront(0.0, cameraOffsetY, 0.0);
    glm::vec3 cameraUp(0.0, 1.0, 0.0);

    //=============================================================================
    //------------------ projection matrix
    //=============================================================================
    float f = windowWidth;                                  // focal distance
    float fov = 2 * atan(windowWidth / (float)(2 * f));     // in radian
    float aspectRatio = windowWidth / (float)windowHeight;
    glm::mat4 proj = glm::perspective(fov, aspectRatio, 0.1f, 100.0f);

    //=============================================================================
    //------------------ head mesh
    //=============================================================================
    glm::mat4 meshModel = glm::translate(glm::mat4(1.0), glm::vec3(2.0, 0.0, -2.0));
    meshModel = glm::rotate(meshModel, glm::radians(-30.0f), glm::vec3(0.0, 1.0, 0.0));
    float faceSc = 1.0;
    meshModel = glm::scale(meshModel, glm::vec3(faceSc, faceSc, faceSc));

    glVisual.loadModel("../files/pose_17_with_normals.obj");
    unsigned int meshVao = glVisual.createMeshVertexArray();                      // vertex array
    unsigned int meshIbo = glVisual.createMeshIndexBuffer();                      // index buffer
    unsigned int meshSbo = glVisual.handleShaders("../files/faceMesh.glsl");      // shader

    unsigned int meshLoc_mvp     = glGetUniformLocation(meshSbo, "mvp");
    unsigned int meshModelLoc    = glGetUniformLocation(meshSbo, "model");
    unsigned int meshNormMatLoc  = glGetUniformLocation(meshSbo, "nMat");
    unsigned int meshColorLoc    = glGetUniformLocation(meshSbo, "objectColor");
    unsigned int meshLightPosLoc = glGetUniformLocation(meshSbo, "lightPos");
    unsigned int meshCamPosLoc   = glGetUniformLocation(meshSbo, "cameraPos");

    glm::mat3 meshNormMat = glm::mat3(glm::transpose(glm::inverse(meshModel)));

    glUniformMatrix4fv(meshModelLoc, 1, GL_FALSE, glm::value_ptr(meshModel));
    glUniformMatrix3fv(meshNormMatLoc, 1, GL_FALSE, glm::value_ptr(meshNormMat));
    glUniform3f(meshColorLoc, 0.6, 0.6, 0.8);
    glUniform3f(meshLightPosLoc, lightPos[0], lightPos[1], lightPos[2]);

    size_t numOfMeshVisualVerts = NUM_OF_FACES * 4;

    while (true) {

        glm::mat4 view = glm::lookAt(cameraPos, cameraFront, cameraUp);

        glBindVertexArray(meshVao);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshIbo);
        glUseProgram(meshSbo);
        glm::mat4 mvp = proj * view * meshModel;
        glUniform3f(meshCamPosLoc, cameraPos[0], cameraPos[1], cameraPos[2]);
        glUniformMatrix4fv(meshLoc_mvp, 1, GL_FALSE, glm::value_ptr(mvp));

        glDrawArrays(GL_QUADS, 0, numOfMeshVisualVerts);

        glfwSwapBuffers(window);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  // clear buffer 
        glfwPollEvents();                                    // Poll for and process events
        if (glfwWindowShouldClose(window))
            break;

    }


    glfwTerminate();
    return 0;
}

 

Шейдеры вершин и фрагментов.

 #shader vertex
#version 330 core

layout(location = 0) in vec3 n_position;
layout(location = 1) in vec2 n_uv;
layout(location = 2) in vec3 n_normal;

out vec3 faceNormal;
out vec3 fragPos;     // fragment position

uniform mat4 mvp;      // mvp matrix
uniform mat4 model;    // model matrix
uniform mat3 nMat;     // normal matrix

void main() {

    gl_Position = mvp * vec4(n_position, 1.0);
    fragPos = vec3(model * vec4(n_position, 1.0));
    faceNormal = nMat * n_normal;   

};


#shader fragment
#version 330 core


layout(location = 0) out vec4 color;

in vec3 faceNormal;
in vec3 fragPos;     // fragment position in world coordinates

uniform vec3 objectColor;
uniform vec3 lightPos;
uniform vec3 cameraPos;

void main() {

    vec3 lightColor = vec3(1.0, 1.0, 1.0);

    float ambientCoeff = 0.3;
    
    vec3 normVec = normalize(faceNormal);
    vec3 lightDir = normalize(lightPos - fragPos);
    float diff = max(dot(normVec, lightDir), 0.0);
    vec3 diffuseCoeff = diff * lightColor;

    float specularStrength = 0.1;                     // depends on the material
    int shininess = 4;                                // how much to spread on the surface
    vec3 viewDir = normalize(cameraPos - fragPos);
    vec3 reflectDir = reflect(-lightDir, normVec);
    float reflectAmount = pow(max(dot(viewDir, reflectDir), 0.0), shininess);    
    vec3 specularCoeff = specularStrength * reflectAmount * lightColor;

    color = vec4((ambientCoeff   diffuseCoeff   specularCoeff) * objectColor, 1.0);

//  color = vec4(objectColor, 1.0);
    
};
 

Комментарии:

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

2. obj-файлы печально известны непоследовательным правилом намотки… используйте двустороннее освещение и отключите GL_CULL_FACE (так что не имеет значения, в какую сторону обращена нормаль) … код без выборки сетки, выдающий ошибку, не поможет… Под неровным вы подразумеваете неправильно окрашенный? поскольку неровная форма означает, что форма отличается, чего я не вижу, однако разрешение и вид изображений невелики, чтобы понять, так ли это

3. @Spektre Я ценю комментарий. Я попробую ваше предложение сегодня и посмотрю, что получится. Тем временем я поместил файл obj вместе с увеличенным изображением моей визуализированной сетки по ссылке ниже. Да, я имел в виду, что некоторые квадратики имеют два отдельных треугольника с совершенно разными цветами, создавая иллюзию, что поверхность неровная. Ссылка

4. Нормали модели выглядят нормально (добавьте одно маленькое отверстие на губах), так что проблема в вашем коде

5. @Spektre Большое спасибо за ваше блестящее предложение. Именно это и было причиной проблемы. Это в порядке вещей, и теперь это исправлено. Я собираюсь опубликовать ответ с более подробной информацией.

Ответ №1:

Как @Spektre предложил в разделе комментариев, проблема заключалась в том, как я настраивал макет для своего вершинного буфера. В частности, я неправильно рассчитал свое обычное смещение атрибута следующим образом.

     unsigned int posComponents = 3;          // xyz
    unsigned int uvComponents = 2;           // uv
    unsigned int normComponents = 3;         // ijk

    int posOffset = 0 * sizeof(float);
    int uvOffset = (posOffset   posComponents) * sizeof(float);
    int normOffset = (uvOffset   uvComponents) * sizeof(float);

    glVertexAttribPointer(posAttribId, posComponents, GL_FLOAT, GL_FALSE, stride, (const void*)posOffset);
    glVertexAttribPointer(uvAttribId, uvComponents, GL_FLOAT, GL_FALSE, stride, (const void*)uvOffset);
    glVertexAttribPointer(normAttribId, normComponents, GL_FLOAT, GL_FALSE, stride, (const void*)normOffset);

 

Тогда как это должно было быть сделано таким образом

 int normOffset = (posComponents   uvComponents) * sizeof(float);
 

Это была глупая ошибка, но я рад, что теперь она исправлена.

введите описание изображения здесь