1. 程式人生 > >非常簡單,教你用OpenGL讀入obj模型

非常簡單,教你用OpenGL讀入obj模型

最近在學習OpenGL如何讀入obj模型的時候,發現百度出來,相應的示例程式要麼跑不通、要麼較複雜,初學者的我想找到個簡單易懂的示例程式愣是沒找到。只好自己通過谷歌學習之後,寫了個簡單的示例程式,實現了基本的讀入obj模型的功能,希望對於初學OpenGL的朋友有一些幫助。

第一,配置VS2015 + OpenGL開發環境

  • 這裡我是參考的簡書作者@fan2b,直通車,這位作者寫的非常詳細,而且配置出來的程式能直接整個工程拷走執行,很贊

第二,開始正題

1、新建 ObjLoader.h
#include "Dependencies\glew\glew.h"
#include "Dependencies\freeglut\freeglut.h"
#include <vector> #include <string> using namespace std; class ObjLoader{ public: ObjLoader(string filename);//建構函式 void Draw();//繪製函式 private: vector<vector<GLfloat>>vSets;//存放頂點(x,y,z)座標 vector<vector<GLint>>fSets;//存放面的三個頂點索引 };
2、新建ObjLoader.cpp
#include "ObjLoader.h"
#include <fstream> #include <iostream> using namespace std; ObjLoader::ObjLoader(string filename) { string line; fstream f; f.open(filename, ios::in); if (!f.is_open()){ cout << "Something Went Wrong When Opening Objfiles" << endl; } while (!f.eof()){ getline(f, line);//拿到obj檔案中一行,作為一個字串
vector<string>parameters; string tailMark = " "; string ans = ""; line = line.append(tailMark); for (int i = 0; i < line.length(); i++) { char ch = line[i]; if (ch != ' ') { ans+=ch; } else { parameters.push_back(ans); //取出字串中的元素,以空格切分 ans = ""; } } //cout << parameters.size() << endl; if (parameters.size() != 4) { cout << "the size is not correct" << endl; } else { if (parameters[0] == "v") { //如果是頂點的話 vector<GLfloat>Point; for (int i = 1; i < 4; i++) { //從1開始,將頂點的xyz三個座標放入頂點vector GLfloat xyz = atof(parameters[i].c_str()); Point.push_back(xyz); } vSets.push_back(Point); } else if (parameters[0] == "f") { //如果是面的話,存放三個頂點的索引 vector<GLint>vIndexSets; for (int i = 1; i < 4; i++){ string x = parameters[i]; string ans = ""; for (int j = 0; j < x.length(); j++) { //跳過‘/’ char ch = x[j]; if (ch != '/') { ans += ch; } else { break; } } GLint index = atof(ans.c_str()); index = index--;//因為頂點索引在obj檔案中是從1開始的,而我們存放的頂點vector是從0開始的,因此要減1 vIndexSets.push_back(index); } fSets.push_back(vIndexSets); } } } f.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(); }
3、最後,新建main.cpp
#include "ObjLoader.h"
using namespace std;
//模型路徑
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檔案讀取,比如說頂點法向量和紋理的載入等,可在之基礎上進行改進。整個工程可在此下載
  • 執行效果:
    執行效果圖

許可協議

  • 商業用途轉載請聯絡 Chen.Jiayang [AT] foxmail.com