Обновить
Похоже, что мои нормали работают нормально, и это связано с тем, как я рисую свои лица (рисуется только половина), и я не могу понять, почему —
Если бы вы могли взглянуть на мой код ранее (показано ниже)
Оригинальный пост
В настоящее время я работаю над парсером / рендерером для типов файлов .obj. Я сталкиваюсь с проблемой с отображением нормальных векторов:
Без нормалей:
С нормалями:
По какой-то причине я не могу понять, почему только половина нормальных векторов оказывает влияние, а другая половина действует так, как будто лица вообще нет.
Вот мой код для загрузки в файл obj:
void ObjModel::Load(string filename){
ifstream file(filename.c_str());
if(!file) return;
stringstream ss;
string param, line;
float nparam, cur;
vector<vector<float> > coords;
vector<float> point;
while(getline(file, line)){
ss.clear();
ss.str(line);
ss >> param;
//vertex
if(param == "v"){
for(int i = 0; i < 3; i++){
ss >> nparam;
this->vertices.push_back(nparam);
}
}
//face
else if(param == "f"){
coords.clear();
point.clear();
for(int i = 0; i < 3; i++){
ss >> nparam;
nparam--;
for(int j = 0; j < 3; j++){
cur = this->vertices[nparam * 3 + j];
this->faces.push_back(cur);
point.push_back(cur);
}
coords.push_back(point);
}
point = this->ComputeNormal(coords[0], coords[1], coords[2]);
for(int i = 0; i < 3; i++) this->normals.push_back(point[i]);
}
else continue;
}
}
void ObjModel::Render(){
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &this->faces[0]);
glNormalPointer(GL_FLOAT, 0, &this->normals[0]);
glDrawArrays(GL_TRIANGLES, 0, this->faces.size() / 3);
glDisableClientState(GL_NORMAL_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
}
А вот функция для вычисления вектора нормалей:
vector<float> ObjModel::ComputeNormal(vector<float> v1, vector<float> v2, vector<float> v3){
vector<float> vA, vB, vX;
float mag;
vA.push_back(v1[0] - v2[0]);
vA.push_back(v1[1] - v2[1]);
vA.push_back(v1[2] - v2[2]);
vB.push_back(v1[0] - v3[0]);
vB.push_back(v1[1] - v3[1]);
vB.push_back(v1[2] - v3[2]);
vX.push_back(vA[1] * vB[2] - vA[2] * vB[1]);
vX.push_back(vA[2] * vB[0] - vA[0] * vB[2]);
vX.push_back(vA[0] * vB[1] - vA[1] * vB[0]);
mag = sqrt(vX[0] * vX[0] + vX[1] * vX[1] + vX[2] * vX[2]);
for(int i = 0; i < 3; i++) vX[i] /= mag;
return vX;}
Я уже проверил, чтобы убедиться, что есть одинаковое количество нормальных векторов и граней (которые должны быть, если я прав).
Заранее спасибо! 🙂
редактировать Вот как я включаю / отключаю функции OpenGL:
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
GLfloat amb_light[] = 0.1, 0.1, 0.1, 1.0 ;
GLfloat diffuse[] = {0.6, 0.6, 0.6, 1};
GLfloat specular[] = {0.7, 0.7, 0.3, 1};
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, amb_light);
glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse);
glLightfv(GL_LIGHT0, GL_SPECULAR, specular);
glEnable(GL_LIGHT0);
glEnable(GL_COLOR_MATERIAL);
glShadeModel(GL_SMOOTH);
glLightModeli(L_LIGHT_MODEL_TWO_SIDE, GL_FALSE);
glDepthFunc(GL_LEQUAL);
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glDisable(GL_CULL_FACE);
Вы используете элементы? Файлы Obj начинают считать с 1, а OpenGL начинает считать с 0. Просто вычтите 1 из каждого элемента, и вы получите правильный рендеринг.
Ориентация норм имеет значение. Похоже, что ориентация лица вашего объекта не соответствует, поэтому нормали соседних граней, с одинаковыми плоскостями, указывают в противоположных направлениях.
Если вы импортировали эту модель из файла модели, я советую вам не рассчитывать нормали в вашем коде — вы все равно не должны этого делать, так как художники могут вносить ручные корректировки в нормали для локально точной настройки освещения — но храните их в файл модели также. Все 3D моделисты имеют функцию, чтобы перевернуть нормали в общую ориентацию. Например, в Blender эта функция достигается с помощью горячей клавиши CTRL + N
в режиме редактирования.
for(int i = 0; i < 3; i++) this->normals.push_back(point[i]);
Это обеспечивает только одну нормаль для каждого лица. Вам нужен один нормальный для каждого вершина.