阅读 3

OpenGL NeHe on Linux

搭建环境

Ubuntu 16.04 OpenGL 开发环境配置指南

创建一个OpenGL窗口

int main(int argc, char** argv)
{
    /* GLUT环境初始化*/
    glutInit (&argc, argv);
    /* 显示模式初始化 */
    glutInitDisplayMode (GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    /* 定义窗口大小 */
    glutInitWindowSize (300, 300);
    /* 定义窗口位置 */
    glutInitWindowPosition (100, 100);
    /* 显示窗口,窗口标题为执行函数名 */
    glutCreateWindow ( argv [ 0 ] );
    /* 调用OpenGL初始化函数 */
    init ( );
    /* 注册OpenGL绘图函数 */
    glutDisplayFunc ( display );
    // /* 进入GLUT消息循环,开始执行程序 */
    glutMainLoop( );
    return 0;
}
复制代码
/* 初始化材料属性、光源属性、光照模型,打开深度缓冲区 */
void init ( void )
{
    InitGL();
    ReSizeGLScene(400, 400);
}
/*调用GLUT函数,绘制一个球*/
void display ( void )
{

    DrawGLScene();
    glFlush ();
}
复制代码

上面的初始化代码是不变的。

/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
ReSizeGLScene(GLsizei width, GLsizei height)                // 重置OpenGL窗口大小
{
    if (height == 0)                                // 防止被零除
    {
        height = 1;                            // 将Height设为1
    }

    glViewport(0, 0, width, height);                    // 重置当前的视口
    /**
     * 下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。
     * 此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是我们在场景中所能绘制深度的起点和终点。
     *
     * glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。投影矩阵负责为我们的场景增加透视。
     * glLoadIdentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。调用 glLoadIdentity()之后我们为场景设置透视图。
     *
     * glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。模型观察矩阵中存放了我们的物体讯息。最后我们重置模型观察矩阵。
     */
    glMatrixMode(GL_PROJECTION);                        // 选择投影矩阵
    glLoadIdentity();                            // 重置投影矩阵

    // 设置视口的大小
    gluPerspective(45.0f, (GLfloat) width / (GLfloat) height, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);                        // 选择模型观察矩阵
    glLoadIdentity();                            // 重置模型观察矩阵
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * @return
 */
InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    /**
     * 下一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
     */
    glShadeModel(GL_SMOOTH);                        // 启用阴影平滑
    /**
     * 下一行设置清除屏幕时所用的颜色。如果您对色彩的工作原理不清楚的话,我快速解释一下。
     * 色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。
     * glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
     *
     * 通过混合三种原色(红、绿、蓝),您可以得到不同的色彩。因此,当您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您将用亮蓝色来清除屏幕。
     * 如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,您将使用中红色来清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。
     * 要得到白色背景,您应该将所有的颜色设成最亮(1.0f)。要黑色背景的话,您该将所有的颜色设为最暗(0.0f)。
     */
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // 黑色背景
    /**
     * 接下来的三行必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。
     * 我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。
     * 这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。
     */
    glClearDepth(1.0f);                            // 设置深度缓存
    glEnable(GL_DEPTH_TEST);                        // 启用深度测试
    glDepthFunc(GL_LEQUAL);                            // 所作深度测试的类型
    /**
     * 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。
     */
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);            // 告诉系统对透视进行修正
    return TRUE;                                // 初始化 OK
}

/**
 * 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。
 * 如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。
 * @return
 */
DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // 清除屏幕和深度缓存
    glLoadIdentity();                            // 重置当前的模型观察矩阵
    return TRUE;                                //  一切 OK
}
复制代码

绘制多边形

/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
ReSizeGLScene(GLsizei width, GLsizei height)                // 重置OpenGL窗口大小
{
    if (height == 0)                                // 防止被零除
    {
        height = 1;                            // 将Height设为1
    }

    glViewport(0, 0, width, height);                    // 重置当前的视口
    /**
     * 下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。
     * 此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是我们在场景中所能绘制深度的起点和终点。
     *
     * glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。投影矩阵负责为我们的场景增加透视。
     * glLoadIdentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。调用 glLoadIdentity()之后我们为场景设置透视图。
     *
     * glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。模型观察矩阵中存放了我们的物体讯息。最后我们重置模型观察矩阵。
     */
    glMatrixMode(GL_PROJECTION);                        // 选择投影矩阵
    glLoadIdentity();                            // 重置投影矩阵

    // 设置视口的大小
    gluPerspective(45.0f, (GLfloat) width / (GLfloat) height, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);                        // 选择模型观察矩阵
    glLoadIdentity();                            // 重置模型观察矩阵
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * @return
 */
InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    /**
     * 下一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
     */
    glShadeModel(GL_SMOOTH);                        // 启用阴影平滑
    /**
     * 下一行设置清除屏幕时所用的颜色。如果您对色彩的工作原理不清楚的话,我快速解释一下。
     * 色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。
     * glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
     *
     * 通过混合三种原色(红、绿、蓝),您可以得到不同的色彩。因此,当您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您将用亮蓝色来清除屏幕。
     * 如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,您将使用中红色来清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。
     * 要得到白色背景,您应该将所有的颜色设成最亮(1.0f)。要黑色背景的话,您该将所有的颜色设为最暗(0.0f)。
     */
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // 黑色背景
    /**
     * 接下来的三行必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。
     * 我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。
     * 这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。
     */
    glClearDepth(1.0f);                            // 设置深度缓存
    glEnable(GL_DEPTH_TEST);                        // 启用深度测试
    glDepthFunc(GL_LEQUAL);                            // 所作深度测试的类型
    /**
     * 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。
     */
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);            // 告诉系统对透视进行修正
    return TRUE;                                // 初始化 OK
}

/**
 * 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。
 * 如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。
 * @return
 */
DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // 清除屏幕和深度缓存
    glLoadIdentity();                            // 重置当前的模型观察矩阵
    /**
     * 当您调用glLoadIdentity()之后,您实际上将当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。
     * OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
     * glTranslatef(x, y, z)沿着 X, Y 和 Z 轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。
     * 注意在glTranslatef(x, y, z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。
     */
    glTranslatef(-1.5f, 0.0f, -6.0f);                        // 左移 1.5 单位,并移入屏幕 6.0
    /**
     * 现在我们已经移到了屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景-创建三角形。
     * glBegin(GL_TRIANGLES)的意思是开始绘制三角形,glEnd() 告诉OpenGL三角形已经创建好了。
     * 通常您会需要画3个顶点,可以使用GL_TRIANGLES。在绝大多数的显卡上,绘制三角形是相当快速的。
     * 如果要画四个顶点,使用GL_QUADS的话会更方便。但据我所知,绝大多数的显卡都使用三角形来为对象着色。最后,如果您想要画更多的顶点时,可以使用GL_POLYGON。
     */
    /**
     * 本节的简单示例中,我们只画一个三角形。如果要画第二个三角形的话,可以在这三点之后,再加三行代码(3点)。
     * 所有六点代码都应包含在glBegin(GL_TRIANGLES) 和 glEnd()之间。在他们之间再不会有多余的点出现,也就是说,(GL_TRIANGLES) 和 glEnd()之间的点都是以三点为一个集合的。
     * 这同样适用于四边形。如果您知道实在绘制四边形的话,您必须在第一个四点之后,再加上四点为一个集合的点组。
     * 另一方面,多边形可以由任意个顶点,(GL_POLYGON)不在乎glBegin(GL_TRIANGLES) 和 glEnd()之间有多少行代码。
     * glBegin之后的第一行设置了多边形的第一个顶点,glVertex 的第一个参数是X坐标,然后依次是Y坐标和Z坐标。
     * 第一个点是上顶点,然后是左下顶点和右下顶点。glEnd()告诉OpenGL没有其他点了。这样将显示一个填充的三角形。
     */
    glBegin(GL_TRIANGLES);                            // 绘制三角形
    glVertex3f(0.0f, 1.0f, 0.0f);                    // 上顶点
    glVertex3f(-1.0f, -1.0f, 0.0f);                    // 左下
    glVertex3f(1.0f, -1.0f, 0.0f);                    // 右下
    glEnd();                                // 三角形绘制结束
    /**
     * 在屏幕的左半部分画完三角形后,我们要移到右半部分来画正方形。为此要再次使用glTranslate。这次右移,所以X坐标值为正值。
     * 因为前面左移了1.5个单位,这次要先向右移回屏幕中心(1.5个单位),再向右移动1.5个单位。总共要向右移3.0个单位。
     */
    glTranslatef(3.0f, 0.0f, 0.0f);                        // 右移3单位
    /**
     * 现在使用GL_QUADS绘制正方形。与绘制三角形的代码相类似,画四边形也很简单。唯一的区别是用GL_QUADS来替换了GL_TRIANGLES。
     * 并增加了一个点。我们使用顺时针次序来画正方形-左上-右上-右下-左下。采用顺时针绘制的是对象的后表面。
     * 这就是说我们所看见的是正方形的背面。逆时针画出来的正方形才是正面朝着我们的。现在这对您来说并不重要,但以后您必须知道。
     */
    glBegin(GL_QUADS);                            //  绘制正方形
    glVertex3f(-1.0f, 1.0f, 0.0f);                    // 左上
    glVertex3f(1.0f, 1.0f, 0.0f);                    // 右上
    glVertex3f(1.0f, -1.0f, 0.0f);                    // 左下
    glVertex3f(-1.0f, -1.0f, 0.0f);                    // 右下
    glEnd();                                // 正方形绘制结束
    return TRUE;                                //  一切 OK
}
复制代码

使用颜色

/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
ReSizeGLScene(GLsizei width, GLsizei height)                // 重置OpenGL窗口大小
{
    if (height == 0)                                // 防止被零除
    {
        height = 1;                            // 将Height设为1
    }

    glViewport(0, 0, width, height);                    // 重置当前的视口
    /**
     * 下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。
     * 此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是我们在场景中所能绘制深度的起点和终点。
     *
     * glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。投影矩阵负责为我们的场景增加透视。
     * glLoadIdentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。调用 glLoadIdentity()之后我们为场景设置透视图。
     *
     * glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。模型观察矩阵中存放了我们的物体讯息。最后我们重置模型观察矩阵。
     */
    glMatrixMode(GL_PROJECTION);                        // 选择投影矩阵
    glLoadIdentity();                            // 重置投影矩阵

    // 设置视口的大小
    gluPerspective(45.0f, (GLfloat) width / (GLfloat) height, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);                        // 选择模型观察矩阵
    glLoadIdentity();                            // 重置模型观察矩阵
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * @return
 */
InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    /**
     * 下一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
     */
    glShadeModel(GL_SMOOTH);                        // 启用阴影平滑
    /**
     * 下一行设置清除屏幕时所用的颜色。如果您对色彩的工作原理不清楚的话,我快速解释一下。
     * 色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。
     * glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
     *
     * 通过混合三种原色(红、绿、蓝),您可以得到不同的色彩。因此,当您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您将用亮蓝色来清除屏幕。
     * 如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,您将使用中红色来清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。
     * 要得到白色背景,您应该将所有的颜色设成最亮(1.0f)。要黑色背景的话,您该将所有的颜色设为最暗(0.0f)。
     */
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // 黑色背景
    /**
     * 接下来的三行必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。
     * 我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。
     * 这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。
     */
    glClearDepth(1.0f);                            // 设置深度缓存
    glEnable(GL_DEPTH_TEST);                        // 启用深度测试
    glDepthFunc(GL_LEQUAL);                            // 所作深度测试的类型
    /**
     * 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。
     */
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);            // 告诉系统对透视进行修正
    return TRUE;                                // 初始化 OK
}

/**
 * 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。
 * 如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。
 * @return
 */
DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // 清除屏幕和深度缓存
    glLoadIdentity();                            // 重置当前的模型观察矩阵
    /**
     * 当您调用glLoadIdentity()之后,您实际上将当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。
     * OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
     * glTranslatef(x, y, z)沿着 X, Y 和 Z 轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。
     * 注意在glTranslatef(x, y, z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。
     */
    glTranslatef(-1.5f, 0.0f, -6.0f);                        // 左移 1.5 单位,并移入屏幕 6.0
    /**
     * 现在我们已经移到了屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景-创建三角形。
     * glBegin(GL_TRIANGLES)的意思是开始绘制三角形,glEnd() 告诉OpenGL三角形已经创建好了。
     * 通常您会需要画3个顶点,可以使用GL_TRIANGLES。在绝大多数的显卡上,绘制三角形是相当快速的。
     * 如果要画四个顶点,使用GL_QUADS的话会更方便。但据我所知,绝大多数的显卡都使用三角形来为对象着色。最后,如果您想要画更多的顶点时,可以使用GL_POLYGON。
     */
    /**
     * 本节的简单示例中,我们只画一个三角形。如果要画第二个三角形的话,可以在这三点之后,再加三行代码(3点)。
     * 所有六点代码都应包含在glBegin(GL_TRIANGLES) 和 glEnd()之间。在他们之间再不会有多余的点出现,也就是说,(GL_TRIANGLES) 和 glEnd()之间的点都是以三点为一个集合的。
     * 这同样适用于四边形。如果您知道实在绘制四边形的话,您必须在第一个四点之后,再加上四点为一个集合的点组。
     * 另一方面,多边形可以由任意个顶点,(GL_POLYGON)不在乎glBegin(GL_TRIANGLES) 和 glEnd()之间有多少行代码。
     * glBegin之后的第一行设置了多边形的第一个顶点,glVertex 的第一个参数是X坐标,然后依次是Y坐标和Z坐标。
     * 第一个点是上顶点,然后是左下顶点和右下顶点。glEnd()告诉OpenGL没有其他点了。这样将显示一个填充的三角形。
     */
    glBegin(GL_TRIANGLES);                            // 绘制三角形
    /**
     * 如果您还记得上节课的内容,这段代码在屏幕的左半部分绘制三角形。下一行代码是我们第一次使用命令glColor3f(r,g,b)。
     * 括号中的三个参数依次是红、绿、蓝三色分量。取值范围可以从0,0f到1.0f。类似于以前所讲的清除屏幕背景命令。我们将颜色设为红色(纯红色,无绿色,无蓝色)。
     * 接下来的一行代码设置三角形的第一个顶点(三角形的上顶点),并使用当前颜色(红色)来绘制。从现在开始所有的绘制的对象的颜色都是红色,直到我们将红色改变成别的什么颜色。
     */
    glColor3f(1.0f,0.0f,0.0f);				// 设置当前色为红色
    glVertex3f(0.0f, 1.0f, 0.0f);                    // 上顶点
    glColor3f(0.0f,1.0f,0.0f);				// 设置当前色为绿色
    glVertex3f(-1.0f, -1.0f, 0.0f);                    // 左下
    glColor3f(0.0f,0.0f,1.0f);				// 设置当前色为蓝色
    glVertex3f(1.0f, -1.0f, 0.0f);                    // 右下
    glEnd();                                // 三角形绘制结束
    /**
     * 在屏幕的左半部分画完三角形后,我们要移到右半部分来画正方形。为此要再次使用glTranslate。这次右移,所以X坐标值为正值。
     * 因为前面左移了1.5个单位,这次要先向右移回屏幕中心(1.5个单位),再向右移动1.5个单位。总共要向右移3.0个单位。
     */
    glTranslatef(3.0f, 0.0f, 0.0f);                        // 右移3单位
    glColor3f(0.5f,0.5f,1.0f);					// 一次性将当前色设置为蓝色
    /**
     * 现在使用GL_QUADS绘制正方形。与绘制三角形的代码相类似,画四边形也很简单。唯一的区别是用GL_QUADS来替换了GL_TRIANGLES。
     * 并增加了一个点。我们使用顺时针次序来画正方形-左上-右上-右下-左下。采用顺时针绘制的是对象的后表面。
     * 这就是说我们所看见的是正方形的背面。逆时针画出来的正方形才是正面朝着我们的。现在这对您来说并不重要,但以后您必须知道。
     */
    glBegin(GL_QUADS);                            //  绘制正方形
    glVertex3f(-1.0f, 1.0f, 0.0f);                    // 左上
    glVertex3f(1.0f, 1.0f, 0.0f);                    // 右上
    glVertex3f(1.0f, -1.0f, 0.0f);                    // 左下
    glVertex3f(-1.0f, -1.0f, 0.0f);                    // 右下
    glEnd();                                // 正方形绘制结束
    return TRUE;                                //  一切 OK
}
复制代码

旋转

/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
ReSizeGLScene(GLsizei width, GLsizei height)                // 重置OpenGL窗口大小
{
    if (height == 0)                                // 防止被零除
    {
        height = 1;                            // 将Height设为1
    }

    glViewport(0, 0, width, height);                    // 重置当前的视口
    /**
     * 下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。
     * 此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是我们在场景中所能绘制深度的起点和终点。
     *
     * glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。投影矩阵负责为我们的场景增加透视。
     * glLoadIdentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。调用 glLoadIdentity()之后我们为场景设置透视图。
     *
     * glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。模型观察矩阵中存放了我们的物体讯息。最后我们重置模型观察矩阵。
     */
    glMatrixMode(GL_PROJECTION);                        // 选择投影矩阵
    glLoadIdentity();                            // 重置投影矩阵

    // 设置视口的大小
    gluPerspective(45.0f, (GLfloat) width / (GLfloat) height, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);                        // 选择模型观察矩阵
    glLoadIdentity();                            // 重置模型观察矩阵
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * @return
 */
InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    /**
     * 下一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
     */
    glShadeModel(GL_SMOOTH);                        // 启用阴影平滑
    /**
     * 下一行设置清除屏幕时所用的颜色。如果您对色彩的工作原理不清楚的话,我快速解释一下。
     * 色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。
     * glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
     *
     * 通过混合三种原色(红、绿、蓝),您可以得到不同的色彩。因此,当您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您将用亮蓝色来清除屏幕。
     * 如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,您将使用中红色来清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。
     * 要得到白色背景,您应该将所有的颜色设成最亮(1.0f)。要黑色背景的话,您该将所有的颜色设为最暗(0.0f)。
     */
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // 黑色背景
    /**
     * 接下来的三行必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。
     * 我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。
     * 这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。
     */
    glClearDepth(1.0f);                            // 设置深度缓存
    glEnable(GL_DEPTH_TEST);                        // 启用深度测试
    glDepthFunc(GL_LEQUAL);                            // 所作深度测试的类型
    /**
     * 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。
     */
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);            // 告诉系统对透视进行修正
    return TRUE;                                // 初始化 OK
}

/**
 * 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。
 * 如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。
 * @return
 */
DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // 清除屏幕和深度缓存
    glLoadIdentity();                            // 重置当前的模型观察矩阵
    /**
     * 当您调用glLoadIdentity()之后,您实际上将当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。
     * OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
     * glTranslatef(x, y, z)沿着 X, Y 和 Z 轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。
     * 注意在glTranslatef(x, y, z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。
     */
    glTranslatef(-1.5f, 0.0f, -6.0f);                        // 左移 1.5 单位,并移入屏幕 6.0
    /**
     * glRotatef(Angle,Xvector,Yvector,Zvector)负责让对象绕某个轴旋转。这个命令有很多用处。 Angle 通常是个变量代表对象转过的角度。
     * Xvector , Yvector 和 Zvector 三个参数则共同决定旋转轴的方向。比如(1,0,0)所描述的矢量经过X坐标轴的1个单位处并且方向向右。(-1,0,0)所描述的矢量经过X坐标轴的1个单位处,但方向向左。
     */
    /**
     * X轴-您正在使用一台台锯。锯片中心的轴从左至右摆放(就像OpenGL中的X轴)。尖利的锯齿绕着X轴狂转,看起来要么向上转,要么向下转。取决于锯片开始转时的方向。这与我们在OpenGL中绕着X轴旋转什么的情形是一样的。
     * Y轴-假设您正处于一个巨大的龙卷风中心,龙卷风的中心从地面指向天空(就像OpenGL中的Y轴)。垃圾和碎片围着Y轴从左向右或是从右向左狂转不止。
     * Z轴-您从正前方看着一台风扇。风扇的中心正好朝着您(就像OpenGL中的Z轴)。风扇的叶片绕着Z轴顺时针或逆时针狂转。
     */
    glRotatef(rtri, 0.0f, 1.0f, 0.0f);                // 绕Y轴旋转三角形
    /**
     * 现在我们已经移到了屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景-创建三角形。
     * glBegin(GL_TRIANGLES)的意思是开始绘制三角形,glEnd() 告诉OpenGL三角形已经创建好了。
     * 通常您会需要画3个顶点,可以使用GL_TRIANGLES。在绝大多数的显卡上,绘制三角形是相当快速的。
     * 如果要画四个顶点,使用GL_QUADS的话会更方便。但据我所知,绝大多数的显卡都使用三角形来为对象着色。最后,如果您想要画更多的顶点时,可以使用GL_POLYGON。
     */
    /**
     * 本节的简单示例中,我们只画一个三角形。如果要画第二个三角形的话,可以在这三点之后,再加三行代码(3点)。
     * 所有六点代码都应包含在glBegin(GL_TRIANGLES) 和 glEnd()之间。在他们之间再不会有多余的点出现,也就是说,(GL_TRIANGLES) 和 glEnd()之间的点都是以三点为一个集合的。
     * 这同样适用于四边形。如果您知道实在绘制四边形的话,您必须在第一个四点之后,再加上四点为一个集合的点组。
     * 另一方面,多边形可以由任意个顶点,(GL_POLYGON)不在乎glBegin(GL_TRIANGLES) 和 glEnd()之间有多少行代码。
     * glBegin之后的第一行设置了多边形的第一个顶点,glVertex 的第一个参数是X坐标,然后依次是Y坐标和Z坐标。
     * 第一个点是上顶点,然后是左下顶点和右下顶点。glEnd()告诉OpenGL没有其他点了。这样将显示一个填充的三角形。
     */
    glBegin(GL_TRIANGLES);                            // 绘制三角形
    /**
     * 如果您还记得上节课的内容,这段代码在屏幕的左半部分绘制三角形。下一行代码是我们第一次使用命令glColor3f(r,g,b)。
     * 括号中的三个参数依次是红、绿、蓝三色分量。取值范围可以从0,0f到1.0f。类似于以前所讲的清除屏幕背景命令。我们将颜色设为红色(纯红色,无绿色,无蓝色)。
     * 接下来的一行代码设置三角形的第一个顶点(三角形的上顶点),并使用当前颜色(红色)来绘制。从现在开始所有的绘制的对象的颜色都是红色,直到我们将红色改变成别的什么颜色。
     */
    glColor3f(1.0f, 0.0f, 0.0f);                // 设置当前色为红色
    glVertex3f(0.0f, 1.0f, 0.0f);                    // 上顶点
    glColor3f(0.0f, 1.0f, 0.0f);                // 设置当前色为绿色
    glVertex3f(-1.0f, -1.0f, 0.0f);                    // 左下
    glColor3f(0.0f, 0.0f, 1.0f);                // 设置当前色为蓝色
    glVertex3f(1.0f, -1.0f, 0.0f);                    // 右下
    glEnd();                                // 三角形绘制结束
    /**
     * 您会注意下面的代码中我们增加了另一个glLoadIdentity()调用。目的是为了重置模型观察矩阵。如果我们没有重置,直接调用glTranslate的话,会出现意料之外的结果。
     * 因为坐标轴已经旋转了,很可能没有朝着您所希望的方向。所以我们本来想要左右移动对象的,就可能变成上下移动了,取决于您将坐标轴旋转了多少角度。
     * 试试将glLoadIdentity() 注释掉之后,会出现什么结果。重置模型观察矩阵之后,X,Y,Z轴都以复位,我们调用glTranslate。
     * 您会注意到这次我们只向右一了1.5单位,而不是上节课的3.0单位。因为我们重置场景的时候,焦点又回到了场景的中心(0.0处)。这样就只需向右移1.5单位就够了。
     */
    glLoadIdentity();					// 重置模型观察矩阵
    /**
     * 在屏幕的左半部分画完三角形后,我们要移到右半部分来画正方形。为此要再次使用glTranslate。这次右移,所以X坐标值为正值。
     * 因为前面左移了1.5个单位,这次要先向右移回屏幕中心(1.5个单位),再向右移动1.5个单位。总共要向右移3.0个单位。
     */
    glTranslatef(1.5f,0.0f,-6.0f);				// 右移1.5单位,并移入屏幕 6.0
    glRotatef(rquad,1.0f,0.0f,0.0f);			//  绕X轴旋转四边形
    glColor3f(0.5f, 0.5f, 1.0f);                    // 一次性将当前色设置为蓝色
    /**
     * 现在使用GL_QUADS绘制正方形。与绘制三角形的代码相类似,画四边形也很简单。唯一的区别是用GL_QUADS来替换了GL_TRIANGLES。
     * 并增加了一个点。我们使用顺时针次序来画正方形-左上-右上-右下-左下。采用顺时针绘制的是对象的后表面。
     * 这就是说我们所看见的是正方形的背面。逆时针画出来的正方形才是正面朝着我们的。现在这对您来说并不重要,但以后您必须知道。
     */
    glBegin(GL_QUADS);                            //  绘制正方形
    glVertex3f(-1.0f, 1.0f, 0.0f);                    // 左上
    glVertex3f(1.0f, 1.0f, 0.0f);                    // 右上
    glVertex3f(1.0f, -1.0f, 0.0f);                    // 左下
    glVertex3f(-1.0f, -1.0f, 0.0f);                    // 右下
    glEnd();                                // 正方形绘制结束

    return TRUE;                                //  一切 OK
}
复制代码

3D空间

/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
ReSizeGLScene(GLsizei width, GLsizei height)                // 重置OpenGL窗口大小
{
    if (height == 0)                                // 防止被零除
    {
        height = 1;                            // 将Height设为1
    }

    glViewport(0, 0, width, height);                    // 重置当前的视口
    /**
     * 下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。
     * 此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是我们在场景中所能绘制深度的起点和终点。
     *
     * glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。投影矩阵负责为我们的场景增加透视。
     * glLoadIdentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。调用 glLoadIdentity()之后我们为场景设置透视图。
     *
     * glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。模型观察矩阵中存放了我们的物体讯息。最后我们重置模型观察矩阵。
     */
    glMatrixMode(GL_PROJECTION);                        // 选择投影矩阵
    glLoadIdentity();                            // 重置投影矩阵

    // 设置视口的大小
    gluPerspective(45.0f, (GLfloat) width / (GLfloat) height, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);                        // 选择模型观察矩阵
    glLoadIdentity();                            // 重置模型观察矩阵
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * @return
 */
InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    /**
     * 下一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
     */
    glShadeModel(GL_SMOOTH);                        // 启用阴影平滑
    /**
     * 下一行设置清除屏幕时所用的颜色。如果您对色彩的工作原理不清楚的话,我快速解释一下。
     * 色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。
     * glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
     *
     * 通过混合三种原色(红、绿、蓝),您可以得到不同的色彩。因此,当您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您将用亮蓝色来清除屏幕。
     * 如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,您将使用中红色来清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。
     * 要得到白色背景,您应该将所有的颜色设成最亮(1.0f)。要黑色背景的话,您该将所有的颜色设为最暗(0.0f)。
     */
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // 黑色背景
    /**
     * 接下来的三行必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。
     * 我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。
     * 这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。
     */
    glClearDepth(1.0f);                            // 设置深度缓存
    glEnable(GL_DEPTH_TEST);                        // 启用深度测试
    glDepthFunc(GL_LEQUAL);                            // 所作深度测试的类型
    /**
     * 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。
     */
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);            // 告诉系统对透视进行修正
    return TRUE;                                // 初始化 OK
}

/**
 * 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。
 * 如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。
 * @return
 */
DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // 清除屏幕和深度缓存
    glLoadIdentity();                            // 重置当前的模型观察矩阵
    /**
     * 当您调用glLoadIdentity()之后,您实际上将当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。
     * OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
     * glTranslatef(x, y, z)沿着 X, Y 和 Z 轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。
     * 注意在glTranslatef(x, y, z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。
     */
    glTranslatef(-1.5f, 0.0f, -6.0f);                        // 左移 1.5 单位,并移入屏幕 6.0
    /**
     * glRotatef(Angle,Xvector,Yvector,Zvector)负责让对象绕某个轴旋转。这个命令有很多用处。 Angle 通常是个变量代表对象转过的角度。
     * Xvector , Yvector 和 Zvector 三个参数则共同决定旋转轴的方向。比如(1,0,0)所描述的矢量经过X坐标轴的1个单位处并且方向向右。(-1,0,0)所描述的矢量经过X坐标轴的1个单位处,但方向向左。
     */
    /**
     * X轴-您正在使用一台台锯。锯片中心的轴从左至右摆放(就像OpenGL中的X轴)。尖利的锯齿绕着X轴狂转,看起来要么向上转,要么向下转。取决于锯片开始转时的方向。这与我们在OpenGL中绕着X轴旋转什么的情形是一样的。
     * Y轴-假设您正处于一个巨大的龙卷风中心,龙卷风的中心从地面指向天空(就像OpenGL中的Y轴)。垃圾和碎片围着Y轴从左向右或是从右向左狂转不止。
     * Z轴-您从正前方看着一台风扇。风扇的中心正好朝着您(就像OpenGL中的Z轴)。风扇的叶片绕着Z轴顺时针或逆时针狂转。
     */
    glRotatef(rtri, 0.0f, 1.0f, 0.0f);                // 绕Y轴旋转三角形
    /**
     * 现在我们已经移到了屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景-创建三角形。
     * glBegin(GL_TRIANGLES)的意思是开始绘制三角形,glEnd() 告诉OpenGL三角形已经创建好了。
     * 通常您会需要画3个顶点,可以使用GL_TRIANGLES。在绝大多数的显卡上,绘制三角形是相当快速的。
     * 如果要画四个顶点,使用GL_QUADS的话会更方便。但据我所知,绝大多数的显卡都使用三角形来为对象着色。最后,如果您想要画更多的顶点时,可以使用GL_POLYGON。
     */
    /**
     * 本节的简单示例中,我们只画一个三角形。如果要画第二个三角形的话,可以在这三点之后,再加三行代码(3点)。
     * 所有六点代码都应包含在glBegin(GL_TRIANGLES) 和 glEnd()之间。在他们之间再不会有多余的点出现,也就是说,(GL_TRIANGLES) 和 glEnd()之间的点都是以三点为一个集合的。
     * 这同样适用于四边形。如果您知道实在绘制四边形的话,您必须在第一个四点之后,再加上四点为一个集合的点组。
     * 另一方面,多边形可以由任意个顶点,(GL_POLYGON)不在乎glBegin(GL_TRIANGLES) 和 glEnd()之间有多少行代码。
     * glBegin之后的第一行设置了多边形的第一个顶点,glVertex 的第一个参数是X坐标,然后依次是Y坐标和Z坐标。
     * 第一个点是上顶点,然后是左下顶点和右下顶点。glEnd()告诉OpenGL没有其他点了。这样将显示一个填充的三角形。
     */
    glBegin(GL_TRIANGLES);                            // 绘制三角形
    /**
     * 要让您的对象绕自身的轴旋转,您必须让对象的中心坐标总是(0.0f,0,0f,0,0f)。下面的代码创建一个绕者其中心轴旋转的金字塔。
     * 金字塔的上顶点高出原点一个单位,底面中心低于原点一个单位。上顶点在底面的投影位于底面的中心。注意所有的面-三角形都是逆时针次序绘制的。
     * 这点十分重要,在以后的课程中我会作出解释。现在,您只需明白要么都逆时针,要么都顺时针,但永远不要将两种次序混在一起,除非您有足够的理由必须这么做。
     *
     * 我们开始画金字塔的前侧面。因为所有的面都共享上顶点,我们将这点在所有的三角形中都设置为红色。底边上的两个顶点的颜色则是互斥的。
     * 前侧面的左下顶点是绿色的,右下顶点是蓝色的。这样相邻右侧面的左下顶点是蓝色的,右下顶点是绿色的。这样四边形的底面上的点的颜色都是间隔排列的。
     */
    /**
     * 如果您还记得上节课的内容,这段代码在屏幕的左半部分绘制三角形。下一行代码是我们第一次使用命令glColor3f(r,g,b)。
     * 括号中的三个参数依次是红、绿、蓝三色分量。取值范围可以从0,0f到1.0f。类似于以前所讲的清除屏幕背景命令。我们将颜色设为红色(纯红色,无绿色,无蓝色)。
     * 接下来的一行代码设置三角形的第一个顶点(三角形的上顶点),并使用当前颜色(红色)来绘制。从现在开始所有的绘制的对象的颜色都是红色,直到我们将红色改变成别的什么颜色。
     */
    glColor3f(1.0f,0.0f,0.0f);			// 红色
    glVertex3f( 0.0f, 1.0f, 0.0f);			// 三角形的上顶点 (前侧面)
    glColor3f(0.0f,1.0f,0.0f);			// 绿色
    glVertex3f(-1.0f,-1.0f, 1.0f);			// 三角形的左下顶点 (前侧面)
    glColor3f(0.0f,0.0f,1.0f);			// 蓝色
    glVertex3f( 1.0f,-1.0f, 1.0f);			// 三角形的右下顶点 (前侧面)

    /**
     * 现在绘制右侧面。注意其底边上的两个顶点的X坐标位于中心右侧的一个单位处。顶点则位于Y轴上的一单位处,且Z坐标正好处于底边的两顶点的Z坐标中心。右侧面从上顶点开始向外侧倾斜至底边上。
     * 这次的左下顶点用蓝色绘制,以保持与前侧面的右下顶点的一致。蓝色将从这个角向金字塔的前侧面和右侧面扩展并与其他颜色混合。
     * 还应注意到后面的三个侧面和前侧面处于同一个glBegin(GL_TRIANGLES) 和 glEnd()语句中间。因为我们是通过三角形来构造这个金字塔的。
     * OpenGL知道每三个点构成一个三角形。当它画完一个三角形之后,如果还有余下的点出现,它就以为新的三角形要开始绘制了。
     * OpenGL在这里并不会将四点画成一个四边形,而是假定新的三角形开始了。所以千万不要无意中增加任何多余的点。
     */
    glColor3f(1.0f,0.0f,0.0f);			// 红色
    glVertex3f( 0.0f, 1.0f, 0.0f);			// 三角形的上顶点 (右侧面)
    glColor3f(0.0f,0.0f,1.0f);			// 蓝色
    glVertex3f( 1.0f,-1.0f, 1.0f);			// 三角形的左下顶点 (右侧面)
    glColor3f(0.0f,1.0f,0.0f);			// 绿色
    glVertex3f( 1.0f,-1.0f, -1.0f);			// 三角形的右下顶点 (右侧面)

    /**
     * 现在是后侧面。再次切换颜色。左下顶点又回到绿色,因为后侧面与右侧面共享这个角。
     */
    glColor3f(1.0f,0.0f,0.0f);			// 红色
    glVertex3f( 0.0f, 1.0f, 0.0f);			// 三角形的上顶点 (后侧面)
    glColor3f(0.0f,1.0f,0.0f);			// 绿色
    glVertex3f( 1.0f,-1.0f, -1.0f);			// 三角形的左下顶点 (后侧面)
    glColor3f(0.0f,0.0f,1.0f);			// 蓝色
    glVertex3f(-1.0f,-1.0f, -1.0f);			// 三角形的右下顶点 (后侧面)

    /**
     * 最后画左侧面。又要切换颜色。左下顶点是蓝色,与后侧面的右下顶点相同。右下顶点是蓝色,与前侧面的左下顶点相同。
     * 到这里金字塔就画完了。因为金字塔只绕着Y轴旋转,我们永远都看不见底面,因而没有必要添加底面。如果您觉得有经验了,尝试增加底面(正方形),并将金字塔绕X轴旋转来看看您是否作对了。
     * 确保底面四个顶点的颜色与侧面的颜色相匹配。
     */
    glColor3f(1.0f,0.0f,0.0f);			// 红色
    glVertex3f( 0.0f, 1.0f, 0.0f);			// 三角形的上顶点 (左侧面)
    glColor3f(0.0f,0.0f,1.0f);			// 蓝色
    glVertex3f(-1.0f,-1.0f,-1.0f);			// 三角形的左下顶点 (左侧面)
    glColor3f(0.0f,1.0f,0.0f);			// 绿色
    glVertex3f(-1.0f,-1.0f, 1.0f);			// 三角形的右下顶点 (左侧面)
    glEnd();                                // 三角形绘制结束
    /**
     * 接下来开始画立方体。他由六个四边形组成。所有的四边形都以逆时针次序绘制。就是说先画右上角,然后左上角、左下角、最后右下角。
     * 您也许认为画立方体的背面的时候这个次序看起来好像顺时针,但别忘了我们从立方体的背后看背面的时候,与您现在所想的正好相反。
     * (译者注:您是从立方体的外面来观察立方体的)。注意到这次我们将立方体移地更远离屏幕了。
     * 因为立方体的大小要比金字塔大,同样移入6个单位时,立方体看起来要大的多。这是透视的缘故。越远的对象看起来越小 :) 。
     */
    /**
     * 您会注意下面的代码中我们增加了另一个glLoadIdentity()调用。目的是为了重置模型观察矩阵。如果我们没有重置,直接调用glTranslate的话,会出现意料之外的结果。
     * 因为坐标轴已经旋转了,很可能没有朝着您所希望的方向。所以我们本来想要左右移动对象的,就可能变成上下移动了,取决于您将坐标轴旋转了多少角度。
     * 试试将glLoadIdentity() 注释掉之后,会出现什么结果。重置模型观察矩阵之后,X,Y,Z轴都以复位,我们调用glTranslate。
     * 您会注意到这次我们只向右一了1.5单位,而不是上节课的3.0单位。因为我们重置场景的时候,焦点又回到了场景的中心(0.0处)。这样就只需向右移1.5单位就够了。
     */
    glLoadIdentity();					// 重置模型观察矩阵
    /**
     * 在屏幕的左半部分画完三角形后,我们要移到右半部分来画正方形。为此要再次使用glTranslate。这次右移,所以X坐标值为正值。
     * 因为前面左移了1.5个单位,这次要先向右移回屏幕中心(1.5个单位),再向右移动1.5个单位。总共要向右移3.0个单位。
     */
    glTranslatef(1.5f,0.0f,-7.0f);				// 先右移再移入屏幕
    glRotatef(rquad,1.0f,0.0f,0.0f);			//  绕X轴旋转四边形
    glColor3f(0.5f, 0.5f, 1.0f);                    // 一次性将当前色设置为蓝色
    /**
     * 现在使用GL_QUADS绘制正方形。与绘制三角形的代码相类似,画四边形也很简单。唯一的区别是用GL_QUADS来替换了GL_TRIANGLES。
     * 并增加了一个点。我们使用顺时针次序来画正方形-左上-右上-右下-左下。采用顺时针绘制的是对象的后表面。
     * 这就是说我们所看见的是正方形的背面。逆时针画出来的正方形才是正面朝着我们的。现在这对您来说并不重要,但以后您必须知道。
     */
    glBegin(GL_QUADS);                            //  绘制正方形
    /**
     * 先画立方体的顶面。从中心上移一单位,注意Y坐标始终为一单位,表示这个四边形与Z轴平行。
     * 先画右上顶点,向右一单位,再屏幕向里一单位。然后左上顶点,向左一单位,再屏幕向里一单位。然后是靠近观察者的左下和右下顶点。就是屏幕往外一单位。
     */
    glColor3f(0.0f,1.0f,0.0f);			// 颜色改为蓝色
    glVertex3f( 1.0f, 1.0f,-1.0f);			// 四边形的右上顶点 (顶面)
    glVertex3f(-1.0f, 1.0f,-1.0f);			// 四边形的左上顶点 (顶面)
    glVertex3f(-1.0f, 1.0f, 1.0f);			// 四边形的左下顶点 (顶面)
    glVertex3f( 1.0f, 1.0f, 1.0f);			// 四边形的右下顶点 (顶面)

    /**
     * 底面的画法和顶面十分类似。只是Y坐标变成了-1。如果我们从立方体的下面来看立方体的话,您会注意到右上角离观察者最近,因此我们先画离观察者最近的顶点。
     * 然后是左上顶点最后才是屏幕里面的左下和右下顶点。如果您真的不在乎绘制多边形的次序(顺时针或者逆时针)的话,您可以直接拷贝顶面的代码,将Y坐标从1改成 -1,也能够工作。
     * 但一旦您进入象纹理映射这样的领域时,忽略绘制次序会导致十分怪异的结果。
     */
    glColor3f(1.0f,0.5f,0.0f);			// 颜色改成橙色
    glVertex3f( 1.0f,-1.0f, 1.0f);			// 四边形的右上顶点(底面)
    glVertex3f(-1.0f,-1.0f, 1.0f);			// 四边形的左上顶点(底面)
    glVertex3f(-1.0f,-1.0f,-1.0f);			// 四边形的左下顶点(底面)
    glVertex3f( 1.0f,-1.0f,-1.0f);			// 四边形的右下顶点(底面)

    /**
     * 接着画立方体的前面。保持Z坐标为一单位,前面正对着我们。
     */
    glColor3f(1.0f,0.0f,0.0f);			// 颜色改成红色
    glVertex3f( 1.0f, 1.0f, 1.0f);			// 四边形的右上顶点(前面)
    glVertex3f(-1.0f, 1.0f, 1.0f);			// 四边形的左上顶点(前面)
    glVertex3f(-1.0f,-1.0f, 1.0f);			// 四边形的左下顶点(前面)
    glVertex3f( 1.0f,-1.0f, 1.0f);			// 四边形的右下顶点(前面)

    /**
     * 立方体后面的绘制方法与前面类似。只是位于屏幕的里面。注意Z坐标现在保持 -1 不变。
     */
    glColor3f(1.0f,1.0f,0.0f);			// 颜色改成黄色
    glVertex3f( 1.0f,-1.0f,-1.0f);			// 四边形的右上顶点(后面)
    glVertex3f(-1.0f,-1.0f,-1.0f);			// 四边形的左上顶点(后面)
    glVertex3f(-1.0f, 1.0f,-1.0f);			// 四边形的左下顶点(后面)
    glVertex3f( 1.0f, 1.0f,-1.0f);			// 四边形的右下顶点(后面)

    /**
     * 还剩两个面就完成了。您会注意到总有一个坐标保持不变。这一次换成了X坐标。因为我们在画左侧面。
     */
    glColor3f(0.0f,0.0f,1.0f);			// 颜色改成蓝色
    glVertex3f(-1.0f, 1.0f, 1.0f);			// 四边形的右上顶点(左面)
    glVertex3f(-1.0f, 1.0f,-1.0f);			// 四边形的左上顶点(左面)
    glVertex3f(-1.0f,-1.0f,-1.0f);			// 四边形的左下顶点(左面)
    glVertex3f(-1.0f,-1.0f, 1.0f);			// 四边形的右下顶点(左面)

    /**
     * 立方体的最后一个面了。X坐标保持为一单位。逆时针绘制。
     */
    glColor3f(1.0f,0.0f,1.0f);			// 颜色改成紫罗兰色
    glVertex3f( 1.0f, 1.0f,-1.0f);			// 四边形的右上顶点(右面)
    glVertex3f( 1.0f, 1.0f, 1.0f);			// 四边形的左上顶点(右面)
    glVertex3f( 1.0f,-1.0f, 1.0f);			// 四边形的左下顶点(右面)
    glVertex3f( 1.0f,-1.0f,-1.0f);			// 四边形的右下顶点(右面)
    glEnd();                                // 正方形绘制结束

    rtri+=0.2f;						// 增加三角形的旋转变量
    rquad-=0.15f;						// 减少四边形的旋转变量
    return TRUE;                                //  一切 OK
}
复制代码

纹理映射

#define TRUE 1
#define FALSE 0

/*
** 利用freeimage加载bmp图像
** 此函数在Linux系统上可以作为常用util调用
*/
GLboolean LoadBmp(const char* filename,
                  AUX_RGBImageRec* texture_image) {
    FREE_IMAGE_FORMAT fifmt = FreeImage_GetFileType(filename, 0);
    FIBITMAP *dib = FreeImage_Load(fifmt, filename, 0);
    dib = FreeImage_ConvertTo24Bits(dib);
    int width = FreeImage_GetWidth(dib);
    int height = FreeImage_GetHeight(dib);
    BYTE *pixels = (BYTE*) FreeImage_GetBits(dib);
    int pix = 0;
    if (texture_image == NULL)
        return FALSE;
    texture_image->data = (BYTE *) malloc(width * height * 3);
    texture_image->sizeX = width;
    texture_image->sizeY = height;
    for (pix = 0; pix < width * height; pix++) {
        texture_image->data[pix * 3 + 0] = pixels[pix * 3 + 2];
        texture_image->data[pix * 3 + 1] = pixels[pix * 3 + 1];
        texture_image->data[pix * 3 + 2] = pixels[pix * 3 + 0];
    }
    FreeImage_Unload(dib);
    return TRUE;
}

/**
 * 下一部分代码载入位图(调用上面的代码)并转换成纹理。
 * @return
 */
int Lesson06::LoadGLTextures() {
    /**
     * 然后设置一个叫做 Status 的变量。我们使用它来跟踪是否能够载入位图以及能否创建纹理。 Status 缺省设为 FALSE (表示没有载入或创建任何东东)。
     */
    int Status = FALSE;                            // 状态指示器
    /**
     * 现在我们创建存储位图的图像记录。次记录包含位图的宽度、高度和数据。
     */
    AUX_RGBImageRec *TextureImage;                    // 创建纹理的存储空间
    TextureImage = (AUX_RGBImageRec *)malloc(sizeof(AUX_RGBImageRec));
    /**
     * 现在载入位图,并将其转换为纹理。 TextureImage[0]=LoadBMP("Data/NeHe.bmp") 调用 LoadBMP() 的代码。
     * 载入 Data 目录下的 NeHe.bmp 位图文件。如果一切正常,图像数据将存放在 TextureImage[0] 中, Status 被设为 TRUE ,然后我们开始创建纹理。
     */
    // 载入位图,检查有无错误,如果位图没找到则退出
    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/earth.bmp", TextureImage)) {
        Status = TRUE;                            // 将 Status 设为 TRUE
        /**
         * 现在使用中 TextureImage[0] 的数据创建纹理。第一行 glGenTextures(1, &texture[0]) 告诉OpenGL我们想生成一个纹理名字(如果您想载入多个纹理,加大数字)。
         * 值得注意的是,开始我们使用 GLuint texture[1] 来创建一个纹理的存储空间,您也许会认为第一个纹理就是存放在 &texture[1] 中的,但这是错的。
         * 正确的地址应该是 &texture[0] 。同样如果使用 GLuint texture[2] 的话,第二个纹理存放在 texture[1] 中。
         */
        /**
         * 第二行 glBindTexture(GL_TEXTURE_2D, texture[0]) 告诉OpenGL将纹理名字 texture[0] 绑定到纹理目标上。
         * 2D纹理只有高度(在 Y 轴上)和宽度(在 X 轴上)。主函数将纹理名字指派给纹理数据。本例中我们告知OpenGL, &texture[0] 处的内存已经可用。我们创建的纹理将存储在 &texture[0] 的 指向的内存区域。
         */
        glGenTextures(1, &texture[0]);                    // 创建纹理

        // 使用来自位图数据生成 的典型纹理
        glBindTexture(GL_TEXTURE_2D, texture[0]);
        /**
         * 下来我们创建真正的纹理。下面一行告诉OpenGL此纹理是一个2D纹理 ( GL_TEXTURE_2D )。
         * 参数“0”代表图像的详细程度,通常就由它为零去了。参数三是数据的成分数。因为图像是由红色数据,绿色数据,蓝色数据三种组分组成。
         * TextureImage[0]->sizeX 是纹理的宽度。如果您知道宽度,您可以在这里填入,但计算机可以很容易的为您指出此值。
         * TextureImage[0]->sizey 是纹理的高度。参数零是边框的值,一般就是“0”。 GL_RGB 告诉OpenGL图像数据由红、绿、蓝三色数据组成。
         * GL_UNSIGNED_BYTE 意味着组成图像的数据是无符号字节类型的。最后... TextureImage[0]->data 告诉OpenGL纹理数据的来源。此例中指向存放在 TextureImage[0] 记录中的数据。
         */
        // 生成纹理
        glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);
        /**
         * 下面的两行告诉OpenGL在显示图像时,当它比放大得原始的纹理大 ( GL_TEXTURE_MAG_FILTER )或缩小得比原始得纹理小( GL_TEXTURE_MIN_FILTER )时OpenGL采用的滤波方式。
         * 通常这两种情况下我都采用 GL_LINEAR 。这使得纹理从很远处到离屏幕很近时都平滑显示。使用 GL_LINEAR 需要CPU和显卡做更多的运算。
         * 如果您的机器很慢,您也许应该采用 GL_NEAREST 。过滤的纹理在放大的时候,看起来斑驳的很『译者注:马赛克啦』。您也可以结合这两种滤波方式。在近处时使用 GL_LINEAR ,远处时 GL_NEAREST 。
         */
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    // 线形滤波
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    // 线形滤波
    }
    /**
     * 现在我们释放前面用来存放位图数据的内存。我们先查看位图数据是否存放在处。如果是的话,再查看数据是否已经存储。如果已经存储的话,删了它。接着再释放 TextureImage[0] 图像结构以保证所有的内存都能释放。
     */
    if (TextureImage)                            // 纹理是否存在
    {
        if (TextureImage->data)                    // 纹理图像是否存在
        {
            free(TextureImage->data);                // 释放纹理图像占用的内存
        }

        free(TextureImage);                        // 释放图像结构
    }
    /**
     * 最后返回状态变量。如果一切OK,变量 Status 的值为 TRUE 。否则为 FALSE 。
     */
    return Status;
}


/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
GLvoid Lesson06::ReSizeGLScene(GLsizei width, GLsizei height)                // 重置OpenGL窗口大小
{
    if (height == 0)                                // 防止被零除
    {
        height = 1;                            // 将Height设为1
    }

    glViewport(0, 0, width, height);                    // 重置当前的视口
    /**
     * 下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。
     * 此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是我们在场景中所能绘制深度的起点和终点。
     *
     * glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。投影矩阵负责为我们的场景增加透视。
     * glLoadIdentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。调用 glLoadIdentity()之后我们为场景设置透视图。
     *
     * glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。模型观察矩阵中存放了我们的物体讯息。最后我们重置模型观察矩阵。
     */
    glMatrixMode(GL_PROJECTION);                        // 选择投影矩阵
    glLoadIdentity();                            // 重置投影矩阵

    // 设置视口的大小
    gluPerspective(45.0f, (GLfloat) width / (GLfloat) height, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);                        // 选择模型观察矩阵
    glLoadIdentity();                            // 重置模型观察矩阵
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * if (!LoadGLTextures()) 这行代码调用上面讲的子例程载入位图并生成纹理。如果因为任何原因 LoadGLTextures() 调用失败,接着的一行返回FALSE。如果一切OK,并且纹理创建好了,我们启用2D纹理映射。
 * 如果您忘记启用的话,您的对象看起来永远都是纯白色,这一定不是什么好事。
 * @return
 */
int Lesson06::InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    if (!LoadGLTextures())							// 调用纹理载入子例程
    {
        return FALSE;							// 如果未能载入,返回FALSE
    }
    glEnable(GL_TEXTURE_2D);						// 启用纹理映射
    /**
     * 下一行启用smooth shading(阴影平滑)。阴影平滑通过多边形精细的混合色彩,并对外部光进行平滑。
     */
    glShadeModel(GL_SMOOTH);                        // 启用阴影平滑
    /**
     * 下一行设置清除屏幕时所用的颜色。如果您对色彩的工作原理不清楚的话,我快速解释一下。
     * 色彩值的范围从0.0f到1.0f。0.0f代表最黑的情况,1.0f就是最亮的情况。
     * glClearColor 后的第一个参数是Red Intensity(红色分量),第二个是绿色,第三个是蓝色。最大值也是1.0f,代表特定颜色分量的最亮情况。最后一个参数是Alpha值。
     *
     * 通过混合三种原色(红、绿、蓝),您可以得到不同的色彩。因此,当您使用glClearColor(0.0f,0.0f,1.0f,0.0f),您将用亮蓝色来清除屏幕。
     * 如果您用 glClearColor(0.5f,0.0f,0.0f,0.0f)的话,您将使用中红色来清除屏幕。不是最亮(1.0f),也不是最暗 (0.0f)。
     * 要得到白色背景,您应该将所有的颜色设成最亮(1.0f)。要黑色背景的话,您该将所有的颜色设为最暗(0.0f)。
     */
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);					// 黑色背景
    /**
     * 接下来的三行必须做的是关于depth buffer(深度缓存)的。将深度缓存设想为屏幕后面的层。深度缓存不断的对物体进入屏幕内部有多深进行跟踪。
     * 我们本节的程序其实没有真正使用深度缓存,但几乎所有在屏幕上显示3D场景OpenGL程序都使用深度缓存。它的排序决定那个物体先画。
     * 这样您就不会将一个圆形后面的正方形画到圆形上来。深度缓存是OpenGL十分重要的部分。
     */
    glClearDepth(1.0f);                            // 设置深度缓存
    glEnable(GL_DEPTH_TEST);                        // 启用深度测试
    glDepthFunc(GL_LEQUAL);                            // 所作深度测试的类型
    /**
     * 接着告诉OpenGL我们希望进行最好的透视修正。这会十分轻微的影响性能。但使得透视图看起来好一点。
     */
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);            // 告诉系统对透视进行修正
    return TRUE;                                // 初始化 OK
}

/**
 * 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。
 * 如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。
 * @return
 */
int Lesson06::DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // 清除屏幕和深度缓存
    glLoadIdentity();                            // 重置当前的模型观察矩阵
    /**
     * 当您调用glLoadIdentity()之后,您实际上将当前点移到了屏幕中心,X坐标轴从左至右,Y坐标轴从下至上,Z坐标轴从里至外。
     * OpenGL屏幕中心的坐标值是X和Y轴上的0.0f点。中心左面的坐标值是负值,右面是正值。移向屏幕顶端是正值,移向屏幕底端是负值。移入屏幕深处是负值,移出屏幕则是正值。
     * glTranslatef(x, y, z)沿着 X, Y 和 Z 轴移动。根据前面的次序,下面的代码沿着X轴左移1.5个单位,Y轴不动(0.0f),最后移入屏幕6.0f个单位。
     * 注意在glTranslatef(x, y, z)中当您移动的时候,您并不是相对屏幕中心移动,而是相对与当前所在的屏幕位置。
     */
    glTranslatef(0.0f,0.0f,-5.0f);						// 移入屏幕 5 个单位
    /**
     * glRotatef(Angle,Xvector,Yvector,Zvector)负责让对象绕某个轴旋转。这个命令有很多用处。 Angle 通常是个变量代表对象转过的角度。
     * Xvector , Yvector 和 Zvector 三个参数则共同决定旋转轴的方向。比如(1,0,0)所描述的矢量经过X坐标轴的1个单位处并且方向向右。(-1,0,0)所描述的矢量经过X坐标轴的1个单位处,但方向向左。
     */
    /**
     * X轴-您正在使用一台台锯。锯片中心的轴从左至右摆放(就像OpenGL中的X轴)。尖利的锯齿绕着X轴狂转,看起来要么向上转,要么向下转。取决于锯片开始转时的方向。这与我们在OpenGL中绕着X轴旋转什么的情形是一样的。
     * Y轴-假设您正处于一个巨大的龙卷风中心,龙卷风的中心从地面指向天空(就像OpenGL中的Y轴)。垃圾和碎片围着Y轴从左向右或是从右向左狂转不止。
     * Z轴-您从正前方看着一台风扇。风扇的中心正好朝着您(就像OpenGL中的Z轴)。风扇的叶片绕着Z轴顺时针或逆时针狂转。
     */
     /**
      * 下面三行使立方体绕X、Y、Z轴旋转。旋转多少依赖于变量 xrot , yrot 和 zrot 的值。
      */
    glRotatef(xrot,1.0f,0.0f,0.0f);						// 绕X轴旋转
    glRotatef(yrot,0.0f,1.0f,0.0f);						// 绕Y轴旋转
    glRotatef(zrot,0.0f,0.0f,1.0f);						// 绕Z轴旋转
    /**
     * 下一行代码选择我们使用的纹理。如果您在您的场景中使用多个纹理,您应该使用来 glBindTexture(GL_TEXTURE_2D, texture[ 所使用纹理对应的数字 ]) 选择要绑定的纹理。
     * 当您想改变纹理时,应该绑定新的纹理。有一点值得指出的是,您不能在 glBegin() 和 glEnd() 之间绑定纹理,必须在 glBegin() 之前或 glEnd() 之后绑定。
     * 注意我们在后面是如何使用 glBindTexture 来指定和绑定纹理的。
     */
    glBindTexture(GL_TEXTURE_2D, texture[0]);				// 选择纹理
    /**
     * 为了将纹理正确的映射到四边形上,您必须将纹理的右上角映射到四边形的右上角,纹理的左上角映射到四边形的左上角,纹理的右下角映射到四边形的右下角,纹理的左下角映射到四边形的左下角。
     * 如果映射错误的话,图像显示时可能上下颠倒,侧向一边或者什么都不是。glTexCoord2f 的第一个参数是X坐标。 0.0f 是纹理的左侧。 0.5f 是纹理的中点, 1.0f 是纹理的右侧。
     * glTexCoord2f 的第二个参数是Y坐标。 0.0f 是纹理的底部。 0.5f 是纹理的中点, 1.0f 是纹理的顶部。所以纹理的左上坐标是 X:0.0f,Y:1.0f ,四边形的左上顶点是 X: -1.0f,Y:1.0f 。
     * 其余三点依此类推。试着玩玩 glTexCoord2f 的X,Y坐标参数。把 1.0f 改为 0.5f 将只显示纹理的左半部分,把 0.0f 改为 0.5f 将只显示纹理的右半部分。
     */
    glBegin(GL_QUADS);
    // 前面
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// 纹理和四边形的左下
    glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// 纹理和四边形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// 纹理和四边形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// 纹理和四边形的左上
    // 后面
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// 纹理和四边形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// 纹理和四边形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// 纹理和四边形的左上
    glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// 纹理和四边形的左下
    // 顶面
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// 纹理和四边形的左上
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// 纹理和四边形的左下
    glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// 纹理和四边形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// 纹理和四边形的右上
    // 底面
    glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// 纹理和四边形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// 纹理和四边形的左上
    glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// 纹理和四边形的左下
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// 纹理和四边形的右下
    // 右面
    glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);	// 纹理和四边形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);	// 纹理和四边形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);	// 纹理和四边形的左上
    glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);	// 纹理和四边形的左下
    // 左面
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);	// 纹理和四边形的左下
    glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);	// 纹理和四边形的右下
    glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);	// 纹理和四边形的右上
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);	// 纹理和四边形的左上
    glEnd();

    /**
     * 现在增加 xrot , yrot 和 zrot 的值。尝试变化每次各变量的改变值来调节立方体的旋转速度,或改变+/-号来调节立方体的旋转方向。
     */
    xrot+=0.3f;								// X 轴旋转
    yrot+=0.2f;								// Y 轴旋转
    zrot+=0.4f;								// Z 轴旋转
    return TRUE;                                //  一切 OK
}
复制代码
关注下面的标签,发现更多相似文章
评论