Cargar Modelo 3D Formato OBJ

En este punto sabemos cómo crear objetos 3D y cómo aplicarles una textura, sin embargo todo lo hemos hecho mediante código, por lo que a la hora de crear modelos 3D complejos esta práctica será bastante irritante, por lo general los modelos 3D y sus texturas son creados con la ayuda de software de modelado 3D como Blender, Maya, 3D Max y otros, estos modelos son almacenados en diferentes formatos (3DS, FBX, OBJ, etc.) por lo que en este tutorial veremos cómo podemos cargar en nuestra aplicación gráfica un modelo creado usando alguna de estas herramientas.

Formato Wabefront (.obj)


Este formato es capaz de almacenar información como: vértices, normales, texturas y materiales, en texto plano, por lo que podemos abrir el archivo .obj con un editor de texto como notepad.exe, al abrir el archivo veremos cómo está organizada la información.

Usando blender podemos crear un cubo con una textura y exportarlo en formato obj, para exportarlo vamos a file/export/wabefront (.obj)

blender obj opengl
Configuramos las opciones para exportar la información deseada (vértices, uv, normales), y para triangular el modelo, de esta manera trabajaremos con caras triangulares, este grupo de opciones hace nuestro archivo .obj bastante simple, ideal para comenzar a aprender.

obj formato simple
Del lado izquierdo vemos la configuración y en el derecho el archivo .obj de salida abierto con Visual Studio Code, se puede utilizar cualquier editor de texto plano, ejemplo: Notepad++, Sublime Text, etc.

Descripción del formato OBJ


En este formato cada línea representa un grupo de información, para saber de tipo de información se trata vemos el comienzo de la línea, si empieza por:
  • v se trata de un vértice y sus coordenadas x, y, z respectivamente.
  • vt indica las coordenadas UV de la textura.
  • vn almacena el vector normal en sus 3 componentes.
  • f establece los índices de vértices/textura/normal requeridos para formar cada uno de los triángulos que componen el modelo 3D.
Todo lo demás lo omitimos de momento, para este tutorial no lo utilizaremos, aunque debemos mencionar que aquellas líneas que empiezan por # son comentarios.

Cargar Archivo OBJ


Lo primero que requerimos es leer cada una de las líneas que conforman el archivo .obj, usaremos un ifstream para abrir el archivo y la función getline dentro de un bucle while para obtener cada una de las líneas.

void loadOBJ(string archivo) {
    ifstream file = ifstream(archivo);
    if (!file.is_open()) {
        cout << "No se ecuentra: " << archivo << endl;
    }
    //int count = 0;
    while (file) {
        string linea;
        getline(file, linea);
        //cout << ++count << " " << linea << endl;
    }
}

Lo siguiente será filtrar las líneas deseadas, las que empiezan por: (vt, vn, v, f), obtener cada uno de los valores y almacenarlos en las variables correspondientes.

if (start == "vt") {
    str >> v1 >> v2;
    temp_uv.push_back(glm::vec2(v1, v2));
}
else if (start == "vn") {
    str >> v1 >> v2 >> v3;
    temp_normal.push_back(glm::vec3(v1, v2, v3));
}
else if (start == "v") {
    str >> v1 >> v2 >> v3;
    temp_vertex.push_back(glm::vec3(v1, v2, v3));
}
else if (start == "f") {
    unsigned int v1, v2, v3, n1, n2, n3, uv1, uv2, uv3;

    // reemplazar el caracter "/" por " "
    std::replace_if(std::begin(linea), std::end(linea), [](const char& ch) { return ch == '/'; }, ' ');

    istringstream face_str(linea);
    face_str.ignore(linea.length(), ' ');

    face_str >> v1 >> uv1 >> n1 >> v2 >> uv2 >> n2 >> v3 >> uv3 >> n3;

    vertexIndices.push_back(v1);
    vertexIndices.push_back(v2);
    vertexIndices.push_back(v3);

    normalIndices.push_back(n1);
    normalIndices.push_back(n1);
    normalIndices.push_back(n3);

    uvIndices.push_back(uv1);
    uvIndices.push_back(uv2);
}

Para finalizar se necesita formar el modelo a partir de los índices indicados, dibujaremos cada triángulo de modelo por lo que necesitamos saber cómo se forma cada triángulo, las líneas que empiezan con la letra f nos dan los índices para vértices, normales y uv.

for (unsigned int i = 0; i < vertexIndices.size(); i++) {
    unsigned int vIndex = vertexIndices[i];
    unsigned int uvIndex = uvIndices[i];
    unsigned int nIndex = normalIndices[i];

    glm::vec3 temp_v = temp_vertex[vIndex - 1];
    glm::vec2 temp_u = temp_uv[uvIndex - 1];
    glm::vec3 temp_n = temp_normal[nIndex - 1];

    vertex.push_back(temp_v);
    uv.push_back(temp_u);
    normal.push_back(temp_n);
}

Cargamos la textura y creamos los buffers como lo hemos venido haciendo.

cubo textura opengl
La función de carga de modelos 3D en formato OBJ no proporciona acceso todas las características del formato, esta función fue diseñada de manera simplista para ser fácil de comprender, por lo que no será muy eficiente cargando archivos .obj de gran tamaño.

opengl model 3d
Modelo obtenido de TF3DM para usarlo con nuestra aplicación hemos cambiado la textura a formato BMP.

GitHub: Cargar Modelos 3D OBJ

Comentarios

  1. Muy buena tu descripción de la forma de utilizar e identificar la información de un obj, estoy tratando de fabricar un scanner o palpador en 3 dimenciones utilizando 2 mouse mecanivos, el programa que captura los puntos lo haré en visual basic 6.0 porcka facilidad de trabajo, y tú información me ha sido de mucha ayuda para entender como género un archivo que pueda abrir en zbrush o blender.... todavía me falta comprender mucho pero hay vamos, gracias

    ResponderEliminar

Publicar un comentario

Entradas populares de este blog

Conectar SQL Server con Java

Detección de rostros

Instalar OpenCV para Python en Windows