http://blog.csdn.net/silangquan/article/details/9707347

Obj文件简介

OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模型之间的互导,也可以通过Maya读写。比如你在3dsMax或LightWave中建了一个模型,想把它调到Maya里面渲染或动画,导出OBJ文件就是一种很好的选择。目前几乎所有知名的3D软件都支持OBJ文件的读写,不过其中很多需要通过插件才能实现。

有一款开源的3D编辑,渲染软件,叫Blender,通过Blender建模,OpenGL导入,就可以更好的发挥想象,通过Linux+Blender+OpenGL这一套完全的开源平台,创造属于自己的3D世界。

OBJ文件导出

首先需要在系统中安装Blender,软件中心搜一下就可以找到了,2.62版本。也可以到官网下载最新的2.68版本。

安装好之后打开软件,已经建好了一个cubic,鼠标右键可以选定并且拖动模型,中键用于变换视角,滚轮用于放大和缩小视图。

选中方块之后按delete把它删掉,因为这个模型太简单了,之所以要用三维建模软件,就是为了应付复杂的模型,如果只是一个cubic,直接画一下就出来了。

接下来创建一个猴脸!

Add -> Mesh -> Monkey

File -> Export -> .obj

下面的选项一定要注意!

改一下名字,命名为monkey.obj, 回车~。

用gedit打开看一下:

# Blender v2.62 (sub 0) OBJ File: 'quit.blend'
# www.blender.org
o Monkey
v 0.576885 0.756494 -0.239630
v -0.298115 0.756494 -0.239630
v 0.639385 0.678369 -0.169318
v -0.360615 0.678369 -0.169318

... ...

vn 0.223706 0.722743 0.653910
vn -0.223706 0.722743 0.653910
vn -0.153610 0.967743 0.199693
... ...

f 96//81 152//81 150//81
f 153//82 99//82 97//82
f 153//82 97//82 151//82
f 98//83 100//83 154//83

#表示注释

v    :顶点的坐标

vn :顶点法向量

f    :面,96//81 152//81 150//81 指的是面的三个顶点,96指面的第一个顶点指的是前面定义的第96号顶点,81指的是第81个法向量,后面的两个顶点类推。

注:老版本的blender导出来的obj文件会有些许的不同,最好查看好obj的文件内容之后再进行解析!

简单的Parser

obj文件格式非常简单,用c++的vector可以很好的处理变长数组的问题。

[cpp] view plaincopy
  1. bool loadOBJ(
  2. const char * path,
  3. std::vector<glm::vec3> & out_vertices,
  4. std::vector<glm::vec2> & out_uvs,
  5. std::vector<glm::vec3> & out_normals
  6. ){
  7. printf("Loading OBJ file %s...\n", path);
  8. std::vector<unsigned int> vertexIndices, uvIndices, normalIndices;
  9. std::vector<glm::vec3> temp_vertices;
  10. std::vector<glm::vec2> temp_uvs;
  11. std::vector<glm::vec3> temp_normals;
  12. FILE * file = fopen(path, "r");
  13. if( file == NULL ){
  14. printf("Impossible to open the file ! Are you in the right path ? See Tutorial 1 for details\n");
  15. return false;
  16. }
  17. while( 1 ){
  18. char lineHeader[128];
  19. // read the first word of the line
  20. int res = fscanf(file, "%s", lineHeader);
  21. if (res == EOF)
  22. break; // EOF = End Of File. Quit the loop.
  23. // else : parse lineHeader
  24. if ( strcmp( lineHeader, "v" ) == 0 ){
  25. cout<<"Get v"<<endl;
  26. glm::vec3 vertex;
  27. fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z );
  28. temp_vertices.push_back(vertex);
  29. }else if ( strcmp( lineHeader, "vt" ) == 0 ){
  30. cout<<"Get vt"<<endl;
  31. glm::vec2 uv;
  32. fscanf(file, "%f %f\n", &uv.x, &uv.y );
  33. uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders.
  34. temp_uvs.push_back(uv);
  35. }else if ( strcmp( lineHeader, "vn" ) == 0 ){
  36. cout<<"Get vn"<<endl;
  37. glm::vec3 normal;
  38. fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z );
  39. temp_normals.push_back(normal);
  40. }else if ( strcmp( lineHeader, "f" ) == 0 ){
  41. cout<<"Get f"<<endl;
  42. std::string vertex1, vertex2, vertex3;
  43. unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
  44. int matches = fscanf(file, "%d//%d %d//%d %d//%d\n", &vertexIndex[0], &normalIndex[0], &vertexIndex[1], &normalIndex[1], &vertexIndex[2], &normalIndex[2]);
  45. if (matches != 6){
  46. printf("File can't be read by our simple parser :-( Try exporting with other options\n");
  47. return false;
  48. }
  49. vertexIndices.push_back(vertexIndex[0]);
  50. vertexIndices.push_back(vertexIndex[1]);
  51. vertexIndices.push_back(vertexIndex[2]);
  52. normalIndices.push_back(normalIndex[0]);
  53. normalIndices.push_back(normalIndex[1]);
  54. normalIndices.push_back(normalIndex[2]);
  55. }else{
  56. // Probably a comment, eat up the rest of the line
  57. char stupidBuffer[1000];
  58. fgets(stupidBuffer, 1000, file);
  59. }
  60. }
  61. // For each vertex of each triangle
  62. for( unsigned int i=0; i<vertexIndices.size(); i++ ){
  63. // Get the indices of its attributes
  64. unsigned int vertexIndex = vertexIndices[i];
  65. unsigned int normalIndex = normalIndices[i];
  66. // Get the attributes thanks to the index
  67. glm::vec3 vertex = temp_vertices[ vertexIndex-1 ];
  68. glm::vec3 normal = temp_normals[ normalIndex-1 ];
  69. // Put the attributes in buffers
  70. out_vertices.push_back(vertex);
  71. out_normals .push_back(normal);
  72. }
  73. return true;
  74. }

这里只处理顶点和法向量,uv坐标再议。

下面的代码可以用来加载monkey.obj , 并且打印坐标信息。

[cpp] view plaincopy
  1. // Read our .obj file
  2. vector<glm::vec3> vertices;
  3. vector<glm::vec2> uvs;
  4. vector<glm::vec3> normals;
  5. bool res = loadOBJ("monkey.obj", vertices, uvs, normals);
  6. cout<<vertices.size()<<endl;
  7. for(int i=0; i<vertices.size(); i++)
  8. {
  9. cout << vertices[i].x << " " << vertices[i].y << " " << vertices[i].z<<endl;
  10. }

运行效果就想这样;

说明格式解析正确了。

安装glm库

由于C/C++标准库中没有几何数学库,这样造成在开发一个三维系统之初往往都需要自行实现一个实用的几何数学库,这样太费时费力了。Boost中有几何数学库,不过就这几行代码要用boost似乎有点牛刀小用了....GLM的出现可以很好的解决这个问题。
      GLM设计上遵照OpenGL Shading Language风格,使用开放的MIT授权协议。会GLSL的人可以很快上手。因采用了数据结构与函数方法分离的方式,可以很容易扩充函数方法而不改变原文件(增加新的头文件即可,不过得在不同的头文件中找函数方法比较费力)。

顺便提一句,glm还可以很好的集成到cuda中去。

可以去官网( http://glm.g-truc.net/)下载最新的源码,无需编译,解压之后直接将里面的glm文件夹拷贝到/usr/local/include 目录下面就可以使用了。

官网可能被墙掉了,我自己上传了一份到csdn,点我下载。

封装代码

之前的一些代码都是用C来完成,因为代码的没几行,但现在我觉得需要花点时间来重构一下代码了,改用面向对象,我用的是CodeBlocks来构建项目的,觉得不想用IDE的可以用CMake的。

首先来看一下项目结构:

cgl 是对OpenGL的封装,csdl是对SDL的封装,util则是一个工具类。简单看一下头文件。

cgl.h

[cpp] view plaincopy
  1. #ifndef CGL_H
  2. #define CGL_H
  3. #include <GL/glew.h>
  4. #include <GL/gl.h>
  5. #include <GL/glu.h>
  6. #include <SDL/SDL.h>
  7. #include "util.h"
  8. class CGL
  9. {
  10. public:
  11. CGL();
  12. CGL(int _width, int _height);
  13. virtual ~CGL();
  14. bool initGL();
  15. bool resizeGL(int width,int height);
  16. bool renderGL();
  17. protected:
  18. private:
  19. int width;
  20. int height;
  21. };
  22. #endif // CGL_H

csdl.h

[cpp] view plaincopy
  1. #ifndef SDL_H
  2. #define SDL_H
  3. #include <SDL/SDL.h>
  4. #include <GL/glew.h>
  5. #include <GL/gl.h>
  6. #include <GL/glu.h>
  7. class CSDL
  8. {
  9. public:
  10. CSDL();
  11. CSDL(int width, int height, int bpp, int flags);
  12. virtual ~CSDL();
  13. void init(Uint32 flags);
  14. void quit(int code);
  15. void toggle_fullscreen();
  16. void handleKeyEvent( SDL_keysym* keysym );
  17. void handleEvent();
  18. void setCaption(char *);
  19. protected:
  20. private:
  21. int screen_width;
  22. int screen_height;
  23. int screen_bpp;
  24. SDL_Surface *screen;
  25. //Whether the window is windowed or not
  26. bool windowed;
  27. //Whether the window is fine
  28. bool windowOK;
  29. };
  30. #endif // SDL_H

util.h

[cpp] view plaincopy
  1. #ifndef UTIL_H
  2. #define UTIL_H
  3. #include <glm/glm.hpp>
  4. #include <vector>
  5. #include <string>
  6. #include <cstring>
  7. #include <iostream>
  8. using namespace std;
  9. class Util
  10. {
  11. public:
  12. Util();
  13. virtual ~Util();
  14. bool loadOBJ(const char * path,std::vector<glm::vec3> & out_vertices,std::vector<glm::vec2> & out_uvs,std::vector<glm::vec3> & out_normals);
  15. char *textFileRead(char *fn);
  16. protected:
  17. private:
  18. };
  19. #endif // UTIL_H

cpp就不贴了,太长。

这样,main.cpp就非常简洁了~

[cpp] view plaincopy
  1. #include <iostream>
  2. #include "csdl.h"
  3. #include "cgl.h"
  4. using namespace std;
  5. const int SCREEN_WIDTH = 800;
  6. const int SCREEN_HEIGHT =800;
  7. const int SCREEN_BPP = 32;
  8. int main()
  9. {
  10. CGL *gl = new CGL(SCREEN_WIDTH, SCREEN_HEIGHT);
  11. // Color depth in bits of our window.
  12. int flags= SDL_OPENGL|SDL_RESIZABLE;
  13. CSDL *sdl = new CSDL(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,flags, gl);
  14. sdl->setCaption("Load obj in OpenGL");
  15. gl->initGL();
  16. while(true) {
  17. sdl->handleEvent(gl);
  18. gl->renderGL();
  19. }
  20. return 0;}

需要修改渲染内容的话,直接修改cgl.cpp中的代码就可以了,要添加功能函数就在util中添加,而sdl类基本就不用修改了。
花了一个晚上了用面向对象的方式重构代码,这个时间是值得的——代码结构变得非常清晰,健壮性和可读性都有很大的提升,调试起来也更加方便。 
接下来就要在OpenGL中导入obj文件了。
首先修改一下initGL函数,设置一下灯光和材质,同时导入obj文件。

[cpp] view plaincopy
  1. bool CGL::initGL()
  2. {
  3. float ratio = width / height;
  4. // Our shading model--Gouraud (smooth).
  5. glShadeModel( GL_SMOOTH );
  6. // Set the clear color.
  7. glClearColor( 0, 0, 0, 0 );
  8. // Setup our viewport.
  9. glViewport( 0, 0, width, height );
  10. glEnable(GL_DEPTH_TEST);
  11. //Change to the projection matrix and set our viewing volume.
  12. glMatrixMode( GL_PROJECTION );
  13. glLoadIdentity();
  14. gluPerspective( 60.0, ratio, 1.0, 100.0 );
  15. GLfloat light_position[] = {13.0, 13.0, 13.0, 0.0};
  16. GLfloat white_light[] = {1.0, 0.0, 0.0, 1.0};
  17. GLfloat lmodel_ambient[] = {0.1, 0.1, 0.1, 1.0};
  18. glLightfv(GL_LIGHT0,GL_POSITION,light_position);
  19. glLightfv(GL_LIGHT0,GL_DIFFUSE,white_light);
  20. glLightfv(GL_LIGHT0,GL_SPECULAR,white_light);
  21. GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
  22. GLfloat mat_shininess[] = {50.0};
  23. glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);
  24. glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess);
  25. glLightModelfv(GL_LIGHT_MODEL_AMBIENT,lmodel_ambient);
  26. glEnable(GL_LIGHTING);
  27. glEnable(GL_LIGHT0);
  28. Util util;
  29. //Read .obj file and store the infos
  30. bool res = util.loadOBJ("monkey.obj", vertices, uvs, normals);
  31. return( true );
  32. }

接下来就可以进行渲染了:

[cpp] view plaincopy
  1. glBegin(GL_TRIANGLES);
  2. for(i = 0; i<nodesSize; i++)
  3. {
  4. glNormal3f(normals[i].x, normals[i].y, normals[i].z);
  5. glVertex3f( vertices[i].x, vertices[i].y, vertices[i].z);
  6. }
  7. glEnd();

实际就是不断地绘制三角形...

当然,你也可以去一些网站下载一些模型来载入~搜 Blender model

代码下载

SDL C++ 编程框架 - http://download.csdn.net/detail/qp120291570/5856783

工程代码 - http://download.csdn.net/detail/qp120291570/5856935

参考:

《OpenGL编程指南》示例笔记(1)--渲染光照球体 - http://blog.csdn.net/norains/article/details/5684011
Model loading - http://www.opengl-tutorial.org/beginners-tutorials/tutorial-7-model-loading/

OpenGL - obj文件的导入相关推荐

  1. 3Dmax中obj文件的导入导出(以3Dmax2017版为例)

    3Dmax中obj文件的导入导出 1.文件的导入 打开3Dmax,点击左上角的图标,选择导入. 选择要导入的obj文件,导入,完成. 文件的导出 点击左上角图标,选择导出,导出选定对象. 选择要导出的 ...

  2. [OpenGL]导入obj文件

    通常来说我们构建一个模型是比较复杂的工作,那么我们还有什么途径获得模型呢.其中一种方法就是导入obj模型,不要被这个名词给吓到了,其实就是把一个制作好的模型保存到一个文件中,我们称为obj文件. 先上 ...

  3. opengl关于obj文件相关知识

    首先看一下obj文件格式: obj文件中可以有v(顶点位置).vt(顶点纹理坐标).vn(顶点法向量).f(面)开头的四种类型数据. 所有的obj文件中都有v型数据.格式如下: v 0.437500 ...

  4. 【OpenGL】八、初始化 OpenGL 渲染环境 ( 导入 OpenGL 头文件 | 链接 OpenGL 库 | 将窗口设置为 OpenGL 窗口 | 设置像素格式描述符 | 渲染绘制 ) ★

    文章目录 一.导入 OpenGL 的两个头文件 二.链接 OpenGL 库 三.将 Windows 桌面窗口改成 OpenGL 窗口 四.获取窗口设备 五.设置像素格式描述符 六.设置像素格式 七.创 ...

  5. 【我的OpenGL学习进阶之旅】关于3D模型知识之:什么是obj文件和mtl文件

    文章目录 一.学习3D模型的背景 二.3D模型效果展示 三.好奇3D模型文件是啥内容? 3.1 打开.obj文件 3.2 打开.obj文件 3.3 在外部使用查看3D模型的软件打开.obj文件 3.3 ...

  6. zbrush导入obj模型不显示_ZBrush中如何导入和导出OBJ文件—ZBrush教程

    原标题:ZBrush中如何导入和导出OBJ文件-ZBrush教程 ZBrush中如何导入和导出OBJ文件 ZBrush软件中对于文件的导出与储存格式是多样的.OBJ格式是如何导入和导出ZBrush的, ...

  7. C++/OpenGL 入门(18):读取obj文件并贴图

    来源:<Computer Graphics Programming in OpenGL Using C++ >by V Scott Gordon John L Clevenger 内容:程 ...

  8. ANSYS APDL学习(2):如何将obj文件或stl文件导入ANSYS APDL

    参考网址:ANSYS APDL经典界面导入网格文件_femap的博客-CSDN博客_ansys导出网格文件 这个网站说明,可以通过 Workbench 读取stl文件,然后导入APDL中 将obj转s ...

  9. 用 3ds Max Script 同时导入多个.obj文件

    一. 用 3ds Max Script 同时导入多个.obj文件 MAXScript -> New Script, 将下列代码拷贝, 修改路径: ctrl + s 保存该script文件: MA ...

最新文章

  1. PHP开发工具 zend studio
  2. java方法6_6.1 JAVA方法入门
  3. dede单独显示查到了多少条记录的方法
  4. 六十八、快速幂算法、牛顿迭代法、累加数组+二分查找的变形
  5. [十一]SpringBoot 之 添加JSP支持
  6. SAP Spartacus CurrentProductService.getProduct方法的实现原理
  7. oracle之数据处理之视图练习
  8. 我感到很惭愧的飞鸽传书
  9. linux下的tuxedo开发实例
  10. 8192 oracle,ORA-39095: 转储文件空间已耗尽: 无法分配 8192 字节
  11. python写机器人插件_从Python写入机器人框架控制台
  12. Apache禁止指定的user_agent、指定目录被访问
  13. KNN——K nearest neighbor
  14. PyTorch - torch.nn.PReLU
  15. 应届生软件测试个人简历模板,软件测试实习生个人简历模板.doc
  16. Pointer Network指针网络
  17. # 杂谈偶感 × 基于QFD方法的质量屋构建
  18. 用Python写随机密码生成
  19. 更换网站logo,超简单方法
  20. 设定行车路线实验matlab,避障最优路径系统研究

热门文章

  1. greenplum gpfdist工具
  2. C库函数中一些常用的常量值
  3. 手写百度页面问题小结
  4. 2个百万年薪工作同时到手,选程序员还是飞行员?网友都眼红了
  5. offsetof获取结构体的偏移
  6. 【C语言】深度探索offsetof,解析结构体的成员数组和指针
  7. Java打印输出:委托代销
  8. 电声乐器的演奏特征与制作技巧-----(1)打击乐篇
  9. Jolin的10句经典告白
  10. 微信小程序学习笔记(一)入门