opengl導入obj模型


在經過查閱各種資料以及各種bug之后,終於成功的實現了導入基本的obj模型。

首相介紹一下什么是obj模型

一.什么是OBJ模型

obj文件實際上是一個文本文檔,主要有以下數據,一般可以通過blender軟件導出模型的obj文件。

在3d圖形處理中,一個模型(model)通常由一個或者多個Mesh(網格)組成,一個Mesh是可繪制的獨立實體。例如復雜的人物模型,可以分別划分為頭部,四肢等各個部分來建模,這些Mesh組合在一起最終形成人物模型。

obj的文本內容一般包括以下數據

usemtl和mtllib表示的材質相關數據,解析材質數據稍微繁瑣,本節我們只是為了說明加載模型的原理,不做討論。

o 引入一個新的object

v 表示頂點位置

vt 表示頂點紋理坐標

vn 表示頂點法向量

f 表示一個面,面使用1/2/8這樣格式,表示頂點位置/紋理坐標/法向量的索引,這里索引的是前面用v,vt,vn定義的數據 注意這里Obj的索引是從1開始的,而不是0

二 利用opengl導入obj模型

新建一個ObjLoader

1 class ObjLoader{
2 public:
3     ObjLoader(string filename);//構造函數
4     void Draw();//繪制函數
5 private:
6     vector<vector<GLfloat>>vSets;//存放頂點(x,y,z)坐標
7     vector<vector<GLint>>fSets;//存放面的三個頂點索引
8 };

一個構造函數ObjLoader::ObjLoader用於導入obj文本內容

Draw用於在opengl中繪制模型

vector<vector<GLfloat>>vSets;//存放頂點(x,y,z)坐標
vector<vector<GLint>>fSets;//存放面的三個頂點索引

ObjLoader::ObjLoader(string filename)
{
    std::ifstream file(filename);
    std::string line;
while (getline(file, line))
{
    if (line.substr(0, 2) == "vt")
    {

    }
    else if (line.substr(0, 2) == "vn")
    {

    }
    else if (line.substr(0, 1) == "v")
    {
        vector<GLfloat> Point;
        GLfloat x, y, z;
        std::istringstream s(line.substr(2));
        s >> x; s >> y; s >> z;
        Point.push_back(x);
        Point.push_back(y);
        Point.push_back(z);
        vSets.push_back(Point);

    }
    else if (line.substr(0, 1) == "f")
    {
        vector<GLint> vIndexSets;
        GLint u, v, w;
        std::istringstream vtns(line.substr(2));
        vtns >> u; vtns >> v; vtns>> w;
        vIndexSets.push_back(u-1);
        vIndexSets.push_back(v-1);
        vIndexSets.push_back(w-1);
        fSets.push_back(vIndexSets);
    }
    else if (line.substr(0, 1) == "#")
    {

    }
    else
    {

    }
}
file.close();
}



void ObjLoader::Draw(){

    glBegin(GL_TRIANGLES);//開始繪制
    for (int i = 0; i < fSets.size(); i++) {
        GLfloat VN[3];
        //三個頂點
        GLfloat SV1[3];
        GLfloat SV2[3];
        GLfloat SV3[3];

        if ((fSets[i]).size() != 3) {
            cout << "the fSetsets_Size is not correct" << endl;
        }
        else {
                GLint firstVertexIndex = (fSets[i])[0];//取出頂點索引
                GLint secondVertexIndex = (fSets[i])[1];
                GLint thirdVertexIndex = (fSets[i])[2];

                SV1[0] = (vSets[firstVertexIndex])[0];//第一個頂點
                SV1[1] = (vSets[firstVertexIndex])[1];
                SV1[2] = (vSets[firstVertexIndex])[2];

                SV2[0] = (vSets[secondVertexIndex])[0]; //第二個頂點
                SV2[1] = (vSets[secondVertexIndex])[1];
                SV2[2] = (vSets[secondVertexIndex])[2];

                SV3[0] = (vSets[thirdVertexIndex])[0]; //第三個頂點
                SV3[1] = (vSets[thirdVertexIndex])[1];
                SV3[2] = (vSets[thirdVertexIndex])[2];


                GLfloat vec1[3], vec2[3], vec3[3];//計算法向量
                //(x2-x1,y2-y1,z2-z1)
                vec1[0] = SV1[0] - SV2[0];
                vec1[1] = SV1[1] - SV2[1];
                vec1[2] = SV1[2] - SV2[2];

                //(x3-x2,y3-y2,z3-z2)
                vec2[0] = SV1[0] - SV3[0];
                vec2[1] = SV1[1] - SV3[1];
                vec2[2] = SV1[2] - SV3[2];

                //(x3-x1,y3-y1,z3-z1)
                vec3[0] = vec1[1] * vec2[2] - vec1[2] * vec2[1];
                vec3[1] = vec2[0] * vec1[2] - vec2[2] * vec1[0];
                vec3[2] = vec2[1] * vec1[0] - vec2[0] * vec1[1];

                GLfloat D = sqrt(pow(vec3[0], 2) + pow(vec3[1], 2) + pow(vec3[2], 2));

                VN[0] = vec3[0] / D;
                VN[1] = vec3[1] / D;
                VN[2] = vec3[2] / D;
           
                glNormal3f(VN[0], VN[1], VN[2]);//繪制法向量

                glVertex3f(SV1[0], SV1[1], SV1[2]);//繪制三角面片
                glVertex3f(SV2[0], SV2[1], SV2[2]);
                glVertex3f(SV3[0], SV3[1], SV3[2]);    
        }
    }
    glEnd();
}

然后我們寫一個主函數

//模型路徑
string filePath = "../data/monkey.obj";

ObjLoader objModel = ObjLoader(filePath);
//實現移動鼠標觀察模型所需變量
static float c = 3.1415926 / 180.0f;
static float r = 1.0f;
static int degree = 90;
static int oldPosY = -1;
static int oldPosX = -1;

//安置光源
void setLightRes() {
    GLfloat lightPosition[] = { 0.0f, 0.0f, 1.0f, 0.0f };
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosition);
    glEnable(GL_LIGHTING); //啟用光源
    glEnable(GL_LIGHT0);   //使用指定燈光
}

//初始化
void init() {
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutCreateWindow("ObjLoader");
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_SMOOTH);
    setLightRes();
    glEnable(GL_DEPTH_TEST);
}

void display()
{
    glColor3f(1.0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_MODELVIEW);                            
    glLoadIdentity();                                     
    glTranslatef(0.0f, 0.0f, -5.0f);                                                              
    setLightRes();
    glPushMatrix();

    gluLookAt(r*cos(c*degree), 0, r*sin(c*degree), 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);


    objModel.Draw();//繪制obj模型
    glPopMatrix();
    glutSwapBuffers();
}

void reshape(int width, int height)
{
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0f, (GLdouble)width / (GLdouble)height, 1.0f, 200.0f);
    glMatrixMode(GL_MODELVIEW);
}

//移動鼠標360觀察模型
void moseMove(int button, int state, int x, int y)
{
    if (state == GLUT_DOWN) {
        oldPosX = x; oldPosY = y;
    }
}
void changeViewPoint(int x, int y)
{
    int temp = x - oldPosX;
    degree += temp;
    oldPosX = x;
    oldPosY = y;
}

void myIdle()
{
    glutPostRedisplay();
}

int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMouseFunc(moseMove);
    glutMotionFunc(changeViewPoint);
    glutIdleFunc(myIdle);
    glutMainLoop();
    return 0;
}

這份代碼只適合比較簡單的obj文件,針對更為復雜的obj文件讀取,比如說頂點法向量和紋理的載入等,可在這基礎上進行改進。

運行效果:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM