OpenGL学习进程(10)第七课:四边形绘制与动画基础

    本节是OpenGL学习的第七个课时,下面以四边形为例介绍绘制OpenGL动画的相关知识:


    (1)绘制几种不同的四边形:

    1)四边形(GL_QUADS)

      OpenGL的GL_QUADS图元用于绘制四边形,它根据每四个顶点绘制一个四边形。

      注意:在使用四边形时必需记住四边形的四个角必须位于同一个平面中(不存在弯曲的四边形)。

    2)四边形带(GL_QUAD_STRIP)

  该图元指定一个连接的四边形带。它们都保持相同方向的环绕。

      3)通用多边形GL_POLYGON

      我们可以用它绘制任意数量的多边形。与四边形一样,多边形的所有顶点也必须位于同一平面中。如果想越过这个规则,可以采用一种变通的方法,使用GL_TRIANGLE_FAN代替GL_POLYGON。

      4)多边形的创建规则

      1.所有的多边形都必须是平面的。也就是说,多边形的所有顶点必须位于同一个平面中。在空间中,多边形不能扭曲或弯曲。

      2.多边形的边必须不相交,多边形必须是凸的。

      OpenGL施加这些限制的原因:为了使用一些非常快速的算法对多边形进行渲染

      5)细分和边界

      尽管OpenGL只能绘制凸多边形,但我们仍然能够创建非凸的多边形,那就是把两个或多个多边形排列在一起。

  我们使用OpenGL命令glPolygonMode可以把绘图模式切换到线框模式,这样就能看到组成较大表面区域的每一个较小的三角形。另外,我们还可以通过glEdgeFlag命令来通知OpenGL哪些线段属于边界线(围绕形状边缘的直线),哪些线段不属于边界线(形状内部的直线),这些形状内部的直线将不可见。这个功能通过使用win API的函数glEdgeFlag()完成。

      void glEdgeFlag( GLboolean flag);

      Each vertex of a polygon, separate triangle, or separate quadrilateral specified between a glBegin/glEnd pair is marked as the start of either a boundary or nonboundary edge. If the current edge flag is true when the vertex is specified, the vertex is marked as the start of a boundary edge. Otherwise, the vertex is marked as the start of a nonboundary edge. glEdgeFlag sets the edge flag bit to GL_TRUE if flag is GL_TRUE and to GL_FALSE otherwise.

      glEdgeFlag (GLboolean flag)表示一个顶点是否应该被认为是多边形的一条边界边的起点。如果当前flag值为true,这个节点就被标记为边界的起点。否则,该节点就被标记为非边界的起点。

      flag : Specifies the current edge flag value, either GL_TRUE or GL_FALSE. The initial value is GL_TRUE.

      flag : 指定当前边的标记值,为GL_TRUE或GL_FALSE。初始值为GL_TRUE。flag为GL_TRUE后面的点都被认为是边界上的点,flag为GL_FALSE则之后的点不是边界上的点。

    (2)实例:

    1)用凸边形堆砌凹边形:

#include <GL/glut.h>
#pragma comment(linker,"/subsystem:"windows" /entry:"mainCRTStartup"")
#define false 0
#define true  1
void SetupRC()
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glColor3f(.0f, 0.0f, 1.0f);
}

void RenderScene()
{
    glClear(GL_COLOR_BUFFER_BIT);
    // OpenGL命令,使用线框模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    //总体图形是由6个三角形堆砌而成的凹边形
    glBegin(GL_TRIANGLES);
    //第一个三角形作为实例讲解:在设为true的顶点都可以作为凹边形边界的起点,所以有两条直线BC,CA。没有AB直线的原因是A不能作为线的起点。
    glEdgeFlag(false);
    glVertex2f(-20.0f, 0.0f);//A
    glEdgeFlag(true);
    glVertex2f(20.0f, 0.0f);//B
    glVertex2f(0.0f, 40.0f);//C

    glVertex2f(-20.0f, 0.0f);
    glVertex2f(-60.0f, -20.0f);
    glEdgeFlag(false);
    glVertex2f(-20.0f, -40.0f);

    glEdgeFlag(true);
    glVertex2f(-20.0f, -40.0f);
    glVertex2f(0.0f, -80.0f);
    glEdgeFlag(false);
    glVertex2f(20.0f, -40.0f);

    glEdgeFlag(true);
    glVertex2f(20.0f, -40.0f);
    glVertex2f(60.0f, -20.0f);
    glEdgeFlag(false);
    glVertex2f(20.0f, 0.0f);
    //中间的两个三角形的边统统不要
    glVertex2f(-20.0f, 0.0f);
    glVertex2f(-20.0f, -40.0f);
    glVertex2f(20.0f, 0.0f);

    glVertex2f(-20.0f, -40.0f);
    glVertex2f(20.0f, -40.0f);
    glVertex2f(20.0f, 0.0f);
    glEdgeFlag(true);//这个函数的功能是恢复画线到初始状态
    //结束绘制三角形
    glEnd();
    glFlush();
}

void ChangeSize(GLsizei w, GLsizei h)
{
    GLfloat nRange = 100.0f;
    GLfloat aspectRatio;
    if (0 == h){
        h = 1;
    }
    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    aspectRatio = (GLfloat)w / (GLfloat)h;
    if (w <= h) {
        glOrtho(-nRange, nRange, -nRange / aspectRatio, nRange / aspectRatio, -nRange, nRange);
    }
    else{
        glOrtho(-nRange * aspectRatio, nRange *aspectRatio, -nRange, nRange, -nRange, nRange);
    }
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(400, 300);
    glutCreateWindow("用glEdgeFlag函数和凸边形拼接非凸多边形");
    glutDisplayFunc(RenderScene);
    glutReshapeFunc(ChangeSize);
    SetupRC();
    glutMainLoop();

    return 0;
}

      如果不堆砌的话效果是这样的:(测试时不用注释glEdgeFlag语句,只需将语句#define false 0改为#define false 1)

    2)给上述实例增加动画效果,并自定义显示模式:

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

//用于存取获取屏幕的高宽分辨率
GLint SCREEN_WIDTH = 0;
GLint SCREEN_HEIGHT = 0;
//初始状态程序的窗口大小
GLint windowWidth = 400;
GLint windowHeight = 300;
//用于设置旋转动画
GLfloat xRotAngle = 0.0f;
GLfloat yRotAngle = 0.0f;
#define MODE_SOLID  1
#define MODE_LINE   2
#define MODE_POINTS 3
GLint iMode = MODE_SOLID;//多边形的填充方式
GLboolean bEdgeFlag = GL_TRUE;//控制边的显示与否
//受支持的点大小范围
GLfloat sizes[2];
//受支持的点大小增量
GLfloat step;
//菜单回调函数
void processMenu(int value){
    switch (value){
    case 1:
        iMode = MODE_SOLID;
        break;
    case 2:
        iMode = MODE_LINE;
        break;
    case 3:
        iMode = MODE_POINTS;
        break;
    case 4:
        bEdgeFlag = GL_TRUE;
        break;
    case 5:
        bEdgeFlag = GL_FALSE;
        break;
    default:
        break;
    }
    //重新绘制
    glutPostRedisplay();
}
//显示回调函数
void renderScreen(void){
    GLfloat x, y, z, angle;
    int i;
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glPushMatrix();
    glRotatef(xRotAngle, 1.0f, 0.0f, 0.0f);
    glRotatef(yRotAngle, 0.0f, 1.0f, 0.0f);
    //设置多边形正面和背面的填充模式 
    if (MODE_SOLID == iMode)
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    if (MODE_LINE == iMode)
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    if (MODE_POINTS == iMode)
        glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
    x = 0.0f;
    y = 0.0f;
    z = 0.0f;
    //进行平滑处理 
    glEnable(GL_POINT_SMOOTH);
    glHint(GL_POINT_SMOOTH, GL_NICEST);
    glEnable(GL_LINE_SMOOTH);
    glHint(GL_LINE_SMOOTH, GL_NICEST);
    glEnable(GL_POLYGON_SMOOTH);
    glHint(GL_POLYGON_SMOOTH, GL_NICEST);
    //绘制坐标系
    if (MODE_POINTS != iMode){
        glColor3f(1.0f, 1.0f, 1.0f);
        glBegin(GL_LINES);
        glVertex3f(-80.0f, 0.0f, 0.0f);
        glVertex3f(80.0f, 0.0f, 0.0f);
        glVertex3f(0.0f, -80.0f, 0.0f);
        glVertex3f(0.0f, 80.0f, 0.0f);
        glVertex3f(0.0f, 0.0f, -80.0f);
        glVertex3f(0.0f, 0.0f, 80.0f);
        glEnd();

        glColor3f(1.0, 0.0, 0.0);
        glPushMatrix();
        glTranslatef(80.0f, 0.0f, 0.0f);
        glRotatef(90.0f, 0.0f, 1.0f, 0.0f);
        glutWireCone(3, 6, 10, 10);
        glPopMatrix();

        glPushMatrix();
        glTranslatef(0.0f, 80.0f, 0.0f);
        glRotatef(-90.0f, 1.0f, 0.0f, 0.0f);
        glutWireCone(3, 6, 10, 10);
        glPopMatrix();

        glPushMatrix();
        glTranslatef(0.0f, 0.0f, 80.0f);
        glRotatef(90.0f, 0.0f, 0.0f, 1.0f);
        glutWireCone(3, 6, 10, 10);
        glPopMatrix();
        glColor3f(1.0f, 1.0f, 1.0f);
    }
    glColor3f(0.0f, 0.0f, 0.5f);
    glBegin(GL_TRIANGLES);
    glPointSize(sizes[1]);
    glEdgeFlag(bEdgeFlag);
    glVertex2f(-20.0f, 20.0f);
    glEdgeFlag(GL_TRUE);
    glVertex2f(20.0f, 20.0f);
    glVertex2f(0.0f, 60.0f);
    glVertex2f(-20.0f, 20.0f);
    glVertex2f(-60.0f, 0.0f);
    glEdgeFlag(bEdgeFlag);
    glVertex2f(-20.0f, -20.0f);
    glEdgeFlag(GL_TRUE);
    glVertex2f(-20.0f, -20.0f);
    glVertex2f(0.0f, -60.0f);
    glEdgeFlag(bEdgeFlag);
    glVertex2f(20.0f, -20.0f);
    glEdgeFlag(GL_TRUE);
    glVertex2f(20.0f, -20.0f);
    glVertex2f(60.0f, 0.0f);
    glEdgeFlag(bEdgeFlag);
    glVertex2f(20.0f, 20.0f);
    glEdgeFlag(GL_TRUE);
    glEdgeFlag(bEdgeFlag);
    glVertex2f(-20.0f, 20.0f);
    glVertex2f(-20.0f, -20.0f);
    glVertex2f(20.0f, 20.0f);
    glVertex2f(-20.0f, -20.0f);
    glVertex2f(20.0f, -20.0f);
    glVertex2f(20.0f, 20.0f);
    glEdgeFlag(GL_TRUE);
    glEnd();

    glPopMatrix();
    glutSwapBuffers();
}
void setupRC(void){
    glClearColor(0.4f, 0.4, 0.4, 1.0f);
    glColor3f(0.0f, 1.0f, 0.0f);
    //使能深度测试,深度测试开启之后能够透过物体看到其他物体的色彩
    glEnable(GL_DEPTH_TEST);
    //获取受支持的点大小范围
    glGetFloatv(GL_POINT_SIZE_RANGE, sizes);
    //获取受支持的点大小增量
    glGetFloatv(GL_POINT_SIZE_GRANULARITY, &step);
    printf("point size range:%f-%f
", sizes[0], sizes[1]);
    printf("point step:%f
", step);
}
void changSize(GLint w, GLint h){
    GLfloat ratio;
    //设置坐标系范围大小为x(-100.0f,100.0f)、y(-100.0f,100.0f)、z(-100.0f,100.0f)
    GLfloat coordinatesize = 100.0f;
    if ((w == 0) || (h == 0))
        return;//就是使窗体改变操作无效
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    ratio = (GLfloat)w / (GLfloat)h;
    if (w<h)
        glOrtho(-coordinatesize, coordinatesize, -coordinatesize / ratio, coordinatesize / ratio, -coordinatesize, coordinatesize);
    else
        glOrtho(-coordinatesize*ratio, coordinatesize*ratio, -coordinatesize, coordinatesize, -coordinatesize, coordinatesize);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
void specialKey(int key, int x, int y){

    if (key == GLUT_KEY_UP){
        xRotAngle -= 5.0f;
    }
    else if (key == GLUT_KEY_DOWN){
        xRotAngle += 5.0f;
    }
    else if (key == GLUT_KEY_LEFT){
        yRotAngle -= 5.0f;
    }
    else if (key == GLUT_KEY_RIGHT){
        yRotAngle += 5.0f;
    }
    glutPostRedisplay();
}

int main(int argc, char* argv[])
{
    int nModeMenu;
    int nEdgeMenu;
    int nMainMenu;
    glutInit(&argc, argv);
    //使用双缓冲区模式(动画),深度缓冲(3D)和带有透明度值得色彩设置
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    //获取系统的高度和宽度分辨率
    SCREEN_WIDTH = glutGet(GLUT_SCREEN_WIDTH);
    SCREEN_HEIGHT = glutGet(GLUT_SCREEN_HEIGHT);

    //创建二级菜单
    nModeMenu = glutCreateMenu(processMenu);
    glutAddMenuEntry("Solid", 1);
    glutAddMenuEntry("Outline", 2);
    glutAddMenuEntry("Points", 3);
    nEdgeMenu = glutCreateMenu(processMenu);
    glutAddMenuEntry("On", 4);
    glutAddMenuEntry("Off", 5);
    //创建一级菜单
    nMainMenu = glutCreateMenu(processMenu);
    glutAddSubMenu("Mode", nModeMenu);
    glutAddSubMenu("Edge", nEdgeMenu);

    //这两个函数的顺序不能调换
    glutCreateWindow("3D空间中绘制凹边形");
    //将菜单榜定到鼠标右键上
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    printf("窗口的显示宽度=%d,高度=%d
", SCREEN_WIDTH, SCREEN_HEIGHT);

    /*这两套语句具有相同的功能:初始化窗体的位置和大小,但一个放在创建窗体之前,一个放在其后
    glutInitWindowSize(windowWidth,windowHeight);
    glutInitWindowPosition((1536 - 400) / 2,(864 - 300) / 2 );
    */
    glutPositionWindow((SCREEN_WIDTH - windowWidth) / 2, (SCREEN_HEIGHT - windowHeight) / 2);
    glutReshapeWindow(windowWidth, windowHeight);

    glutReshapeFunc(changSize);
    glutSpecialFunc(specialKey);
    glutDisplayFunc(renderScreen);
    setupRC();//RC=RederingState
    glutMainLoop();
    return 0;
}

    3)2D球体反弹动画

#include <GL/glut.h>
#include <math.h>
#pragma comment(linker,"/subsystem:"windows" /entry:"mainCRTStartup"")
#define PI 3.1416
//方块的起始位置和大小

GLfloat xIndex = 0.0f;
GLfloat yIndex = 0.0f;
GLfloat rsize = 25.0f;
// 在x和y方向的步进大小
GLfloat xstep = 1.0f;
GLfloat ystep = 1.0f;
// 窗口的大小(使用逻辑笛卡尔坐标系统)
GLfloat windowWidth;
GLfloat windowHeight;

void RenderScene()
{
    int i;
    GLfloat x, y;
    glClear(GL_COLOR_BUFFER_BIT);
    /* 把当前绘图颜色设置为红色
    glColor3f(1.0f, 0.0f, 0.0f);
    OpenGL命令,用当前的绘图颜色绘制一个填充矩形(提供左上角和右下角的顶点坐标)
    glRectf(xIndex, yIndex, xIndex + rsize, yIndex - rsize);*/
    glColor3f(1.0f, 0.0f, 1.0f);
    glBegin(GL_POLYGON);
    for (i = 0; i < 1000;i++)
    {
        x = 12.5*sin(2 * PI*i / 1000)+xIndex+12.5;
        y = 12.5*cos(2 * PI*i / 1000)+yIndex-12.5;
        glVertex2f(x,y);
    }
    glEnd();
    glutSwapBuffers();
}
//动画的效果通过改变绘制图形的位置实现的
// 由GLUT函数库调用,计时器函数
void TimerFunction(int value)
{
    // 在到达右边或者左边时翻转方向,这里的边界选取要结合裁剪区域的定义
    if (xIndex >windowWidth - rsize || xIndex < -windowWidth) {
        xstep = -xstep;
    }
    // 在到达上边或者下边时翻转方向,这里的边界选取要结合裁剪区域的定义
    if (yIndex >windowHeight || yIndex < -windowHeight + rsize) {
        ystep = -ystep;
    }
    // 移动方块
    xIndex += xstep;
    yIndex += ystep;
    // 检查边界,防止方块在反弹时窗口变小,使方块出现在新的裁剪区域之外
    if (xIndex >(windowWidth - rsize + xstep)) {
        xIndex = windowWidth - rsize - 1;
    } else if(xIndex < -windowWidth - xstep) {
        xIndex = -windowWidth - 1;
    }
    // 检查边界,防止方块在反弹时窗口变小,使方块出现在新的裁剪区域之外
    if (yIndex >(windowHeight + ystep)) {
        yIndex = windowHeight - 1;
    } else if(yIndex < -windowHeight + rsize - ystep) {
        yIndex = -windowHeight + rsize - 1;
    }
    glutPostRedisplay();
    glutTimerFunc(20, TimerFunction, 1);
}
void SetupRC()
{
    glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
}

// 当窗口大小改变时由GLUT函数库调用
void ChangeSize(GLsizei w, GLsizei h)
{
    GLfloat aspectRatio;
    if (0 == h){
        h = 1;
    }
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    aspectRatio = (GLfloat)w / (GLfloat)h;
    if (w <= h) {
        windowWidth = 100.0;
        windowHeight = 100.0 / aspectRatio;
        glOrtho(-100.0, 100.0, -100 / aspectRatio, 100 / aspectRatio, 1.0, -1.0);
    }
    else if(w>h) {
        windowWidth = 100.0 * aspectRatio;
        windowHeight = 100.0;
        glOrtho(-100.0 * aspectRatio, 100.0 *aspectRatio, -100.0, 100.0, 1.0, -1.0);
    }
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char *argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(480, 320);
    glutInitWindowPosition(400,300);
    glutCreateWindow("2D反弹动画");
    glutDisplayFunc(RenderScene);
    glutReshapeFunc(ChangeSize);
    // 设置计时器函数
    glutTimerFunc(20, TimerFunction, 1);
    SetupRC();
    glutMainLoop();

    return 0;
}

      3)在平面中实现动态画线:(实际上相当于把每条直线分为若干个直线,每绘制一小段直线进程陷入睡眠一段时间,看起来好像是动态效果。)

#include<GL/glut.h>
#pragma comment(linker,"/subsystem:"windows" /entry:"mainCRTStartup"")

void init(void)
{
    glClearColor(0.5, 0.5, 0.5, 0.5);
    glShadeModel(GL_FLAT);
}
/*
特别注意:
  1、斜率不存在。2、直线斜率一定要保持一致,将直线两点式方程转化为斜率式方程y=kx+c
  k=(y2-y1)/(x2-x1);    
  c=(x2*y1-x1*y2)/(x2-x1);
*/
//此函数用来画一条自定义起始点颜色的线,自定义线的类型并且能够实现动态绘制的功能
void DrawDynamicLine(GLfloat x1, GLfloat y1,GLfloat x2, GLfloat y2,    GLfloat red, GLfloat green, GLfloat blue,int speed,int type)
{
    glEnable(GL_LINE_STIPPLE);//点画线模式
    glColor3f(red, green, blue);
    glLineStipple(1, type);//dashed(虚线),type为16位2进制数,0表示实点,1表示虚点

    if (x1 != x2)//如果斜率存在
    {
        GLfloat k = (y2 - y1) / (x2 - x1);        //直线斜率 y=kx+c
        GLfloat c = (x2*y1 - x1*y2) / (x2 - x1);//直线常数
        //假定以A为原点建立二维坐标系,则下边4个if分别对应于:第一象限,第二象限,第三象限,第四象限
        if (x1<x2&&y1 <= y2)
        {
            for (int i = 0; i <= x2 - x1; i++)
            {
                glBegin(GL_LINES);
                glVertex2f(x1, y1);
                glVertex2f(x1 + i, k*(x1 + i) + c);
                glEnd();
                Sleep(100 - speed);
                glFlush();    
            }
        }
        else if (x1>x2&&y1 <= y2)
        {
            for (int i = 0; i >= x2 - x1; i--)
            {
                glBegin(GL_LINES);
                glVertex2f(x1, y1);
                glVertex2f(x1 + i, k*(x1 + i) + c);
                glEnd();
                Sleep(100 - speed);
                glFlush();
            }
        }
        else if (x1>x2&&y1 >= y2)
        {
            for (int i = 0; i >= x2 - x1; i--)
            {
                glBegin(GL_LINES);
                glVertex2f(x1, y1);
                glVertex2f(x1 + i, k*(x1 + i) + c);
                glEnd();
                Sleep(100 - speed);
                glFlush();
            }
        }
        else if (x1<x2&&y1 >= y2)
        {
            for (int i = 0; i <= x2 - x1; i++)
            {
                glBegin(GL_LINES);
                glVertex2f(x1, y1);
                glVertex2f(x1 + i, k*(x1 + i) + c);
                glEnd();
                Sleep(100 - speed);
                glFlush();
            }
        }
    }
    else
    {
        if (y1<y2)
        {
            for (int i = 0; i <= y2 - y1; i++)    
            {
                glBegin(GL_LINES);
                glVertex2f(x1, y1);
                glVertex2f(x1, y1 + i);
                glEnd();
                Sleep(100 - speed);
                glFlush();                    
            }
        }
        else                    
        {
            for (int i = 0; i >= y2 - y1; i--)        
            {
                glBegin(GL_LINES);
                glVertex2f(x1, y1);
                glVertex2f(x1, y1 + i);
                glEnd();
                Sleep(100 - speed);
                glFlush();        
            }
        }
    }

}
void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);            //清除所有的像素
    //正方形ABCD
    DrawDynamicLine(100, 500, 500, 500, 1, 1, 0, 80, 0xFFFF);            //AB
    DrawDynamicLine(500, 500, 500, 100, 1, 0, 1, 85, 0xFFFF);            //BC
    DrawDynamicLine(500, 100, 100, 100, 1, 1, 1, 90, 0xFFFF);            //CD
    DrawDynamicLine(100, 100, 100, 500, 0, 1, 1, 95, 0xFFFF);            //DA

    //沿顺时针方向测试动态画线:第一象限,第四象限,第三象限,第二象限
    DrawDynamicLine(300, 300, 300, 500, 1, 0, 0, 95, 0xFFFF);            //1
    DrawDynamicLine(300, 300, 400, 500, 1, 0, 0, 95, 0x00FF);
    DrawDynamicLine(300, 300, 500, 500, 1, 0, 0, 95, 0xFFFF);
    DrawDynamicLine(300, 300, 500, 400, 1, 0, 0, 95, 0x00FF);
    DrawDynamicLine(300, 300, 500, 300, 1, 0, 0, 95, 0xFFFF);

    DrawDynamicLine(300, 300, 500, 300, 1, 0, 0, 95, 0xFFFF);            //2
    DrawDynamicLine(300, 300, 500, 200, 1, 0, 0, 95, 0x00FF);
    DrawDynamicLine(300, 300, 500, 100, 1, 0, 0, 95, 0xFFFF);
    DrawDynamicLine(300, 300, 400, 200, 1, 0, 0, 95, 0x00FF);
    DrawDynamicLine(300, 300, 400, 100, 1, 0, 0, 95, 0x00FF);

    DrawDynamicLine(300, 300, 300, 100, 1, 0, 0, 95, 0xFFFF);            //3
    DrawDynamicLine(300, 300, 200, 100, 1, 0, 0, 95, 0x00FF);
    DrawDynamicLine(300, 300, 100, 100, 1, 0, 0, 95, 0xFFFF);
    DrawDynamicLine(300, 300, 100, 200, 1, 0, 0, 95, 0x00FF);
    DrawDynamicLine(300, 300, 100, 300, 1, 0, 0, 95, 0xFFFF);

    DrawDynamicLine(300, 300, 100, 300, 1, 0, 0, 95, 0xFFFF);            //4
    DrawDynamicLine(300, 300, 100, 400, 1, 0, 0, 95, 0x00FF);
    DrawDynamicLine(300, 300, 100, 500, 1, 0, 0, 95, 0xFFFF);
    DrawDynamicLine(300, 300, 200, 500, 1, 0, 0, 95, 0x00FF);
    DrawDynamicLine(300, 300, 300, 500, 1, 0, 0, 95, 0xFFFF);

}
void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);           //为了选择一个更小的绘图区域,在窗口中定义一个像素矩形,将图像映射到这个矩形中
    glMatrixMode(GL_PROJECTION);                        //指定哪一个矩阵是当前矩阵(GL_PROJECTION,对投影矩阵应用随后的矩阵操作)
    glLoadIdentity();                                   //将当前的用户坐标系的原点移到了屏幕中心:类似于一个复位操作  
    gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);     //将当前的可视空间设置为正投影空间,这个函数描述了一个平行修剪空间,意味着离观察者较远的对象看上去不会变小  
}
int main(int argc, char* argv[])
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
    glutInitWindowSize(600, 600);
    glutInitWindowPosition(300, 100);
    glutCreateWindow("演示OpenGL图形绘制过程");    
    init();                                    
    glutDisplayFunc(display);                        
    glutReshapeFunc(reshape);
    glutMainLoop();                                
    return 0;
}

    (3)相关知识:

    1)获取显示器宽和高:

  int glutGet(GLenum state)glutGet retrieves simple GLUT state represented by integers.检索指定的GLUT状态。

      Description:

      glutGet retrieves simple GLUT state represented by integers. The state parameter determines what type of state to return. Window capability state is returned for the layer in use. GLUT state names beginning with GLUT_WINDOW_ return state for the current window. GLUT state names beginning with GLUT_MENU_ return state for the current menu. Other GLUT state names return global state. Requesting state for an invalid GLUT state name returns negative one.

  state为指定要检索的状态类型,为以下常量:

GLUT_WINDOW_X                           当前窗口的x坐标,以像素为单位
GLUT_WINDOW_Y                           当前窗口的y坐标,以像素为单位
GLUT_WINDOW_WIDTH                       当其窗口的宽度,以像素为单位
GLUT_WINDOW_HEIGHT                      当前窗口的高度,以像素为单位
GLUT_WINDOW_BUFFER_SIZE                 当前窗口中,颜色分量占用的位数,即用多少bit表示颜色分量
GLUT_WINDOW_STENCIL_SIZE                当前窗口中,蒙板分量占用的位数,即用多少bit表示蒙板分量
GLUT_WINDOW_DEPTH_SIZE                  当前窗口中,深度分量占用的位数,即用多少bit表示深度分量
GLUT_WINDOW_RED_SIZE                    当前窗口中,红色分量占用的位数,即用多少bit表示红色分量
GLUT_WINDOW_GREEN_SIZE                  当前窗口中,绿色分量占用的位数,即用多少bit表示绿色分量
GLUT_WINDOW_BLUE_SIZE                   当前窗口中,蓝色分量占用的位数,即用多少bit表示蓝色分量
GLUT_WINDOW_ALPHA_SIZE                  当前窗口中,alpha色分量占用的位数,即用多少bit表示alpha色分量
GLUT_WINDOW_ACCUM_RED_SIZE              当前窗口累积缓存中,红色分量占用的位数,即用多少bit表示红色分量
GLUT_WINDOW_ACCUM_GREEN_SIZE            当前窗口累积缓存中,绿色分量占用的位数,即用多少bit表示绿色分量
GLUT_WINDOW_ACCUM_BLUE_SIZE             当前窗口累积缓存中,蓝色分量占用的位数,即用多少bit表示蓝色分量
GLUT_WINDOW_ACCUM_ALPHA_SIZE            当前窗口累积缓存中,alpha色分量占用的位数,即用多少bit表示alpha色分量
GLUT_WINDOW_DOUBLEBUFFER                如果窗口式双缓存模式,返回1,否则返回0
GLUT_WINDOW_RGBA                        如果窗口是RGBA模式,返回1,否则返回0
GLUT_WINDOW_PARENT                      查询当前窗口的父窗口个数,如果为顶层窗口返回0
GLUT_WINDOW_NUM_CHILDREN                查询当前窗口的子窗口个数
GLUT_WINDOW_NUM_SAMPLES                 查询多重采样的采样点个数
GLUT_WINDOW_STEREO                      查询是否使用立体模式,是则返回1,否则返回0
GLUT_WINDOW_CURSOR                      返回光标的整数标示
GLUT_SCREEN_HEIGHT                      屏幕的高度,以像素为单位
GLUT_SCREEN_WIDTH                       屏幕的宽度,以像素为单位
GLUT_SCREEN_WIDTH_MM                    屏幕的宽度,以毫米为单位
GLUT_SCREEN_HEIGHT_MM                   屏幕的高度,以毫米为单位
GLUT_MENU_NUM_ITEMS                     查询当前菜单包含的菜单项的个数
GLUT_DISPLAY_MODE_POSSIBLE              查询窗口系统是否支持当前的显示模式,1表示支持,0表示不支持
GLUT_INIT_DISPLAY_MODE                  初始窗口的显示模式
GLUT_INIT_WINDOW_X                      初始窗口的x坐标
GLUT_INIT_WINDOW_Y                      初始窗口的y坐标
GLUT_INIT_WINDOW_WIDTH                  初始窗口的宽度
GLUT_INIT_WINDOW_HEIGHT                 初始窗口的高度
GLUT_ELAPSED_TIME                       返回两次调用glutGet(GLUT_ELAPSED_TIME)的时间间隔,单位为毫秒

  2)添加菜单:

创建弹出式菜单的三个步骤:
1.定义菜单内各菜单项
2.定义每个菜单项的行为
3.把菜单关接到鼠标按钮上

  1.int glutCreateMenu(void(*func)(int value)

  创建一个新的弹出式菜单并返回一个唯一标识此菜单的整型表示符。func指明此菜单的功能。

      2.int glutAddMenuEntry(char *name,int value)

  在当前菜单底部增加一个菜单条目。name指定显示在新菜单条目上的ASCII码字符串。 value指定当选择该菜单条目时传递到菜单回调函数中的数值。

  3.void glutAddSubMenu(char *name,int menu)

  在当前菜单的底部增加一个子菜单触发条目。在当前菜单的底部增加一个子菜单触发条目。 name指定显示在新菜单触发条目上的ASCII码字符串。 meun当选择该子菜单触发条目时弹出的子菜单的标识符。

      4.void glutAttachMenu(int button)

  把当前窗口的一个鼠标按键与当前菜单的标识符联系起来。 button指明鼠标的哪个按键。GLUT_LEFT_BUTTON、GLUT_MIDDLE_BUTTON及GLUT_RIGHT_BUTTON,分别表明鼠标左、中及右键。

  5.int glutGetMenu(void)

  获取当前菜单的标识符,如果没有菜单存在或前一个当前菜单被删除了,glutGetMenu则返回0值。

    3)计时器:

      glutTimerFunc(unsigned int millis, void (*func)(int value), int value)

      三个参数分别为毫秒数, 回调函数指针, 区别值。区别值用于写自己的回调函数:void OnTimer(int value)可以用value值区分是哪个定时器。

      使用方法:

1.在函数里改变和位置有关的变量;
2.然后调用glutPostRedisplay();
3.用来重绘最后再次调用glutTimerFunc;
4.函数末尾再次调用gluTimerfunc();

     因为glut的定时器是调用一次才产生一次定时,所以如果要持续产生定时的话,在定时函数末尾再次调用glutTimerFunc。

    4)进程睡眠:

      Sleep函数:void Sleep(DWORD dwMilliseconds);

      功能: 执行挂起一段时间

  注意:在VC中使用带上头文件 #include <windows.h> 

Sleep()单位为毫秒,sleep()单位为秒;unsigned sleep(unsigned seconds);
usleep单位为微秒;
返回值:若进程/线程挂起到参数所指定的时间则返回0,若有信号中断则返回剩余秒数。

    (4)在3D空间中动态画线:(一次次重复地绘制全部的场景,每次场景仅比上次多一个小短边。)

#include <GL/glut.h>
#pragma comment(linker,"/subsystem:"windows" /entry:"mainCRTStartup"")
GLfloat xRotAngle = 0.0f;
GLfloat yRotAngle = 30.0f;
GLint flag = 0;
GLint temp = 0;

void draw3DLine(GLfloat xBegin,GLfloat yBegin,GLfloat zBegin,GLfloat xEnd,GLfloat yEnd,GLfloat zEnd,GLint type)
{
    //glLineStipple(1, type);//dashed(虚线),type为16位2进制数,0表示实点,1表示虚点(交替画虚线和实线)
    GLint i = 0;
    GLfloat xStep, yStep, zStep;
    xStep = (xEnd-xBegin) / 500;
    yStep = (yEnd-yBegin) / 500;
    zStep = (zEnd-zBegin) / 500;
    glPointSize(2.0);
    glColor3f(0.0,0.0,1.0f);
    glEnable(GL_LINE_STIPPLE);//点画线模式
    for (i = 0; i < 500;i++)
    {
        glBegin(GL_LINES);
        glVertex3f(xBegin,yBegin,zBegin+i*zStep);
        glVertex3f(xBegin + (i+1)*xStep, yBegin + (i+1)*yStep, zBegin + (i+1)*zStep);
        glEnd();
        if (flag == temp){
            flag++;//每一次只比前一次多画一条短线
            break;
        }
        temp++;
    }
}
void paintSingleAxis(GLfloat axisLength)
{
    glLineWidth(2.0f);
    glColor3f(1.0f,1.0f,1.0f);//白色的轴
    glPushMatrix();
    glBegin(GL_LINES);
    glVertex3f(0.0,0.0,0.0);
    glVertex3f(0.0, axisLength, 0.0);//绘制初态为y轴,转换为x轴和z轴需要旋转
    glEnd();
    glLineWidth(2.0f);
    glTranslated(0.0,axisLength - 8,0.0);//绘制红色的尖端
    glColor3f(1.0,0.0,0.0);

    glPushMatrix();
    glRotated(-90,1,0,0);//由z轴旋转到y轴上,右手系
    glutWireCone(1, 8, 20, 20); 
    glPopMatrix();

    glPopMatrix();
    glFlush();
}
void readerScreen()
{
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrtho(-200.0, 200, -200.0, 200.0, -200, 200);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(45, 60, 30, 0.0, 30, 0.0, 0, 10, 0);

    glPushMatrix();
    glRotatef(xRotAngle, 1.0f, 0.0f, 0.0f);
    glRotatef(yRotAngle, 0.0f, 1.0f, 0.0f);
    //绘制坐标轴
    glPushMatrix();
    glRotated(-90, 0.0, 0.0, 1.0);//旋转和绘制的顺序不能调换
    paintSingleAxis(80);//x轴
    glPopMatrix();

    paintSingleAxis(80);//y轴

    glPushMatrix();
    glRotated(90, 1.0, 0.0, 0.0);
    paintSingleAxis(80);//z轴
    glPopMatrix();
    glPointSize(20);

    temp = 0;    //停在temp==flag上。
    draw3DLine( 50, 50,-50, 50, 50, 50, 1);//A(50,50,-50),B(50,50,50)
    draw3DLine( 50, 50, 50,-50, 50, 50, 1);
    draw3DLine(-50, 50, 50,-50, 50,-50, 1);//C(-50,50,50),D(-50,50,-50)
    draw3DLine(-50, 50,-50, 50, 50,-50, 1);

    draw3DLine( 50,-50,-50, 50,-50, 50, 1);//E(50,-50,-50),F(50,-50,50)
    draw3DLine( 50,-50, 50,-50,-50, 50, 1);
    draw3DLine(-50,-50, 50,-50,-50,-50, 1);//G(-50,-50,50),H(-50,-50,-50)
    draw3DLine(-50,-50,-50, 50,-50,-50, 1);

    draw3DLine(-50, 50,-50,-50,-50,-50, 1);
    draw3DLine(-50, 50, 50,-50,-50, 50, 1);
    draw3DLine( 50, 50, 50, 50,-50, 50, 1);
    draw3DLine( 50, 50,-50, 50,-50,-50, 1);

    glPopMatrix();
    glutSwapBuffers();
}
void setupRC(void)
{
    glClearColor(0.2,0.2,0.2,1.0);
    glEnable(GL_DEPTH_TEST);
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
}
// 当窗口大小改变时由GLUT函数库调用
void ChangeSize(GLsizei w, GLsizei h)
{
    GLfloat aspectRatio;
    if (0 == h){
        h = 1;
    }
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    aspectRatio = (GLfloat)w / (GLfloat)h;
    if (w <= h) {
        glOrtho(-200.0, 200.0, -200 / aspectRatio, 200 / aspectRatio, 200, -200);
    }
    else if (w>h) {
        glOrtho(-200.0 * aspectRatio, 200.0 *aspectRatio, -200.0, 200.0, 200, -200);
    }
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
void specialKey(int key, int x, int y)
{
    if (key == GLUT_KEY_UP){
        xRotAngle -= 5.0f;
    }
    else if (key == GLUT_KEY_DOWN){
        xRotAngle += 5.0f;
    }
    else if (key == GLUT_KEY_LEFT){
        yRotAngle -= 5.0f;
    }
    else if (key == GLUT_KEY_RIGHT){
        yRotAngle += 5.0f;
    }
    glutPostRedisplay();
}
void IdleFunction()
{
    glutPostRedisplay();
    Sleep(12-flag*0.002);//用来尽量保持匀速画线
}
int main(int argc,char *argv[])
{
    glutInit(&argc,argv);
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB);
    glutInitWindowSize(400,400);
    glutInitWindowPosition(200,100);
    glutCreateWindow("在3D空间中动态绘制");
    glutDisplayFunc(readerScreen);
    glutSpecialFunc(specialKey);
    setupRC();
    glutIdleFunc(IdleFunction);
    glutMainLoop();
    return 0;
}

原文地址:https://www.cnblogs.com/MenAngel/p/5645765.html