Basic framework of openGL drawing

Posted by benny_g on Thu, 13 Jan 2022 08:17:36 +0100

Getting started with openGL drawing and importing external files

This paper mainly introduces the data type and basic drawing framework of the general drawing software openGL. In addition, it also provides the method of importing obj external files. The provided code can be used with a little modification. I hope it can be convenient for beginners to get started quickly.

Data type of openGL

 

Program structure of openGL

 

 

Basic drawing framework of openGL

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

void main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // When creating a window, specify the type of its display mode(Includes color modes and buffer types) ,set up GLUT_DEPTH To display 3 D graphical
    glutInitWindowPosition(50, 100); // Sets the initial position of the window display
    glutInitWindowSize(400, 300); // Sets the width and height of the window
    glutCreateWindow("The name of the window"); // create a window
    init(); // Execute your own initialization function
    glutDisplayFunc(myDisplay); // Callback function, display image, parameters are your own display function
    glutMainLoop(); // Displays and waits for subsequent commands
}

void init(void)//Initialization settings
{
    glClearColor(1.0, 1.0, 1.0, 0.0); // Specifies the clear value of the color buffer and sets the background color of the window
    //The transformation of projection is also often written reshape Inside, init You don't have to
    //Draw a two-dimensional image
    glMatrixMode(GL_PROJECTION); // Set matrix mode
    //Draw a two-dimensional image with two-dimensional orthographic projection
    gluOrtho2D(0.0, 200.0, 0.0, 150.0);
    //Draw a three-dimensional image, set the visual vertebral body, and open the depth test
    //To display 3 D Graphics, need to be enabled GLUT Depth buffer of window program( GLUT_DEPTH),Depth test occlusion culling( GL_DEPTH_TEST),And set the viewing clipping space
    glFrustum(-2.5, 2.5, -2.5, 2.5, mynear, myfar);
    glEnable(GL_DEPTH_TEST);
}

void myDisplay(void)//Display function
{
    //Set color, R,G,B Between 0 and 1
    glColor3f(0.0, 0.4, 0.2);
    //Call the function that performs drawing operation in the display function
    myGraphy();
    //Flush current buffer
    glFlush();
}

void myGraphy()
{
    //Draw points. You can't use them directly glVertex2i,This should only be a positioning function and will not be displayed
    glBegin(GL_POINTS);
    glVertex2i(xCoord, yCoord);
    glEnd();
    //Draw a line and set the position of the two endpoints
    glBegin(GL_LINES);
        glVertex2i(180, 15); 
        glVertex2i(10, 145); 
    glEnd();
}

//To prevent deformation, you can use reshape Function to set init Some initialization settings are moved to reshape Inside, in main In function init()Call after 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);
}

Import external files

First, introduce the format of OBJ file

OBJ is a 3D model file, so it does not contain animation, mapping path, dynamics and other information, and mainly supports models drawn from polygonal patches. The OBJ file does not contain face color and material definition information, and the material library information is stored in a separate file with ". mtl" suffix.

The import of obj file is divided into two stages. First, analyze the file, that is, save the corresponding parameters according to the file format, and then draw polygon patches according to these parameters, which are spliced into 3D models.

1.OBJ file parsing: the principle is to read the relevant data of polygon patches in the file and store it in the array for easy use when drawing the model. Vertices in the file are identified by "v" in the format of "v x y z". Where x, y and z respectively represent the coordinate values of the three dimensions. The face image is marked by "f" and can be composed of triangles or quadrangles. The parsing code is as follows:

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);//Get obj A line in the file as a string
        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); //Take out the elements in the string and divide them with spaces
                ans = "";
            }
        }
        //cout << parameters.size() << endl;
        if (parameters.size() != 4 && parameters.size() != 5) {
            cout << "the size is not correct" << endl;
        }
        else {
            if (parameters[0] == "v") {   //If it's a vertex
                vector<GLfloat>Point;
                for (int i = 1; i < 4; i++) {   //Starting from 1, set the vertex's xyz Put the three coordinates into the vertex vector
                    GLfloat xyz = atof(parameters[i].c_str());
                    Point.push_back(xyz);
                }
                vSets.push_back(Point);
            }

            else if (parameters[0] == "f") {   //If it is a face, the index of three vertices is stored
                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++) {   //skip'/'
                            char ch = x[j];
                            if (ch != '/') {
                                ans += ch;
                            }
                            else {
                                break;
                            }
                        }
                        GLint index = atof(ans.c_str());
                        index = index--;//Because the vertex index is obj The file starts with 1, and we store vertices vector It starts from 0, so subtract 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++) {   //skip'/'
                            char ch = x[j];
                            if (ch != '/') {
                                ans += ch;
                            }
                            else {
                                break;
                            }
                        }
                        GLint index = atof(ans.c_str());
                        index = index--;//Because the vertex index is obj The file starts with 1, and we store vertices vector It starts from 0, so subtract 1
                        vIndexSets.push_back(index);
                    }
                    fSets.push_back(vIndexSets);                      
                }
            }
        }
    }
    f.close();
}

 

2. Polygon patch drawing: some source codes on the Internet default all statements marked with "f" to contain only three parameters, resulting in only triangular patches and unable to present a complete model. In fact, it can have three or four parameters, which are drawn with triangular or quadrilateral patches. After modifying the code, the complete model is drawn.

void ObjLoader::Draw() {
    for (int i = 0; i < fSets.size(); i++) {
        GLfloat VN[3];
        //Three vertices
        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);//Start drawing triangles
                glColor3f(0.255, 0.215, 0);
                GLint firstVertexIndex = (fSets[i])[0];//Extract vertex index
                GLint secondVertexIndex = (fSets[i])[1];
                GLint thirdVertexIndex = (fSets[i])[2];

                SV1[0] = (vSets[firstVertexIndex])[0];//First vertex
                SV1[1] = (vSets[firstVertexIndex])[1];
                SV1[2] = (vSets[firstVertexIndex])[2];

                SV2[0] = (vSets[secondVertexIndex])[0]; //Second vertex
                SV2[1] = (vSets[secondVertexIndex])[1];
                SV2[2] = (vSets[secondVertexIndex])[2];

                SV3[0] = (vSets[thirdVertexIndex])[0]; //Third vertex
                SV3[1] = (vSets[thirdVertexIndex])[1];
                SV3[2] = (vSets[thirdVertexIndex])[2];


                GLfloat vec1[3], vec2[3], vec3[3];//Calculate normal vector
                //(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]);//Draw normal vector
                glVertex3f(SV1[0], SV1[1], SV1[2]);//Draw a triangular patch
                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];//Extract vertex index
                GLint secondVertexIndex = (fSets[i])[1];
                GLint thirdVertexIndex = (fSets[i])[2];
                GLint fourthVertexIndex = (fSets[i])[3];

                SV1[0] = (vSets[firstVertexIndex])[0];//First vertex
                SV1[1] = (vSets[firstVertexIndex])[1];
                SV1[2] = (vSets[firstVertexIndex])[2];

                SV2[0] = (vSets[secondVertexIndex])[0]; //Second vertex
                SV2[1] = (vSets[secondVertexIndex])[1];
                SV2[2] = (vSets[secondVertexIndex])[2];

                SV3[0] = (vSets[thirdVertexIndex])[0]; //Third vertex
                SV3[1] = (vSets[thirdVertexIndex])[1];
                SV3[2] = (vSets[thirdVertexIndex])[2];
                
                SV4[0] = (vSets[fourthVertexIndex])[0]; //Fourth vertex
                SV4[1] = (vSets[fourthVertexIndex])[1];
                SV4[2] = (vSets[fourthVertexIndex])[2];

                glVertex3f(SV1[0], SV1[1], SV1[2]);//Draw Quad patch
                glVertex3f(SV2[0], SV2[1], SV2[2]);
                glVertex3f(SV3[0], SV3[1], SV3[2]);
                glVertex3f(SV4[0], SV4[1], SV4[2]);
                glEnd();
            }
        }
    }   
}

Effect display:

 

 

github source code: ursulalujun/CG-openGL (github.com)

This warehouse also has the source code of geometric transformation, observation transformation, mouse, keyboard interaction and other operations. Welcome to visit!

Reference article:

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