zoukankan      html  css  js  c++  java
  • 如何将外部的obj模型导入OpenGL

    作者:feiquan
    
    出处:http://www.cnblogs.com/feiquan/
    
    版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    
    大家写文都不容易,请尊重劳动成果~ 这里谢谢大家啦(*/ω\*)

    1.关于obj的说明。

      obj中存放的是顶点坐标信息(v),面的信息(f),法线(vn),纹理坐标(vt),以及材质(这个放在mtl)中

      我使用CINEMA 4D导出用VS查看后的信息:

      CINEMA 4D中的正方体:

      

      导出obj后的信息

      

      VS中查看;未标题4.obj

      

    # WaveFront *.obj file (generated by CINEMA 4D)
    
    mtllib ./未标题4.mtl
    
    v -100 -100 100
    v -100 100 100
    v 100 -100 100
    v 100 100 100
    v 100 -100 -100
    v 100 100 -100
    v -100 -100 -100
    v -100 100 -100
    # 8 vertices
    
    vn 0 0 1
    vn 1 0 0
    vn 0 0 -1
    vn -1 0 0
    vn 0 1 0
    vn 0 -1 0
    # 6 normals
    
    vt 0 0 0
    vt 0 1 0
    vt 1 1 0
    vt 1 0 0
    # 4 texture coordinates
    
    o 立方体
    usemtl default
    f 3/4/1 4/3/1 2/2/1 1/1/1
    f 5/4/2 6/3/2 4/2/2 3/1/2
    f 7/4/3 8/3/3 6/2/3 5/1/3
    f 1/4/4 2/3/4 8/2/4 7/1/4
    f 4/4/5 6/3/5 8/2/5 2/1/5
    f 5/4/6 3/3/6 1/2/6 7/1/6
    

      未标题4.mtl

      

    # WaveFront *.mtl file (generated by CINEMA 4D)
    
    newmtl default
    Kd 1 1 1
    

      这就是其中的信息。

      我们知道OpenGL中建立不规则模型可以通过点和三角面来创建。那么我们要做的就是从obj中导出点的三维信息和面的三维信息。

    2、obj中模型导出的设置。

      首先在三维软件中创建一个模型。这里我以一个人体模型演示(只以线框显示)。

      

      模型分析:

      ·我们可以看到这个模型的点面信息太多了,如果直接导出数据太多,处理时程序读取花费的时间太长,而且我们演示也不需要这种高模。

      ·还有就是,我们知道OpenGl中的运行后可视化界面的坐标是重-1到1,这里可以看到这个模型的尺寸太大,我们必须进行缩放。

      模型不是一个整体,分层太多,导出后各个模块的数据是分开的,所以必须在软件中对模型连接,使其变为一个整体。

      模型处理后:

      

      我感觉这样就满足我的要求,后期运行程序读取数据不会超过4分钟

      模型导出:注意一下以下地方就好,因为这里只要点(v),面(f)其他会干扰,也会使程序效率下降。导出后只有以下一个文件。

      

     3.数据处理

      将那个文件用VS打开后,可以看到里面的数据,这里我们要处理一下:

      VS虽然可以直接打开obj但是从这个里面是无法读取数据的。

      

      所以我们要新建一个txt文件,将里面的数据复制进来,然后把这个文件放在你新建的win32或MFC项目中,将先前的obj从工程中移除。

      

      在VS中查找“#”开头的,有以下两句话,然后直接删除这个是我用CINEMA 4D导出后自带的,会干扰程序的运行。

      

      

     4.编程

      分析:

      ¨分析这些数据,不难猜想obj文件由若干行组成,每行开始有一个字母,用来标注改行数据所表示的意思,v 应该是顶点,g应该是一个名字,观察到,f后紧跟着三个整数,由opengl中的顶点数组得到,他应该是三角行的三个顶点,后面的三个整数就代表着前端顶点的索引值.

    ¨好知道了数据结构,就可以提取分析obj文件啦
      
    class obj3dmodel{
        struct vertex{
            double x,y,z;
        };
        struct face{
            unsigned int v1,v2,v3;
        };
        vector<vertex>vertexs;
        vector<face>faces;
    
    public :
        void parse(const char *filename);
        void draw();
    };

      这里,我们定义两个结构体来表示顶点和三角形,并采用vector容器来保存数据组.

      ¨之所以把两个结构体定义在类的内部,一个是不想污染源代码,另一个是暂时还没有想到会在别的什么地方会用到他们.所以暂且把他们藏起来

    ¨我们同时为他定义了parse和draw方法,分别用来从文件中解析数据和利用opengl绘制

      

    void obj3dmodel::parse(const char *filename){
        string s;
        ifstream fin(filename);
        if(!fin)return;
        while(fin>>s){
            switch(*s.c_str()){
            case 'f':
            {
                face f;
                fin>>f.v1>>f.v2>>f.v3;
                cout<<"f      "<<f.v1<<"       "<<f.v2 <<"       "<<f.v3<<endl;
                faces.push_back(f);
            }
            break;
    
            case 'v':
            {
                vertex v;
                fin>>v.x>>v.y>>v.z;
                cout<<"v      "<<v.x<<"       "<<v.y<<"       "<<v.z<<"       "<<endl;
                this->vertexs.push_back(v);
            }
            break;
            case 'w':break;
            case 'x':break;
            case 'y':break;
            case 'z':break;
            case '#':break;
    
            default: 
                {}
            break;
            
            }
        }         
    }
    
    void obj3dmodel::draw(){
        glBegin(GL_TRIANGLES);
        for(int i=0;i<faces.size();i++){
            //下标减一
            float move_y=0.5; //控制模型的位置
            vertex v1=vertexs[faces[i].v1-1];
            vertex v2=vertexs[faces[i].v2-1];
            vertex v3=vertexs[faces[i].v3-1];
            glColor3f(0.3,0.5,0);
            glVertex3f(v1.x,v1.y-move_y,v1.z);
    
            glColor3f(1,1,0);
            glVertex3f(v2.x,v2.y-move_y,v2.z);
    
            glColor3f(0.5,0.5,0);
            glVertex3f(v3.x,v3.y-move_y,v3.z);
        }
        glEnd();
    }
      注意顶点索引方式,obj里面是以1为基数的,所以在索引时要将索引值减1.则会报越界.
      运行程序:
      
    5.运行结果:

    ·   

              低模                                                                高模

    源码提供:

      

    // 1.cpp : 定义控制台应用程序的入口点。
    //
    
    #include "stdafx.h"
    #include <GL/glut.h>
    #include<vector>//vector(向量): C++中的一种数据结构,确切的说是一个类.它相当于一个动态的数组,当程序员无法知道自己需要的数组的规模多大时,用其来解决问题可以达到最大节约空间的目的.
    #include <fstream>
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    GLfloat step=0.0,s=0.1,move[]={0,0,0};
    
    class obj3dmodel{
        struct vertex{
            double x,y,z;
        };
        struct face{
            unsigned int v1,v2,v3;
        };
        vector<vertex>vertexs;
        vector<face>faces;
    
    public :
        void parse(const char *filename);
        void draw();
    };
    
    void obj3dmodel::parse(const char *filename){
        string s;
        ifstream fin(filename);
        if(!fin)return;
        while(fin>>s){
            switch(*s.c_str()){
            case 'f':
            {
                face f;
                fin>>f.v1>>f.v2>>f.v3;
                cout<<"f      "<<f.v1<<"       "<<f.v2 <<"       "<<f.v3<<endl;
                faces.push_back(f);
            }
            break;
    
            case 'v':
            {
                vertex v;
                fin>>v.x>>v.y>>v.z;
                cout<<"v      "<<v.x<<"       "<<v.y<<"       "<<v.z<<"       "<<endl;
                this->vertexs.push_back(v);
            }
            break;
            case 'w':break;
            case 'x':break;
            case 'y':break;
            case 'z':break;
            case '#':break;
    
            default: 
                {}
            break;
            
            }
        }         
    }
    
    void obj3dmodel::draw(){
        glBegin(GL_TRIANGLES);
        for(int i=0;i<faces.size();i++){
            //下标减一
            float move_y=0.5;
            vertex v1=vertexs[faces[i].v1-1];
            vertex v2=vertexs[faces[i].v2-1];
            vertex v3=vertexs[faces[i].v3-1];
            glColor3f(0.3,0.3,0);
            glVertex3f(v1.x,v1.y-move_y,v1.z);
    
            glColor3f(1,1,0);
            glVertex3f(v2.x,v2.y-move_y,v2.z);
    
            glColor3f(0.5,0.5,0);
            glVertex3f(v3.x,v3.y-move_y,v3.z);
        }
        glEnd();
    }
    
    obj3dmodel obj;
    
    void myinit(){
        GLfloat light_ambient[]={0.3,0.2,0.5};
        GLfloat light_diffuse[]={1,1,1};
        GLfloat light_position[]={2,2,2,1};
        GLfloat light1_ambient[]={0.3,0.3,0.2};
        GLfloat light1_diffuse[]={1,1,1};
        GLfloat light1_position[]={-2,-2,-2,1};
        //灯光
        glLightfv(GL_LIGHT0,GL_AMBIENT,light_ambient);
        glLightfv(GL_LIGHT0,GL_DIFFUSE,light_diffuse);
        glLightfv(GL_LIGHT0,GL_POSITION,light_position);
        glLightfv(GL_LIGHT1,GL_AMBIENT,light1_ambient);
        glLightfv(GL_LIGHT1,GL_DIFFUSE,light1_diffuse);
        glLightfv(GL_LIGHT1,GL_POSITION,light1_position);
        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_TRUE);
        glEnable(GL_LIGHTING);
        glEnable(GL_LIGHT0);
        glEnable(GL_LIGHT1);
        //深度
        glDepthFunc(GL_LESS);
        glEnable(GL_DEPTH_TEST);
        //材质
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE );
        glEnable(GL_COLOR_MATERIAL);
    
    }
    
    void DrawColorBox(){
        obj.draw();
        //glutSolidTeapot(1);    
    }
    
    void display(){
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    
        /*s+=0.005;
        if(s>1.0)s=0.1;*/  //不进行缩放
    
        //位移
        //move[0]+=0.005;        //x
        //move[1]+=0.005;        //y
        //move[2]+=0.005;        //z
        //if(move[0]>2)move[0]=0;
        //if(move[1]>2)move[1]=0;
        //if(move[2]>2)move[2]=0;
    
        glPushMatrix();
        //glScalef(s,s,s);
        //glTranslatef(move[0],move[1],move[2]);
        glRotatef(step,0,1,0);
        //glRotatef(step,0,0,1);
        //glRotatef(step,1,0,0);
    
        DrawColorBox();
        glPopMatrix();
        glFlush();
        glutSwapBuffers();
    }
    
    void stepDisplay(){
        //旋转
        step=step+1;
        if(step>360)step=0;
    
        display();
    }
    
    void myReshape(GLsizei w,GLsizei h){
        glViewport(0,0,w,h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        if(w<=h){
            glOrtho(-1.5,1.5,-1.5*(GLfloat)h/(GLfloat)w,1.5*(GLfloat)h/(GLfloat)w,-10,10);
        }else{
            glOrtho(-1.5*(GLfloat)w/(GLfloat)h,1.5*(GLfloat)w/(GLfloat)h,-1.5,1.5,-10,10);
        }
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
    }
    
    void keyboard (unsigned char key,int x,int y){
        switch(key){
        case 27:exit(0);break;
        }
    }
    
    int _tmain(int argc, CHAR* argv[])
    {
        obj.parse("66.txt");
        //obj.parse("11.txt");
        glutInit(&argc,argv);
        glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB);
        glutInitWindowSize(500,400);
        glutCreateWindow("simple");
        myinit();
        glutReshapeFunc(myReshape);
        glutDisplayFunc(display);
        glutKeyboardFunc(keyboard);
        glutIdleFunc(stepDisplay);
        glutMainLoop();
        return 0;
    }

      

  • 相关阅读:
    演示使用string对象(续)
    P2216 [HAOI2007]理想的正方形 单调队列
    SP1805 HISTOGRA
    P4556 [Vani有约会]雨天的尾巴 树链剖分 线段树合并
    codeforces 600E 线段树合并
    HDU2197 本原串
    P3806 【模板】点分治1
    牛客10 Popping Balloons
    P3261 [JLOI2015]城池攻占 左偏树
    P4549 【模板】裴蜀定理
  • 原文地址:https://www.cnblogs.com/feiquan/p/8207407.html
Copyright © 2011-2022 走看看