openGL绘图基本框架

openGL绘图入门和导入外部文件

本文主要介绍通用绘图软件openGL的数据类型和基本的绘图框架,此外还提供了导入obj外部文件的方法,提供的代码稍作修改即可使用,希望能方便初学者快速上手。

openGL的数据类型

openGL绘图基本框架

 

 openGL的程序结构

openGL绘图基本框架

 

 

openGL的基本绘图框架

#include <GL/glut.h>
#include <cstdlib>

void main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 在创建窗口的时候,指定其显示模式的类型(包括颜色模式和缓冲区类型) ,设置GLUT_DEPTH才能显示3D图形
    glutInitWindowPosition(50, 100); // 设置窗口显示的初始位置
    glutInitWindowSize(400, 300); // 设置窗口的宽和高
    glutCreateWindow("窗口的名字"); // 创建窗口
    init(); // 执行你自己编写的初始化函数
    glutDisplayFunc(myDisplay); // 回调函数,显示图像,参数是你自己编写的显示函数
    glutMainLoop(); // 显示并且等待后续的命令
}

void init(void)//初始化设置
{
    glClearColor(1.0, 1.0, 1.0, 0.0); // 指定颜色缓冲区的清除值,设置窗口的背景颜色
    //投影的变换也常常被写到reshape里面,init中就不用
    //画二维的图像
    glMatrixMode(GL_PROJECTION); // 设置矩阵模式
    //画二维图像,用二维正投影
    gluOrtho2D(0.0, 200.0, 0.0, 150.0);
    //画三维的图像,设置视椎体,开启深度测试
    //为了显示 3D 图形,需要启用 GLUT 窗口程序的深度缓冲区(GLUT_DEPTH),深度测试遮挡剔除(GL_DEPTH_TEST),并设置观察裁剪空间
    glFrustum(-2.5, 2.5, -2.5, 2.5, mynear, myfar);
    glEnable(GL_DEPTH_TEST);
}

void myDisplay(void)//显示函数
{
    //设置颜色,R,G,B在0到1
    glColor3f(0.0, 0.4, 0.2);
    //在显示函数里面调用执行画图操作的函数
    myGraphy();
    //刷新当前缓冲区
    glFlush();
}

void myGraphy()
{
    //画点,不可以直接用glVertex2i,这应该只是一个定位函数,不会显示
    glBegin(GL_POINTS);
    glVertex2i(xCoord, yCoord);
    glEnd();
    //画线,设置两个端点的位置
    glBegin(GL_LINES);
        glVertex2i(180, 15); 
        glVertex2i(10, 145); 
    glEnd();
}

//为了防止变形可以用reshape函数进行设置,把init里面的一些初始化设置移到reshape里面,在main函数中init()的后面调用glutReshapeFunc(myReshape);
void myReshape(int w, int h)
{
    winWidth = w;
    winHeight = h;
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-5.0, 5.0, -5.0, 5.0, mynear, myfar);
    viewxform_z = -5.0;
    glMatrixMode(GL_MODELVIEW);
}

 导入外部文件

首先介绍一下OBJ文件的格式

OBJ是一种3D模型文件,因此不包含动画、贴图路径、动力学等信息,且主要支持由多边形面片绘制成的模型。OBJ文件不包含面的颜色和材质定义信息,材质库信息储存在后缀是".mtl"的独立文件中。

obj文件的导入分为两个阶段,首先解析文件,也就是按文件的格式保存相应的参数,然后再根据这些参数绘制多边形面片,这些面片拼接成了3D模型。

1.OBJ文件解析:原理就是读取文件中的多边形面片的相关数据并存储在数组中方便绘制模型时使用。文件中顶点通过“v”进行标识,格式为“v x y z”。其中x,y,z分别表示三个维度的坐标值。面对象由“f”标识,可以由三角形或者四边形构成。解析代码如下:

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 && parameters.size() != 5) {
            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;
                if (parameters.size() == 5)
                {
                    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);
                }
                else
                {
                    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();
}

 

2.多边形面片的绘制:网上的有些源代码把所有的“f”标识的语句都默认成了只含三个参数,导致只能绘制三角形面片,无法呈现出完整的模型。其实可以有三个或者四个参数,用三角形或者四边形面片来绘制,对代码进行修改后绘出了完整模型。

void ObjLoader::Draw() {
    for (int i = 0; i < fSets.size(); i++) {
        GLfloat VN[3];
        //三个顶点
        GLfloat SV1[4];
        GLfloat SV2[4];
        GLfloat SV3[4];
        GLfloat SV4[4];

        if ((fSets[i]).size() != 4&& (fSets[i]).size() != 3) {
            cout << "the fSetsets_Size is not correct" << endl;
        }
        else {
            if ((fSets[i]).size() == 3)
            {
                glBegin(GL_TRIANGLES);//开始绘制三角形
                glColor3f(0.255, 0.215, 0);
                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();
            }
            else
            {
               
                glBegin(GL_POLYGON);
                glColor3f(0.5, 0.5, 0);
                GLint firstVertexIndex = (fSets[i])[0];//取出顶点索引
                GLint secondVertexIndex = (fSets[i])[1];
                GLint thirdVertexIndex = (fSets[i])[2];
                GLint fourthVertexIndex = (fSets[i])[3];

                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];
                
                SV4[0] = (vSets[fourthVertexIndex])[0]; //第四个顶点
                SV4[1] = (vSets[fourthVertexIndex])[1];
                SV4[2] = (vSets[fourthVertexIndex])[2];

                glVertex3f(SV1[0], SV1[1], SV1[2]);//绘制四边形面片
                glVertex3f(SV2[0], SV2[1], SV2[2]);
                glVertex3f(SV3[0], SV3[1], SV3[2]);
                glVertex3f(SV4[0], SV4[1], SV4[2]);
                glEnd();
            }
        }
    }   
}

效果展示:

openGL绘图基本框架

 

 

github源代码:ursulalujun/CG-openGL (github.com)

这个仓库里还有几何变换,观察变换,鼠标、键盘交互等其他操作的源代码,欢迎大家访问!

参考文章:

https://blog.csdn.net/qq_38906523/article/details/75210105

上一篇:Android 开发者计划近半年的跳槽,面试四家,斩获3家Offer


下一篇:使用3ds Max和3D Exploration将Solidworks模型导入Opengl