#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);
Это была глупая ошибка, но я рад, что теперь она исправлена.