OpenGL - obj文件的导入

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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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
plain
copy

  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/

时间: 2024-10-23 05:15:20

OpenGL - obj文件的导入的相关文章

openGL加载obj文件+绘制大脑表层+高亮染色

绘制大脑表层并高亮染色的工作是以openGL加载obj文件为基础的,这里是我们用到的原始程序:只能加载一个obj文件的demo. 然而,一个完整的大脑表层是由很多分区组成的,因此我们的程序需要支持两个功能: 同时加载多个obj文件. 每个大脑分区obj文件保持其相对位置. 明白了需求后,我们就可以开始修改代码了~ glmUnitize函数的作用是单位化,也就是把模型通过平移和缩放变换限制到3维坐标系中点为中心的一个单位正方体区域内.所以控制obj显示位置是在glmUnitize()函数中,源代码

[计算机图形学] OpenGL读取obj文件并显示其3D效果

? 读取三维网格模型(Wavefront OBJ文件) 无法向立方体:cube.obj 有法向兔子模型:bunny.obj 有法向有纹理八字模型:Eight.obj OBJ文件的格式可参考:http://www.cnblogs.com/youthlion/archive/2013/01/21/2870451.html ? 利用OpenGL显示该模型的绘制效果(全部) 顶点显示 线条显示 面片显示 ? 核心代码说说 1.下面的点.纹理.法向量.面用于构成一个PIC的类,PIC用于存储从OBJ文件中

OBJ文件中面的连接

面的连接点是按顺时针排列或逆时针排列,将决定面的法线方向(面的反正).例如:"f 1 2 3 4"面的法线向外,"f 4 3 2 1"面的法线向里. 面的连接点顺序错误,是导致导入模型产生碎面的一个重要原因.一个面不能出现两个以 上相同的顶点,这也是检查OBJ文件出错的一个要点. 例如:"f 1 2 3 4 3",有两个相同的顶点,索引号是3.一个面出现两个相同顶点,可能造成程序的内存分配错误.

visual stdio 安装OpenGL库文件

1.将下载的压缩包解开,将得到5个文件 1. 将glut解压出来,将其中的glut.h复制到C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include\gl下,若gl目录不存在,则新建.(在这种情况下头文件为#include <gl/glut.h>,我是直接放在C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\include下,所以头文件#include <glu

java实现文件批量导入导出实例(兼容xls,xlsx)

1.介绍 java实现文件的导入导出数据库,目前在大部分系统中是比较常见的功能了,今天写个小demo来理解其原理,没接触过的同学也可以看看参考下. 目前我所接触过的导入导出技术主要有POI和iReport,poi主要作为一些数据批量导入数据库,iReport做报表导出.另外还有jxl类似poi的方式,不过貌似很久没跟新了,2007之后的office好像也不支持,这里就不说了. 2.POI使用详解 2.1 什么是Apache POI? Apache POI是Apache软件基金会的开放源码函式库,

poi excel文件的导入

使用poi来实现excel文件的导入导出.使用struts2来做处理. 首先看jsp页面: index.jsp: <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <%@ taglib uri="/struts-tags" prefix="s"%> <!DOCTYPE HTML PUBLIC

MsSqlServer bak文件数据导入

MsSqlServer  bak文件数据导入 第一步首先在你的数据库中建立一个空数据库 选中新建的数据库 鼠标右键 任务 还原 数据库 这个时候会弹出这样的一个框 之后选择原设备 会弹出 点击添加 找到你的bak文件选择 这时弹出 Ok

JAVA文件夹导入到Eclipse中方法:

将JAVA文件夹导入到Eclipse中方法:方法一: 直接将java文件夹复制,然后粘贴到项目下:方法二:1.打开eclipse,点击项目的空白处,选择import:2.选择Existing Project into Workspace,点击next,找到下载的java代码,导入到eclipse中. 选到你要导入的文件,然后Finish就可以了

linux 编译C语言代码后产生OBJ文件的方法

如果你不指定编译成什么文件,gcc默认一步到位,直接生成可执行文件你可以试试以下几个参数 -c 只激活预处理,编译,和汇编,也就是他只把程序做成obj文件 例子用法: gcc -c hello.c 他将生成.o的obj文件 -S 只激活预处理和编译,就是指把文件编译成为汇编代码. 例子用法 gcc -S hello.c 他将生成.s的汇编代码,你可以用文本编辑器察看 -E 只激活预处理,这个不生成文件,你需要把它重定向到一个输出文件里面. 例子用法: gcc -E hello.c > piano