2014-06-22 7 views
0

Я написал базовую программу, которая загружает модель и отображает ее на экране. Я использую GLSL для правильной трансформации модели, но нормали всегда кажутся неправильными после их вращения с каждой комбинацией матриц модели, матрицы просмотра, обратной, транспозиции и т. Д., О которых я мог думать. Матричная модель только вращение вокруг оси у, используя GLM:Нормальное вращение в GLSL

angle += deltaTime; 
modelMat = glm::rotate(glm::mat4(), angle, glm::vec3(0.f, 1.f, 0.f)); 

Мой текущий код вершинного шейдера (я изменил нормаль много много раз):

#version 150 core 
uniform mat4 projMat; 
uniform mat4 viewMat; 
uniform mat4 modelMat; 
in vec3 inPosition; 
in vec3 inNormal; 
out vec3 passColor; 

void main() 
{ 
    gl_Position = projMat * viewMat * modelMat * vec4(inPosition, 1.0); 
    vec3 normal = normalize(mat3(inverse(modelMat)) * inNormal); 
    passColor = normal; 
} 

И мой фрагмент шейдер:

#version 150 core 
in vec3 passColor; 
out vec4 outColor; 

void main() 
{ 
    outColor = vec4(passColor, 1.0); 
} 

Я знаю точно, что унифицированные переменные передается в шейдер правильно, так как модель сама преобразуется должным образом, и начальные нормали правильно, если я делаю расчеты су ch как направленное освещение.

Я создал GIF вращающейся модели, жаль низкого качества: http://i.imgur.com/LgLKHCb.gif?1

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

Edit:

Я добавил еще немного кода клиента ниже.

Это где буферы получить связаны для модели, в Mesh классе (vao является GLuint, определенный в классе):

GLuint vbo[3]; 

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

glGenBuffers(normals? (uvcoords? 3 : 2) : (uvcoords? 2 : 1), vbo); 

glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); 
glBufferData(GL_ARRAY_BUFFER, vcount * 3 * sizeof(GLfloat), vertices, GL_STATIC_DRAW); 
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0); 
glEnableVertexAttribArray(0); 

if(normals) 
{ 
    glBindBuffer(GL_ARRAY_BUFFER, vbo[1]); 
    glBufferData(GL_ARRAY_BUFFER, vcount * 3 * sizeof(GLfloat), normals, GL_STATIC_DRAW); 
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, 0, 0); 
    glEnableVertexAttribArray(1); 
} 

if(uvcoords) 
{ 
    glBindBuffer(GL_ARRAY_BUFFER, vbo[2]); 
    glBufferData(GL_ARRAY_BUFFER, vcount * 2 * sizeof(GLfloat), uvcoords, GL_STATIC_DRAW); 
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, 0); 
     glEnableVertexAttribArray(2); 
} 

glBindVertexArray(0); 

glGenBuffers(1, &ib); 
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib); 
glBufferData(GL_ELEMENT_ARRAY_BUFFER, icount * sizeof(GLushort), indices, GL_STATIC_DRAW); 

Это где шейдеры компилируются после загрузки в память с простой readf(), в классе Material:

u32 vertexShader = glCreateShader(GL_VERTEX_SHADER); 
u32 fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); 

glShaderSource(vertexShader, 1, (const GLchar**)&vsContent, 0); 
glCompileShader(vertexShader); 
if(!validateShader(vertexShader)) return false; 
    glShaderSource(fragmentShader, 1, (const GLchar**)&fsContent, 0); 
glCompileShader(fragmentShader); 
if(!validateShader(fragmentShader)) return false; 

programHandle = glCreateProgram(); 

glAttachShader(programHandle, vertexShader); 
glAttachShader(programHandle, fragmentShader); 

glBindAttribLocation(programHandle, 0, "inPosition"); 
glBindAttribLocation(programHandle, 1, "inNormal"); 
//glBindAttribLocation(programHandle, 2, "inUVCoords"); 

glLinkProgram(programHandle); 
if(!validateProgram()) return false; 

и validateShader(GLuint) и validateProgram() функции:

bool Material::validateShader(GLuint shaderHandle) 
{ 
    char buffer[2048]; 
    memset(buffer, 0, 2048); 
    GLsizei len = 0; 

    glGetShaderInfoLog(shaderHandle, 2048, &len, buffer); 
    if(len > 0) 
    { 
     Logger::log("ve::Material::validateShader: Failed to compile shader - %s", buffer); 
     return false; 
    } 

    return true; 
} 

bool Material::validateProgram() 
{ 
    char buffer[2048]; 
    memset(buffer, 0, 2048); 
    GLsizei len = 0; 

    glGetProgramInfoLog(programHandle, 2048, &len, buffer); 
    if(len > 0) 
    { 
     Logger::log("ve::Material::validateProgram: Failed to link program - %s", buffer); 
     return false; 
    } 

    glValidateProgram(programHandle); 
    GLint status; 
    glGetProgramiv(programHandle, GL_VALIDATE_STATUS, &status); 
    if(status == GL_FALSE) 
    { 
     Logger::log("ve::Material::validateProgram: Failed to validate program"); 
     return false; 
    } 

    return true; 
} 

Каждый Material экземпляр имеет std::map из Mesh с, и получить визуализируется как так:

void Material::render() 
{ 
    if(loaded) 
    { 
     glUseProgram(programHandle); 

     for(auto it = mmd->uniforms.begin(); it != mmd->uniforms.end(); ++it) 
     { 
      GLint loc = glGetUniformLocation(programHandle, (const GLchar*)it->first); 

      switch(it->second.type) 
      { 
      case E_UT_FLOAT3: glUniform3fv(loc, 1, it->second.f32ptr); break; 
      case E_UT_MAT4: glUniformMatrix4fv(loc, 1, GL_FALSE, it->second.f32ptr); break; 
      default: break; 
      } 
     } 

     for(Mesh* m : mmd->objects) 
     { 
      GLint loc = glGetUniformLocation(programHandle, "modelMat"); 
      glUniformMatrix4fv(loc, 1, GL_FALSE, &m->getTransform()->getTransformMatrix()[0][0]); 
      m->render(); 
     } 
    } 
} 

it->second.f32ptr будет float указатель на &some_vec3[0] или &some_mat4[0][0]. Я вручную загружаю матрицу преобразования модели перед рендерингом, однако (которая является только матрицей вращения, класс Transform (возвращаемый Mesh :: getTransform()) будет выполнять только glm :: rotation(), поскольку я пытался выяснить проблема).

Наконец, Mesh делают код:

if(loaded) 
{ 
    glBindVertexArray(vao); 
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib); 
    glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_SHORT, 0); 
} 

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

ответ

1

По-видимому, если нормали вершин сетки неверны, то будут происходить странные артефакты вращения. В моем случае я преобразовал сетку в мою программу 3D моделирования (Blender) на 90 градусов по оси X, так как Blender использует ось z в качестве своей вертикальной оси, тогда как моя программа использует ось y как вертикальную ось. Однако метод, который я использовал для преобразования/поворота сетки в Blender в моем сценарии экспорта, неправильно преобразовывал нормали, а только позиции вершин. Без каких-либо предварительных преобразований программа работает должным образом. Первоначально я обнаружил, что нормали были неправильными, сравнивая нормализованные положения и нормали в симметричном объекте (я использовал куб со сглаженными нормалями) и увидел, что нормали повернуты. Спасибо @derhass и @Solkar за то, что они отвели меня на ответ.

Однако, если кто-то все еще хочет внести свой вклад, я хотел бы знать, почему нормали не вращаются по одной оси при умножении на матрицу вращения одной оси, даже если они неверны.

1

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

Что вы делаете, это просто инвертировать полную матрицу 4x4 и взять верхнюю левую подматрицу 3x3, что совершенно неверно.

Вы должны вычислить transpose(inverse(mat3(modelMat))), но вы действительно не должны делать это в шейдере, но calulate это совместный с модельной матрицей на CPU, чтобы избежать позволяя GPU вычисления довольно дорогой обращения матрицы на вершину.

+0

Благодарим за ответы, но я все еще в недоумении. Я изменил нормальную линию на 'vec3 normal = normalize (transpose (inverse (mat3 (modelMat))) * inNormal);' , но все же имеют похожие результаты с неправильными нормалями – user3764686

+0

@ user3764686: Ну, какое пространство вы хотите, чтобы ваши нормали в? Это не совсем понятно из вашего вопроса. Также неясно, правильны ли ваши нормали ввода. И вы также должны перенормировать их в FS, поскольку линейная интерполяция не сохраняет длину. – derhass

+0

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

1

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

В общем, это транспонированная обратная матрица, которая должна быть применена к нормалям, используя только правильную матрицу линейного преобразования 3x3, без части перевода, которая расширяет матрицу до 4x4.

Для поворота и равномерного масштабирования инверсная транспозиция идентична исходной матрице. Таким образом, матричные операции для инвертирования и транспонирования матриц необходимы только в том случае, если вы применяете другие типы преобразований, такие как неравномерное масштабирование или сдвиговые преобразования.

 Смежные вопросы

  • Нет связанных вопросов^_^