Проблема с добавлением нормалей к .obj мешу

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

Экспорт содержит только вершины, нормали и грани. В соответствии с форматом .obj: я добавляю вершины в сетку и использую предоставленные индексы для рисования граней. Нормалы и индексы для нормалей поставляются аналогичным образом. Как показано на рисунке ниже, я не правильно их применяю.

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

Вот мой скрипт загрузчика модели:

modelLoader.h

#ifndef _WAVEFRONTLOADER
#define _WAVEFRONTLOADER

#include "ofMain.h"
class waveFrontLoader {

public:

ofMesh mesh;

waveFrontLoader();
~waveFrontLoader();

void loadFile(char *fileName);
ofMesh generateMesh();

private:

typedef struct
{
ofIndexType v1,v2,v3;
ofIndexType vn1,vn2,vn3;
}
Index;

std::vector<ofVec3f> vertices;
std::vector<ofVec3f> normals;
std::vector<Index> indices;

void parseLine(char *line);
void parseVertex(char *line);
void parseNormal(char *line);
void parseFace(char *line);
};

#endif

modelLoader.cpp

#include "waveFrontLoader.h"
waveFrontLoader::waveFrontLoader()
{

}

void waveFrontLoader::loadFile(char *fileName)
{
ifstream file;
char line[255];

//open file in openframeworks data folder
file.open(ofToDataPath(fileName).c_str());

if (file.is_open())
{
while (file.getline(line,255))
{
parseLine(line);
}
}
}

void waveFrontLoader::parseLine(char *line)
{
//If empty, don't do anything with it
if(!strlen(line))
{
return;
}

//get line type identifier from char string
char *lineType = strtok(strdup(line), " ");

//parse line depending on type
if (!strcmp(lineType, "v")) // Vertices
{
parseVertex(line);
}
else if (!strcmp(lineType, "vn")) // Normals
{
parseNormal(line);
}
else if (!strcmp(lineType, "f")) // Indices (Faces)
{
parseFace(line);
}
}

void waveFrontLoader::parseVertex(char *line)
{
float x;
float y;
float z;

vertices.push_back(ofVec3f(x,y,z));

//get coordinates from vertex line and assign
sscanf(line, "v %f %f %f", &vertices.back().x, &vertices.back().y, &vertices.back().z);
}

void waveFrontLoader::parseNormal(char *line)
{
float x;
float y;
float z;

normals.push_back(ofVec3f(x,y,z));

//get coordinates from normal line and assign
sscanf(line, "vn %f %f %f", &normals.back().x, &normals.back().y, &normals.back().z);
}

void waveFrontLoader::parseFace(char *line)
{
indices.push_back(Index());

//get vertex and normal indices
sscanf(line, "f %d//%d %d//%d %d//%d",
&indices.back().v1,
&indices.back().vn1,
&indices.back().v2,
&indices.back().vn2,
&indices.back().v3,
&indices.back().vn3);
}

ofMesh waveFrontLoader::generateMesh()
{
//add vertices to mesh
for (std::vector<ofVec3f>::iterator i = vertices.begin(); i != vertices.end(); ++i)
{
mesh.addVertex(*i);
}

//add indices to mesh
for (std::vector<Index>::iterator i = indices.begin(); i != indices.end(); ++i)
{
// -1 to count from 0
mesh.addIndex((i->v1) - 1);
mesh.addIndex((i->v2) - 1);
mesh.addIndex((i->v3) - 1);

mesh.addNormal(normals[(i->vn1) - 1]);
mesh.addNormal(normals[(i->vn2) - 1]);
mesh.addNormal(normals[(i->vn3) - 1]);

}

return mesh;
}

waveFrontLoader::~waveFrontLoader()
{

}

Я также попытался добавить такие нормали (имеет смысл, так как это одна норма на лицо):

mesh.addNormal(normals[(i->vn1) - 1]);

Я также попытался добавить нормали только один раз на два нарисованных треугольника и попытался добавить индексы и нормали перед индексами. Ни то, ни другое не сработало.

testApp.h

#pragma once

#include "ofMain.h"#include "waveFrontLoader.h"
class testApp : public ofBaseApp{

public:
void setup();
void update();
void draw();
void exit();

waveFrontLoader *objectLoader;
ofMesh mesh;
ofEasyCam camera;
ofLight light;
};

testApp.cpp

#include "testApp.h"
//--------------------------------------------------------------
void testApp::setup()
{
glEnable(GL_DEPTH_TEST);
glEnable(GL_LIGHTING);

ofBackground(10, 10, 10);

camera.setDistance(10);
light.setPosition(10,30,-25);

objectLoader = new waveFrontLoader();
objectLoader->loadFile("test.obj");
mesh = objectLoader->generateMesh();
}

//--------------------------------------------------------------
void testApp::update()
{

}

//--------------------------------------------------------------
void testApp::draw()
{
camera.begin();
light.enable();
mesh.draw();
light.disable();
camera.end();
}

void testApp::exit()
{
delete objectLoader;
}

3

Решение

Я думаю, что это может быть ошибкой индексации из-за того, что индексы вашей позиции и нормальные массивы не соответствуют. Обычно при создании буферов вершин с помощью OpenGL все массивы атрибутов вершин (позиция, нормаль, текскорд и т. Д.) Должны иметь одинаковую длину. Поэтому, когда треугольник определяется как индексы [0, 1, 2], он будет использовать позиции [v0, v1, v2] и нормали [n0, n1, n2].

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

Файл куба .obj будет содержать:

- 8 positions, lets call them v0 to v7
- 6 normals, lets call them n0 to n5
- 12 faces/triangles of format v//n v//n v//n, called f0 to f11

В вашем коде generateMesh () вы должны представить массив вершин:

[ v0, v1, v2, v3, v4, v5, v6, v7 ] // length of 8

индексный массив из:

[ f0.a, f0.b, f0.c, .... f11.a, f11.b, f11.c ] // length of 36

и нормальный массив

[ n0, n0, n0, n0, n0, n0, n1, n1, n1, ... n5, n5, n5 ] // length of 36.

В этом примере значения индекса треугольника будут варьироваться от [0 до 7] для позиций и от [0 до 5] для нормалей. Это работает для ваших отправленных вершин, но ваши отправленные нормали варьируются от [0 до 31].

Попробуйте сгенерировать свой ofMesh, используя следующий код, который собирает объединенные массивы вершин с соответствующими вершинами и нормальными индексами:

ofMesh waveFrontLoader::generateMesh()
{
int indexCount = 0;
for (std::vector<Index>::iterator i = indices.begin(); i != indices.end(); ++i)
{
// add face of positions, -1 to count from 0
mesh.addVertex(vertices[(i->v1) - 1]);
mesh.addVertex(vertices[(i->v2) - 1]);
mesh.addVertex(vertices[(i->v3) - 1]);

// add face of normals, -1 to count from 0
mesh.addNormal(normals[(i->vn1) - 1]);
mesh.addNormal(normals[(i->vn2) - 1]);
mesh.addNormal(normals[(i->vn3) - 1]);

// in this code we are defining our vertex arrays
// according to the indices, so they will always
// be [0 to n]
mesh.addIndex( indexCount++ );
mesh.addIndex( indexCount++ );
mesh.addIndex( indexCount++ );
}
}

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

Более сложным подходом было бы использовать std :: map или std :: set для проверки, если комбинация position + normal + etc уже существует, и использовать эти существующие индексы вместо добавления избыточных данных в массив. В примере с кубом это приведет к тому, что первые две грани будут индексами [0, 1, 2, 1, 2, 3], использующими 4 вершины, а не индексами [0, 1, 2, 3, 4, 5], использующими 6 вершин,

1

Другие решения

С тем, как вы читаете файл, вам совсем не нужны никакие индексы. Вы копируете все вершины и нормали для каждой загружаемой грани (треугольника). Так что индексы не нужны, вы можете просто использовать вершины в последовательности для рисования. Если бы вы использовали OpenGL напрямую, вы могли бы использовать glDrawArrays(GL_TRIANGLES, ...) вместо glDrawElements(GL_TRIANGLES, ...), Вероятно, есть эквивалентный вариант с фреймворком, который вы используете.

Для эффективной визуализации модели вы захотите использовать общие вершины и использовать индексные буферы. Вам необходимо создать вершину OpenGL для каждой уникальной комбинации положения и нормали. Вот два из моих предыдущих ответов по теме, которые более подробно иллюстрируют, как это работает:

OpenGL — индекс буферизует трудности

Почему мой OBJ-парсер рендерит меши так?

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector