OpenGL study notes: model transformation, view transformation, projection transformation, viewport transformation, operation matrix stack

1. Model transformation and view transformation 
From the point of view of "relative movement", changing the position and direction of the observation point is equivalent to changing the position and direction of the object itself. In OpenGL, the same function is even used to implement these two functions. Since the transformation of model and view is realized by matrix operation, before transformation, the matrix of current operation should be set as "model view matrix".

The way to set it is to call the glMatrixMode function as a parameter of GL_MODELVIEW , like this: 

glMatrixMode(GL_MODELVIEW);
Usually, we need to set the current matrix to the identity matrix before doing the transformation.

This also requires only one line of code: 

glLoadIdentity();
Then, model transformation and view transformation can be performed.
Model and view transformations mainly involve three functions: 
glTranslate* , which multiplies the current matrix with a matrix representing the moving object. The three parameters represent the displacement values ​​on the three coordinates respectively. 
glRotate* Multiplies the current matrix by a matrix representing the rotated object. The object will rotate counterclockwise around the line from (0,0,0) to (x,y,z). The parameter angle represents the angle of rotation.
glScale* , which multiplies the current matrix by a matrix representing the scaled object. x, y, z represent the scaling in that direction, respectively. 
Note that I always say "multiply by XX" instead of saying "this function is rotation" or "this function is movement" for a reason, which will be covered in a moment. Assuming that the current matrix is ​​the identity matrix, then multiply by a matrix R representing rotation, then multiply by a matrix T representing movement, and then multiply the resulting matrix by the coordinate matrix v of each vertex. Therefore, the transformed vertex coordinates are ((RT)v). Due to the associative ratio of matrix multiplication, ((RT)v) = (R(Tv)), in other words, the move is actually done first, followed by the rotation. That is: the order of the actual transformations is the reverse of the order written in the code. Since the results obtained by "move first and then rotate" and "rotate and then move" are likely to be different, you need to pay special attention to this when you are beginners. The reason why OpenGL is designed this way is to achieve higher efficiency. But when drawing complex three-dimensional graphics, it is also very painful to think about how to reverse the transformation every time. Here is another way of thinking that can make the code look more natural (the code written is actually the same, but the method used to think about the problem is different). Let's imagine that the coordinates are not fixed. When rotating, the coordinate system rotates with the object. When moving, the coordinate system moves with the object. In this way, there is no need to consider the problem of reversing the order of the code.

The above are all introduced for changing the position and orientation of objects. If you want to change the position of the observation point, in addition to using the glRotate* and glTranslate* functions, you can also use this function: gluLookAt . It has many parameters. The first three parameters represent the position of the observation point, the middle three parameters represent the position of the observation target, and the last three parameters represent the range from (0, 0, 0) to (x, y, z). A straight line, which represents what the observer perceives as the "up" direction.

2. Projection transformation 
Projection transformation is to define a visible space, and objects outside the visible space will not be drawn on the screen. (Note that the coordinates can no longer be -1.0 to 1.0 from now on!) 

OpenGL supports two types of projection transformations, perspective projection and orthographic projection. Projection is also implemented using matrices. If you need to manipulate the projection matrix, you need to call the glMatrixMode function with GL_PROJECTION as the parameter .

glMatrixMode(GL_PROJECTION);

Usually, we need to set the current matrix to the identity matrix before doing the transformation. 

glLoadIdentity();

Perspective projection produces results that are similar to photographs, with effects that are large near and far and small, such as a photo of a railroad track looking forward inside a locomotive, where the two rails appear to meet at a distance. Use the glFrustum function to set the current visible space to the perspective projection space. The meaning of its parameters is as follows:


Disclaimer: The picture is from www.opengl.org, which is a picture of the book "OpenGL Programming Guide". Since the old version of the book (first edition, 1994) has been circulated on the Internet, I hope that the copyright issue has not been touched .

The more common gluPerspective function can also be used. The meaning of its parameters is as follows:


Disclaimer: The picture is from www.opengl.org, which is a picture of the book "OpenGL Programming Guide". Since the old version of the book (first edition, 1994) has been circulated on the Internet, I hope that the copyright issue has not been touched .  
Orthographic projection is equivalent to the result observed at infinity, it is only an ideal state. But for computers, it's possible to get better performance with orthographic projection. 

Use the glOrtho function to set the current visible space to the orthographic space. The meaning of its parameters is as follows:


Disclaimer: The picture is from www.opengl.org, which is a picture of the book "OpenGL Programming Guide". Since the old version of the book (first edition, 1994) has been circulated on the Internet, I hope that the copyright issue has not been touched . 
If the drawing space itself is two-dimensional, gluOrtho2D can be used. His usage is similar to glOrgho.

3. Viewport transformation 

When everything is in place, just draw the pixels to the screen. There's one last question left at this point: to which area of ​​the window should the pixels be drawn? Normally, the default is to fill the entire window completely, but we can fill only half of it. (ie: fill the entire image to half of the window)


Disclaimer: The picture is from www.opengl.org, which is a picture of the book "OpenGL Programming Guide". Since the old version of the book (first edition, 1994) has been circulated on the Internet, I hope that the copyright issue has not been touched . 
Use glViewport to define the viewport. The first two parameters define the bottom left foot of the viewport (0,0 means the bottom left), and the last two parameters are the width and height, respectively.

4. The operation of the matrix stack 
is an introductory tutorial, so let’s briefly introduce the stack first. You can think of a stack as a stack of plates. There are no plates at the beginning, you can put them one by one or take them down one by one. Every time it is removed, it is the last plate that was put on it. Usually, when a computer implements a stack, the capacity of the stack is limited, and if there are too many plates, errors will occur. Of course, if there are no more plates, asking for another plate will also go wrong. 
When we perform matrix operations, it may be necessary to save a matrix first and restore it after a period of time. When we need to save, call the glPushMatrix function, which is equivalent to putting the matrix (equivalent to a plate) on the stack. When the last save needs to be restored, the glPopMatrix function is called, which is equivalent to taking the matrix off the stack. OpenGL specifies that the stack capacity can hold at least 32 matrices, and in some OpenGL implementations, the stack capacity actually exceeds 32. So don't worry too much about the capacity of the matrix. 
Usually, it is more convenient and faster to use this measure of saving first and then restoring than first transforming and then inverse transforming. 

Note: Modelview matrices and projection matrices have corresponding stacks. Use glMatrixMode to specify whether the current operation is on the modelview matrix or the projection matrix.

5. Examples

Based on the rotation relationship of the sun, the earth and the moon, I have personally expanded the book "OpenGL Introductory Tutorial (fine)" and added the animation function, press "f" on the keyboard to time day++, press "b" "Then the time day--.

Source code:

/**
 * "OpenGL Introductory Tutorial"
 * The sun, earth and moon turn and turn
 */
#include <GL/glut.h>

/* Initialize material properties, light properties, lighting model, open depth buffer */
void init ( void )
{
    GLfloat mat_specular [ ] = { 1.0, 1.0, 1.0, 1.0 };
    GLfloat mat_shininess [ ] = { 50.0 };
    GLfloat light_position [ ] = { 1.0, 1.0, 1.0, 0.0 };

    glClearColor ( 0.0, 0.0, 0.0, 0.0 );
    glShadeModel ( GL_SMOOTH );

    glMaterialfv ( GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv ( GL_FRONT, GL_SHININESS, mat_shininess);
    glLightfv ( GL_LIGHT0, GL_POSITION, light_position);
    
    glEnable (GL_LIGHTING);
    glEnable (GL_LIGHT0);
    glEnable (GL_DEPTH_TEST);
}

// 12 months a year, a total of 360 days
static int day = 100;
// change of day: from 0 to 359
void myDisplay(void)
{
    glEnable(GL_DEPTH_TEST);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(75, 1, 1, 400000000);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt (0, -200000000, 200000000, 0, 0, 0, 0, 0, 1);
    // draw the red "sun"
    glColor3f(1.0f, 0.0f, 0.0f);
    glutSolidSphere(69600000, 20, 20);
    // draw the blue "earth"
    glColor3f(0.0f, 0.0f, 1.0f);
    glRotatef(day/360.0*360.0, 0.0f, 0.0f, -1.0f);
    glTranslatef(150000000, 0.0f, 0.0f);
    glutSolidSphere(15945000, 20, 20);
    // draw the yellow "moon"
    glColor3f(1.0f, 1.0f, 0.0f);
    glRotatef(day/30.0*360.0 - day/360.0*360.0, 0.0f, 0.0f, -1.0f);
    glTranslatef(38000000, 0.0f, 0.0f);
    glutSolidSphere(4345000, 20, 20);
    glFlush();
}
/* ARGSUSED1 */
void keyboard(unsigned char key, int x, int y)
{
    switch (key) {
      case 'f':
         day+=1;
            /* set a function */
         glutPostRedisplay();
         break;
      case 'b':
         day-=1;
            /* set a function */
         glutPostRedisplay();
         break;
      case 27:
         exit(0);  /*  Escape key  */
         break;
      default:
         break;
   }
}

int main(int argc, char *argv[])
{
    /*initialization*/
    glutInit(&argc, argv);
    /* Set display mode: RGB color, single buffer */
    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
    /* Set to the center of the window */
    glutInitWindowPosition (100, 100);
    /* set window size */
    glutInitWindowSize (400, 400);
    /* create window */
    glutCreateWindow("Sun Earth Moon");
    /* Call OpenGL initialization function */
    init ( );
    glutKeyboardFunc (&keyboard);
    glutDisplayFunc(&myDisplay);  
    /* Do a message loop */
    glutMainLoop();
    return 0;
}

My running environment:

CentOS7
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-16)
OpenGL implementation vendor's name: VMware, Inc.
Renderer identifier: Gallium 0.4 on llvmpipe (LLVM 3.9, 256 bits)
Version number of OpenGL implementation: 2.1 Mesa 17.0.1
OGLU tool library version: 1.3

Compile and run:

$ make
gcc test.c -lGL -lglut -lGLU -lXmu -Bstatic -Bdyanmic -lm
./a.out

Effect picture:


Part of the code and content are from "OpenGL Introductory Tutorial (fine)", if there is any infringement, please contact to delete it.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325853008&siteId=291194637