阅读 6

OpenGL NeHe on Linux 4

蒙板

#define TRUE 1
#define FALSE 0

/*
** 利用freeimage加载bmp图像
** 此函数在Linux系统上可以作为常用util调用
*/
GLboolean Lesson20::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 Lesson20::LoadGLTextures() {
    /**
     * 然后设置一个叫做 Status 的变量。我们使用它来跟踪是否能够载入位图以及能否创建纹理。 Status 缺省设为 FALSE (表示没有载入或创建任何东东)。
     */
    int Status = FALSE;                            // 状态指示器
    /**
     * 现在我们创建存储位图的图像记录。次记录包含位图的宽度、高度和数据。
     */
    AUX_RGBImageRec *TextureImage, *TextureImage2, *TextureImage3, *TextureImage4, *TextureImage5;                    // 创建纹理的存储空间
    TextureImage = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));
    TextureImage2 = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));
    TextureImage3 = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));
    TextureImage4 = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));
    TextureImage5 = (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/Logo.bmp", TextureImage)
        && LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Mask1.bmp", TextureImage2)
        && LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Image1.bmp", TextureImage3)
        && LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Mask2.bmp", TextureImage4)
        && LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Image2.bmp", TextureImage5)) {
        Status = TRUE;                            // 将 Status 设为 TRUE
        glGenTextures(5, &texture[0]);				// 创建5个纹理

        setTexture(texture[0], TextureImage);
        setTexture(texture[1], TextureImage2);
        setTexture(texture[2], TextureImage3);
        setTexture(texture[3], TextureImage4);
        setTexture(texture[4], TextureImage5);
    }
    /**
     * 现在我们释放前面用来存放位图数据的内存。我们先查看位图数据是否存放在处。如果是的话,再查看数据是否已经存储。如果已经存储的话,删了它。接着再释放 TextureImage[0] 图像结构以保证所有的内存都能释放。
     */
    freeTexture(TextureImage);
    freeTexture(TextureImage2);
    freeTexture(TextureImage3);
    freeTexture(TextureImage4);
    freeTexture(TextureImage5);
    /**
     * 最后返回状态变量。如果一切OK,变量 Status 的值为 TRUE 。否则为 FALSE 。
     */
    return Status;
}

void Lesson20::setTexture(GLuint texture, AUX_RGBImageRec *TextureImage) {
    glBindTexture(GL_TEXTURE_2D, texture);
    // 生成纹理
    glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                 TextureImage->data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    // 线形滤波
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    // 线形滤波
}

void Lesson20::freeTexture(AUX_RGBImageRec *TextureImage) {
    if (TextureImage)                            // 纹理是否存在
    {
        if (TextureImage->data)                    // 纹理图像是否存在
        {
            free(TextureImage->data);                // 释放纹理图像占用的内存
        }

        free(TextureImage);                        // 释放图像结构
    }
}

/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
GLvoid Lesson20::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, 200.0f);

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

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * if (!LoadGLTextures()) 这行代码调用上面讲的子例程载入位图并生成纹理。如果因为任何原因 LoadGLTextures() 调用失败,接着的一行返回FALSE。如果一切OK,并且纹理创建好了,我们启用2D纹理映射。
 * 如果您忘记启用的话,您的对象看起来永远都是纯白色,这一定不是什么好事。
 * @return
 */
int Lesson20::InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    if (!LoadGLTextures())                                // Jump To Texture Loading Routine
    {
        return FALSE;                                    // If Texture Didn't Load Return FALSE
    }

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                // Clear The Background Color To Black
    glClearDepth(1.0);                                    // Enables Clearing Of The Depth Buffer
    glEnable(GL_DEPTH_TEST);                            // Enable Depth Testing
    glShadeModel(GL_SMOOTH);                            // Enables Smooth Color Shading
    glEnable(GL_TEXTURE_2D);                            // Enable 2D Texture Mapping
    return TRUE;                                // 初始化 OK
}

/**
 * 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。
 * 如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。
 * @return
 */
int Lesson20::DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);        // Clear Screen And Depth Buffer
    glLoadIdentity();                                        // Reset The ModelView Matrix
    glTranslatef(0.0f, 0.0f, -2.0f);                          // 物体移入屏幕2个单位

    /**
     * 下面一行,我们选择'logo'纹理。我们将要通过四边形把纹理映射到屏幕,并按照顶点的顺序设置纹理坐标。
     * Jonathan Roy说OpenGL是一个基于顶点的图形系统,大部分你设置的参数是作为顶点的属性而记录的,纹理坐标就是这样一种属性。
     * 你只要简单的设置各个顶点的纹理坐标,OpenGL就自动帮你把多边形内部填充纹理,通过一个插值的过程。
     */
    /**
     * 向前面几课一样,我们假定四边形面对我们,并把纹理坐标(0,0)绑定到左下角,(1,0)绑定到右下角,(1,1)绑定到右上角。
     * 给定这些设置,你应该能猜到四边形中间对应的纹理坐标为(0.5,0.5),但你自己并没有设置此处的纹理坐标!
     * OpenGL为你做了计算。在这一课里,我们通过设置纹理坐标达到一种滚动纹理的目的。纹理坐标是被归一化的,它的范围从0.0-1.0,值0被映射到纹理的一边,值1被映射到纹理的另一边。
     * 超过1的值,纹理可以按照不同的方式被映射,这里我们设置为它将回绕道另一边并重复纹理。例如如果使用这样的映射方式,纹理坐标(0.3,0.5)和(1.3,0.5)被映射到同一个纹理坐标。
     * 在这一课里,我们将尝试一种无缝填充的效果。
     */
    /**
     * 我们使用roll变量去设置纹理坐标,当它为0时,它把纹理的左下角映射到四边形的左下角。当它大于0时,把纹理的左上角映射到四边形的左下角,看起来的效果就是纹理沿四边形向上滚动。
     */
    glBindTexture(GL_TEXTURE_2D, texture[0]);                // 选择Logo纹理
    glBegin(GL_QUADS);                            // 绘制纹理四边形
    glTexCoord2f(0.0f, -roll + 0.0f);
    glVertex3f(-1.1f, -1.1f, 0.0f);
    glTexCoord2f(3.0f, -roll + 0.0f);
    glVertex3f(1.1f, -1.1f, 0.0f);
    glTexCoord2f(3.0f, -roll + 3.0f);
    glVertex3f(1.1f, 1.1f, 0.0f);
    glTexCoord2f(0.0f, -roll + 3.0f);
    glVertex3f(-1.1f, 1.1f, 0.0f);
    glEnd();

    glEnable(GL_BLEND);                            // 启用混合
    glDisable(GL_DEPTH_TEST);                            // 禁用深度测试

    if (masking) {                              // 是否启用“掩模”
        /**
         * 如果启用了“掩模”,我们将要设置“掩模”的混合系数。一个“掩模”只是一幅绘制到屏幕的纹理图片,但只有黑色和白色。白色的部分代表透明,黑色的部分代表不透明。
         * 下面这个混合系数使得,任何对应“掩模”黑色的部分会变为黑色,白色的部分会保持原来的颜色。
         */
        glBlendFunc(GL_DST_COLOR, GL_ZERO);
    }

    /**
     * 现在我们检查绘制那一个层,如果为True绘制第二个层,否则绘制第一个层
     */
    if (scene) {
        /**
         * 为了不使它看起来显得非常大,我们把它移入屏幕一个单位,并把它按roll变量的值进行旋转(沿Z轴)。
         */
        glTranslatef(0.0f, 0.0f, -1.0f);        // 移入屏幕一个单位
        glRotatef(roll * 360, 0.0f, 0.0f, 1.0f);       // 沿Z轴旋转

        if (masking) {                        // “掩模”是否打开
            /**
             * 如果“掩模打开”,我们会把掩模绘制到屏幕。当我们完成这个操作时,将会看到一个镂空的纹理出现在屏幕上。
             */
            glBindTexture(GL_TEXTURE_2D, texture[3]);        // 选择第二个“掩模”纹理
            glBegin(GL_QUADS);                    // 开始绘制四边形
            glTexCoord2f(0.0f, 0.0f);
            glVertex3f(-1.1f, -1.1f, 0.0f);
            glTexCoord2f(1.0f, 0.0f);
            glVertex3f(1.1f, -1.1f, 0.0f);
            glTexCoord2f(1.0f, 1.0f);
            glVertex3f(1.1f, 1.1f, 0.0f);
            glTexCoord2f(0.0f, 1.0f);
            glVertex3f(-1.1f, 1.1f, 0.0f);
            glEnd();
        }

        /**
         * 当我们把“掩模”绘制到屏幕上后,接着我们变换混合系数。这次我们告诉OpenGL把任何黑色部分对应的像素复制到屏幕,这样看起来纹理就像被镂空一样帖子屏幕上。
         * 注意,我们在变换了混合模式后在选择的纹理。如果我们没有使用“掩模”,我们的图像将与屏幕颜色混合。
         */
        glBlendFunc(GL_ONE, GL_ONE);                // 把纹理2复制到屏幕
        glBindTexture(GL_TEXTURE_2D, texture[4]);            // 选择第二个纹理
        glBegin(GL_QUADS);                        // 绘制四边形
        glTexCoord2f(0.0f, 0.0f);
        glVertex3f(-1.1f, -1.1f, 0.0f);
        glTexCoord2f(1.0f, 0.0f);
        glVertex3f(1.1f, -1.1f, 0.0f);
        glTexCoord2f(1.0f, 1.0f);
        glVertex3f(1.1f, 1.1f, 0.0f);
        glTexCoord2f(0.0f, 1.0f);
        glVertex3f(-1.1f, 1.1f, 0.0f);
        glEnd();
    } else {
        /**
         * 如果“掩模打开”,我们会把掩模绘制到屏幕。当我们完成这个操作时,将会看到一个镂空的纹理出现在屏幕上。
         */
        if (masking) {
            glBindTexture(GL_TEXTURE_2D, texture[1]);        // 选择第一个“掩模”纹理
            glBegin(GL_QUADS);                    // 开始绘制四边形
            glTexCoord2f(roll + 0.0f, 0.0f);
            glVertex3f(-1.1f, -1.1f, 0.0f);
            glTexCoord2f(roll + 4.0f, 0.0f);
            glVertex3f(1.1f, -1.1f, 0.0f);
            glTexCoord2f(roll + 4.0f, 4.0f);
            glVertex3f(1.1f, 1.1f, 0.0f);
            glTexCoord2f(roll + 0.0f, 4.0f);
            glVertex3f(-1.1f, 1.1f, 0.0f);
            glEnd();
        }

        /**
         * 当我们把“掩模”绘制到屏幕上后,接着我们变换混合系数。这次我们告诉OpenGL把任何黑色部分对应的像素复制到屏幕,这样看起来纹理就像被镂空一样帖子屏幕上。
         * 注意,我们在变换了混合模式后在选择的纹理。如果我们没有使用“掩模”,我们的图像将与屏幕颜色混合。
         */
        glBlendFunc(GL_ONE, GL_ONE);                    // 把纹理1复制到屏幕
        glBindTexture(GL_TEXTURE_2D, texture[2]);                // 选择第一个纹理
        glBegin(GL_QUADS);                            // 开始绘制四边形
        glTexCoord2f(roll + 0.0f, 0.0f);
        glVertex3f(-1.1f, -1.1f, 0.0f);
        glTexCoord2f(roll + 4.0f, 0.0f);
        glVertex3f(1.1f, -1.1f, 0.0f);
        glTexCoord2f(roll + 4.0f, 4.0f);
        glVertex3f(1.1f, 1.1f, 0.0f);
        glTexCoord2f(roll + 0.0f, 4.0f);
        glVertex3f(-1.1f, 1.1f, 0.0f);
        glEnd();
    }

    glEnable(GL_DEPTH_TEST);                            // 启用深度测试
    glDisable(GL_BLEND);                            // 禁用混合

    roll += 0.002f;                                // 增加纹理滚动变量
    if (roll > 1.0f)                                // 大于1则减1
    {
        roll -= 1.0f;
    }
    return TRUE;                                //  一切 OK
}
复制代码

凹凸映射,多重纹理

#define TRUE 1
#define FALSE 0

bool Lesson22::isInString(char *string, const char *search) {
    int pos = 0;
    int maxpos = strlen(search) - 1;
    int len = strlen(string);
    char *other;
    for (int i = 0; i < len; i++) {
        if ((i == 0) || ((i > 1) && string[i - 1] == '\n')) {                // 新的扩展名开始与这里
            other = &string[i];
            pos = 0;                            // 开始新的比较
            while (string[i] != '\n') {                    // 比较整个扩展名
                if (string[i] == search[pos]) pos++;            // 下一个字符
                if ((pos > maxpos) && string[i + 1] == '\n') return true;    // 如果整个扩展名相同则成功返回
                i++;
            }
        }
    }
    return false;                                // 没找到
}

bool Lesson22::initMultitexture(void) {
    char *extensions;
    extensions = strdup((char *) glGetString(GL_EXTENSIONS));            // 返回扩展名字符串
    int len = strlen(extensions);
    for (int i = 0; i < len; i++)                            // 使用'\n'分割各个扩展名
        if (extensions[i] == ' ') extensions[i] = '\n';

#ifdef EXT_INFO
    MessageBox(hWnd,extensions,"supported GL extensions",MB_OK | MB_ICONINFORMATION);
#endif

    if (isInString(extensions, "GL_ARB_multitexture")                // 是否支持多重纹理扩展?
        && __ARB_ENABLE                            // 是否使用多重纹理扩展?
        && isInString(extensions, "GL_EXT_texture_env_combine"))        // 是否支持纹理环境混合
    {
        glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTexelUnits);
        glMultiTexCoord1fARB = (PFNGLMULTITEXCOORD1FARBPROC) glutGetProcAddress("glMultiTexCoord1fARB");
        glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) glutGetProcAddress("glMultiTexCoord2fARB");
        glMultiTexCoord3fARB = (PFNGLMULTITEXCOORD3FARBPROC) glutGetProcAddress("glMultiTexCoord3fARB");
        glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC) glutGetProcAddress("glMultiTexCoord4fARB");
        glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) glutGetProcAddress("glActiveTextureARB");
        glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) glutGetProcAddress("glClientActiveTextureARB");
#ifdef EXT_INFO
        MessageBox(hWnd,"The GL_ARB_multitexture 扩展被使用.","支持多重纹理",MB_OK | MB_ICONINFORMATION);
#endif

        return true;
    }
    useMultitexture = false;                            // 如果不支持多重纹理则返回false
    return false;
}

void Lesson22::initLights(void) {
    glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
    glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
    glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
    glEnable(GL_LIGHT1);
}

/*
** 利用freeimage加载bmp图像
** 此函数在Linux系统上可以作为常用util调用
*/
GLboolean Lesson22::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 Lesson22::LoadGLTextures() {
    /**
     * 然后设置一个叫做 Status 的变量。我们使用它来跟踪是否能够载入位图以及能否创建纹理。 Status 缺省设为 FALSE (表示没有载入或创建任何东东)。
     */
    int Status = FALSE;                            // 状态指示器
    /**
     * 现在我们创建存储位图的图像记录。次记录包含位图的宽度、高度和数据。
     */
    AUX_RGBImageRec *TextureImage;                    // 创建纹理的存储空间
    TextureImage = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));

    char *alpha = NULL;

    /**
     * 现在载入位图,并将其转换为纹理。 TextureImage[0]=LoadBMP("Data/NeHe.bmp") 调用 LoadBMP() 的代码。
     * 载入 Data 目录下的 NeHe.bmp 位图文件。如果一切正常,图像数据将存放在 TextureImage[0] 中, Status 被设为 TRUE ,然后我们开始创建纹理。
     */
    // 载入位图,检查有无错误,如果位图没找到则退出
    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Base.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(3, &texture[0]);                    // 创建纹理

        // 创建使用临近过滤器过滤得纹理
        glBindTexture(GL_TEXTURE_2D, texture[0]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // 创建使用线形过滤器过滤得纹理
        glBindTexture(GL_TEXTURE_2D, texture[1]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // 创建使用线形Mipmap过滤器过滤得纹理
        glBindTexture(GL_TEXTURE_2D, texture[2]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);
    }
    /**
     * 现在我们释放前面用来存放位图数据的内存。我们先查看位图数据是否存放在处。如果是的话,再查看数据是否已经存储。如果已经存储的话,删了它。接着再释放 TextureImage[0] 图像结构以保证所有的内存都能释放。
     */
    if (TextureImage)                            // 纹理是否存在
    {
        if (TextureImage->data)                    // 纹理图像是否存在
        {
            free(TextureImage->data);                // 释放纹理图像占用的内存
        }

        free(TextureImage);                        // 释放图像结构
    }


    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Bump.bmp", TextureImage)) {
        Status = TRUE;                            // 将 Status 设为 TRUE

        glPixelTransferf(GL_RED_SCALE, 0.5f);                        // Scale RGB By 50%, So That We Have Only
        glPixelTransferf(GL_GREEN_SCALE, 0.5f);                        // Half Intenstity
        glPixelTransferf(GL_BLUE_SCALE, 0.5f);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);    // No Wrapping, Please!
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, Gray);

        /**
         * 现在使用中 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(3, &bump[0]);                    // 创建纹理

        // Create Nearest Filtered Texture
        glBindTexture(GL_TEXTURE_2D, bump[0]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // Create Linear Filtered Texture
        glBindTexture(GL_TEXTURE_2D, bump[1]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // Create MipMapped Texture
        glBindTexture(GL_TEXTURE_2D, bump[2]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        for (int i = 0; i < 3 * TextureImage->sizeX * TextureImage->sizeY; i++)        // Invert The Bumpmap
            TextureImage->data[i] = 255 - TextureImage->data[i];

        glGenTextures(3, invbump);                                // Create Three Textures

        // Create Nearest Filtered Texture
        glBindTexture(GL_TEXTURE_2D, invbump[0]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // Create Linear Filtered Texture
        glBindTexture(GL_TEXTURE_2D, invbump[1]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // Create MipMapped Texture
        glBindTexture(GL_TEXTURE_2D, invbump[2]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE,
                          TextureImage->data);

        glPixelTransferf(GL_RED_SCALE, 1.0f);                // Scale RGB Back To 100% Again
        glPixelTransferf(GL_GREEN_SCALE, 1.0f);
        glPixelTransferf(GL_BLUE_SCALE, 1.0f);
    }
    /**
     * 现在我们释放前面用来存放位图数据的内存。我们先查看位图数据是否存放在处。如果是的话,再查看数据是否已经存储。如果已经存储的话,删了它。接着再释放 TextureImage[0] 图像结构以保证所有的内存都能释放。
     */
    if (TextureImage)                            // 纹理是否存在
    {
        if (TextureImage->data)                    // 纹理图像是否存在
        {
            free(TextureImage->data);                // 释放纹理图像占用的内存
        }

        free(TextureImage);                        // 释放图像结构
    }

    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/OpenGL_Alpha.bmp", TextureImage)) {
        Status = TRUE;                            // 将 Status 设为 TRUE

        alpha = new char[4 * TextureImage->sizeX * TextureImage->sizeY];        // Create Memory For RGBA8-Texture
        for (int a = 0; a < TextureImage->sizeX * TextureImage->sizeY; a++)
            alpha[4 * a + 3] = TextureImage->data[a * 3];                    // Pick Only Red Value As Alpha!
        if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/OpenGL.bmp", TextureImage)) {
            for (int a = 0; a < TextureImage->sizeX * TextureImage->sizeY; a++) {
                alpha[4 * a] = TextureImage->data[a * 3];                    // R
                alpha[4 * a + 1] = TextureImage->data[a * 3 + 1];                // G
                alpha[4 * a + 2] = TextureImage->data[a * 3 + 2];                // B
            }

            glGenTextures(1, &glLogo);                            // Create One Textures

            // Create Linear Filtered RGBA8-Texture
            glBindTexture(GL_TEXTURE_2D, glLogo);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGBA,
                         GL_UNSIGNED_BYTE,
                         alpha);
            delete alpha;
        }
    }
    /**
     * 现在我们释放前面用来存放位图数据的内存。我们先查看位图数据是否存放在处。如果是的话,再查看数据是否已经存储。如果已经存储的话,删了它。接着再释放 TextureImage[0] 图像结构以保证所有的内存都能释放。
     */
    if (TextureImage)                            // 纹理是否存在
    {
        if (TextureImage->data)                    // 纹理图像是否存在
        {
            free(TextureImage->data);                // 释放纹理图像占用的内存
        }

        free(TextureImage);                        // 释放图像结构
    }


    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Multi_On_Alpha.bmp", TextureImage)) {
        Status = TRUE;                            // 将 Status 设为 TRUE

        alpha = new char[4 * TextureImage->sizeX * TextureImage->sizeY];        // Create Memory For RGBA8-Texture
        for (int a = 0; a < TextureImage->sizeX * TextureImage->sizeY; a++)
            alpha[4 * a + 3] = TextureImage->data[a * 3];                    // Pick Only Red Value As Alpha!
        if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Multi_On.bmp", TextureImage)) {
            for (int a = 0; a < TextureImage->sizeX * TextureImage->sizeY; a++) {
                alpha[4 * a] = TextureImage->data[a * 3];                    // R
                alpha[4 * a + 1] = TextureImage->data[a * 3 + 1];                // G
                alpha[4 * a + 2] = TextureImage->data[a * 3 + 2];                // B
            }

            glGenTextures(1, &multiLogo);                            // Create One Textures

            // Create Linear Filtered RGBA8-Texture
            glBindTexture(GL_TEXTURE_2D, multiLogo);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGBA,
                         GL_UNSIGNED_BYTE,
                         alpha);
            delete alpha;
        }
    }

    /**
     * 最后返回状态变量。如果一切OK,变量 Status 的值为 TRUE 。否则为 FALSE 。
     */
    return Status;
}


/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
GLvoid Lesson22::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();                            // 重置模型观察矩阵
}

void Lesson22::doCube(void) {
    int i;
    glBegin(GL_QUADS);
    // Front Face
    glNormal3f(0.0f, 0.0f, +1.0f);
    for (i = 0; i < 4; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Back Face
    glNormal3f(0.0f, 0.0f, -1.0f);
    for (i = 4; i < 8; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Top Face
    glNormal3f(0.0f, 1.0f, 0.0f);
    for (i = 8; i < 12; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Bottom Face
    glNormal3f(0.0f, -1.0f, 0.0f);
    for (i = 12; i < 16; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Right face
    glNormal3f(1.0f, 0.0f, 0.0f);
    for (i = 16; i < 20; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Left Face
    glNormal3f(-1.0f, 0.0f, 0.0f);
    for (i = 20; i < 24; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    glEnd();
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * if (!LoadGLTextures()) 这行代码调用上面讲的子例程载入位图并生成纹理。如果因为任何原因 LoadGLTextures() 调用失败,接着的一行返回FALSE。如果一切OK,并且纹理创建好了,我们启用2D纹理映射。
 * 如果您忘记启用的话,您的对象看起来永远都是纯白色,这一定不是什么好事。
 * @return
 */
int Lesson22::InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    multitextureSupported=initMultitexture();
    if (!LoadGLTextures()) return false;				// Jump To Texture Loading Routine

    glEnable(GL_TEXTURE_2D);							// Enable Texture Mapping
    glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);				// Black Background
    glClearDepth(1.0f);									// Depth Buffer Setup
    glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
    glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations

    initLights();										// Initialize OpenGL Light
    return TRUE;                                // 初始化 OK
}

void Lesson22::VMatMult(GLfloat *M, GLfloat *v) {
    GLfloat res[3];
    res[0]=M[ 0]*v[0]+M[ 1]*v[1]+M[ 2]*v[2]+M[ 3]*v[3];
    res[1]=M[ 4]*v[0]+M[ 5]*v[1]+M[ 6]*v[2]+M[ 7]*v[3];
    res[2]=M[ 8]*v[0]+M[ 9]*v[1]+M[10]*v[2]+M[11]*v[3];;
    v[0]=res[0];
    v[1]=res[1];
    v[2]=res[2];
    v[3]=M[15];											// Homogenous Coordinate
}

void Lesson22::SetUpBumps(GLfloat *n, GLfloat *c, GLfloat *l, GLfloat *s, GLfloat *t) {
    GLfloat v[3];							// Vertex From Current Position To Light
    GLfloat lenQ;							// Used To Normalize

    // Calculate v From Current Vector c To Lightposition And Normalize v
    v[0]=l[0]-c[0];
    v[1]=l[1]-c[1];
    v[2]=l[2]-c[2];
    lenQ=(GLfloat) sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]);
    v[0]/=lenQ;		v[1]/=lenQ;		v[2]/=lenQ;
    // Project v Such That We Get Two Values Along Each Texture-Coordinat Axis.
    c[0]=(s[0]*v[0]+s[1]*v[1]+s[2]*v[2])*MAX_EMBOSS;
    c[1]=(t[0]*v[0]+t[1]*v[1]+t[2]*v[2])*MAX_EMBOSS;
}

void Lesson22::doLogo() {
    glDepthFunc(GL_ALWAYS);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glDisable(GL_LIGHTING);
    glLoadIdentity();
    glBindTexture(GL_TEXTURE_2D,glLogo);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f,0.0f);	glVertex3f(0.23f, -0.4f,-1.0f);
    glTexCoord2f(1.0f,0.0f);	glVertex3f(0.53f, -0.4f,-1.0f);
    glTexCoord2f(1.0f,1.0f);	glVertex3f(0.53f, -0.25f,-1.0f);
    glTexCoord2f(0.0f,1.0f);	glVertex3f(0.23f, -0.25f,-1.0f);
    glEnd();
    if (useMultitexture) {
        glBindTexture(GL_TEXTURE_2D,multiLogo);
        glBegin(GL_QUADS);
        glTexCoord2f(0.0f,0.0f);	glVertex3f(-0.53f, -0.4f,-1.0f);
        glTexCoord2f(1.0f,0.0f);	glVertex3f(-0.33f, -0.4f,-1.0f);
        glTexCoord2f(1.0f,1.0f);	glVertex3f(-0.33f, -0.3f,-1.0f);
        glTexCoord2f(0.0f,1.0f);	glVertex3f(-0.53f, -0.3f,-1.0f);
        glEnd();
    }
    glDepthFunc(GL_LEQUAL);
}

bool Lesson22::doMesh1TexelUnits() {
    GLfloat c[4]={0.0f,0.0f,0.0f,1.0f};					// Holds Current Vertex
    GLfloat n[4]={0.0f,0.0f,0.0f,1.0f};					// Normalized Normal Of Current Surface
    GLfloat s[4]={0.0f,0.0f,0.0f,1.0f};					// s-Texture Coordinate Direction, Normalized
    GLfloat t[4]={0.0f,0.0f,0.0f,1.0f};					// t-Texture Coordinate Direction, Normalized
    GLfloat l[4];										// Holds Our Lightposition To Be Transformed Into Object Space
    GLfloat Minv[16];									// Holds The Inverted Modelview Matrix To Do So.
    int i;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer

    // Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
    // Simply Build It By Doing All Transformations Negated And In Reverse Order.
    glLoadIdentity();
    glRotatef(-yrot,0.0f,1.0f,0.0f);
    glRotatef(-xrot,1.0f,0.0f,0.0f);
    glTranslatef(0.0f,0.0f,-z);
    glGetFloatv(GL_MODELVIEW_MATRIX,Minv);
    glLoadIdentity();
    glTranslatef(0.0f,0.0f,z);

    glRotatef(xrot,1.0f,0.0f,0.0f);
    glRotatef(yrot,0.0f,1.0f,0.0f);

    // Transform The Lightposition Into Object Coordinates:
    l[0]=LightPosition[0];
    l[1]=LightPosition[1];
    l[2]=LightPosition[2];
    l[3]=1.0f;											// Homogenous Coordinate
    VMatMult(Minv,l);

/*	PASS#1: Use Texture "Bump"
			No Blend
			No Lighting
			No Offset Texture-Coordinates */
    glBindTexture(GL_TEXTURE_2D, bump[filter]);
    glDisable(GL_BLEND);
    glDisable(GL_LIGHTING);
    doCube();

/* PASS#2:	Use Texture "Invbump"
			Blend GL_ONE To GL_ONE
			No Lighting
			Offset Texture Coordinates
			*/
    glBindTexture(GL_TEXTURE_2D,invbump[filter]);
    glBlendFunc(GL_ONE,GL_ONE);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_BLEND);

    glBegin(GL_QUADS);
    // Front Face
    n[0]=0.0f;		n[1]=0.0f;		n[2]=1.0f;
    s[0]=1.0f;		s[1]=0.0f;		s[2]=0.0f;
    t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
    for (i=0; i<4; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Back Face
    n[0]=0.0f;		n[1]=0.0f;		n[2]=-1.0f;
    s[0]=-1.0f;		s[1]=0.0f;		s[2]=0.0f;
    t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
    for (i=4; i<8; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Top Face
    n[0]=0.0f;		n[1]=1.0f;		n[2]=0.0f;
    s[0]=1.0f;		s[1]=0.0f;		s[2]=0.0f;
    t[0]=0.0f;		t[1]=0.0f;		t[2]=-1.0f;
    for (i=8; i<12; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Bottom Face
    n[0]=0.0f;		n[1]=-1.0f;		n[2]=0.0f;
    s[0]=-1.0f;		s[1]=0.0f;		s[2]=0.0f;
    t[0]=0.0f;		t[1]=0.0f;		t[2]=-1.0f;
    for (i=12; i<16; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Right Face
    n[0]=1.0f;		n[1]=0.0f;		n[2]=0.0f;
    s[0]=0.0f;		s[1]=0.0f;		s[2]=-1.0f;
    t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
    for (i=16; i<20; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Left Face
    n[0]=-1.0f;		n[1]=0.0f;		n[2]=0.0f;
    s[0]=0.0f;		s[1]=0.0f;		s[2]=1.0f;
    t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
    for (i=20; i<24; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    glEnd();

/* PASS#3:	Use Texture "Base"
			Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
			Lighting Enabled
			No Offset Texture-Coordinates
			*/
    if (!emboss) {
        glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        glBindTexture(GL_TEXTURE_2D,texture[filter]);
        glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
        glEnable(GL_LIGHTING);
        doCube();
    }

    xrot+=xspeed;
    yrot+=yspeed;
    if (xrot>360.0f) xrot-=360.0f;
    if (xrot<0.0f) xrot+=360.0f;
    if (yrot>360.0f) yrot-=360.0f;
    if (yrot<0.0f) yrot+=360.0f;

/*	LAST PASS:	Do The Logos! */
    doLogo();

    return true;										// Keep Going
}

bool Lesson22::doMesh2TexelUnits(void) {

    GLfloat c[4]={0.0f,0.0f,0.0f,1.0f};					// holds current vertex
    GLfloat n[4]={0.0f,0.0f,0.0f,1.0f};					// normalized normal of current surface
    GLfloat s[4]={0.0f,0.0f,0.0f,1.0f};					// s-texture coordinate direction, normalized
    GLfloat t[4]={0.0f,0.0f,0.0f,1.0f};					// t-texture coordinate direction, normalized
    GLfloat l[4];										// holds our lightposition to be transformed into object space
    GLfloat Minv[16];									// holds the inverted modelview matrix to do so.
    int i;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer

    // Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
    // Simply Build It By Doing All Transformations Negated And In Reverse Order.
    glLoadIdentity();
    glRotatef(-yrot,0.0f,1.0f,0.0f);
    glRotatef(-xrot,1.0f,0.0f,0.0f);
    glTranslatef(0.0f,0.0f,-z);
    glGetFloatv(GL_MODELVIEW_MATRIX,Minv);
    glLoadIdentity();
    glTranslatef(0.0f,0.0f,z);

    glRotatef(xrot,1.0f,0.0f,0.0f);
    glRotatef(yrot,0.0f,1.0f,0.0f);

    // Transform The Lightposition Into Object Coordinates:
    l[0]=LightPosition[0];
    l[1]=LightPosition[1];
    l[2]=LightPosition[2];
    l[3]=1.0f;											// Homogenous Coordinate
    VMatMult(Minv,l);

/*	PASS#1: Texel-Unit 0:	Use Texture "Bump"
							No Blend
							No Lighting
							No Offset Texture-Coordinates
							Texture-Operation "Replace"
			Texel-Unit 1:	Use Texture "Invbump"
							No Lighting
							Offset Texture Coordinates
							Texture-Operation "Replace"
*/
    // TEXTURE-UNIT #0
    glActiveTextureARB(GL_TEXTURE0_ARB);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, bump[filter]);
    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
    glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);
    // TEXTURE-UNIT #1:
    glActiveTextureARB(GL_TEXTURE1_ARB);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, invbump[filter]);
    glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
    glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
    // General Switches:
    glDisable(GL_BLEND);
    glDisable(GL_LIGHTING);
    glBegin(GL_QUADS);
    // Front Face
    n[0]=0.0f;		n[1]=0.0f;		n[2]=1.0f;
    s[0]=1.0f;		s[1]=0.0f;		s[2]=0.0f;
    t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
    for (i=0; i<4; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Back Face
    n[0]=0.0f;		n[1]=0.0f;		n[2]=-1.0f;
    s[0]=-1.0f;		s[1]=0.0f;		s[2]=0.0f;
    t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
    for (i=4; i<8; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Top Face
    n[0]=0.0f;		n[1]=1.0f;		n[2]=0.0f;
    s[0]=1.0f;		s[1]=0.0f;		s[2]=0.0f;
    t[0]=0.0f;		t[1]=0.0f;		t[2]=-1.0f;
    for (i=8; i<12; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]     );
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Bottom Face
    n[0]=0.0f;		n[1]=-1.0f;		n[2]=0.0f;
    s[0]=-1.0f;		s[1]=0.0f;		s[2]=0.0f;
    t[0]=0.0f;		t[1]=0.0f;		t[2]=-1.0f;
    for (i=12; i<16; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]     );
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Right Face
    n[0]=1.0f;		n[1]=0.0f;		n[2]=0.0f;
    s[0]=0.0f;		s[1]=0.0f;		s[2]=-1.0f;
    t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
    for (i=16; i<20; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]     );
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    // Left Face
    n[0]=-1.0f;		n[1]=0.0f;		n[2]=0.0f;
    s[0]=0.0f;		s[1]=0.0f;		s[2]=1.0f;
    t[0]=0.0f;		t[1]=1.0f;		t[2]=0.0f;
    for (i=20; i<24; i++) {
        c[0]=data[5*i+2];
        c[1]=data[5*i+3];
        c[2]=data[5*i+4];
        SetUpBumps(n,c,l,s,t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i]     , data[5*i+1]     );
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]);
        glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]);
    }
    glEnd();

/* PASS#2	Use Texture "Base"
			Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
			Lighting Enabled
			No Offset Texture-Coordinates
			*/
    glActiveTextureARB(GL_TEXTURE1_ARB);
    glDisable(GL_TEXTURE_2D);
    glActiveTextureARB(GL_TEXTURE0_ARB);
    if (!emboss) {
        glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        glBindTexture(GL_TEXTURE_2D,texture[filter]);
        glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
        glEnable(GL_BLEND);
        glEnable(GL_LIGHTING);
        doCube();
    }

    xrot+=xspeed;
    yrot+=yspeed;
    if (xrot>360.0f) xrot-=360.0f;
    if (xrot<0.0f) xrot+=360.0f;
    if (yrot>360.0f) yrot-=360.0f;
    if (yrot<0.0f) yrot+=360.0f;

/* LAST PASS:	Do The Logos! */
    doLogo();

    return true;										// Keep Going
}

bool Lesson22::doMeshNoBumps(void) {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);	// Clear The Screen And The Depth Buffer
    glLoadIdentity();									// Reset The View
    glTranslatef(0.0f,0.0f,z);

    glRotatef(xrot,1.0f,0.0f,0.0f);
    glRotatef(yrot,0.0f,1.0f,0.0f);
    if (useMultitexture) {
        glActiveTextureARB(GL_TEXTURE1_ARB);
        glDisable(GL_TEXTURE_2D);
        glActiveTextureARB(GL_TEXTURE0_ARB);
    }
    glDisable(GL_BLEND);
    glBindTexture(GL_TEXTURE_2D,texture[filter]);
    glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
    glEnable(GL_LIGHTING);
    doCube();

    xrot+=xspeed;
    yrot+=yspeed;
    if (xrot>360.0f) xrot-=360.0f;
    if (xrot<0.0f) xrot+=360.0f;
    if (yrot>360.0f) yrot-=360.0f;
    if (yrot<0.0f) yrot+=360.0f;

/* LAST PASS:	Do The Logos! */
    doLogo();

    return true;										// Keep Going
}

int Lesson22::DrawGLScene(GLvoid)								// Here's Where We Do All The Drawing
{	if (bumps) {
        if (useMultitexture && maxTexelUnits>1)
            return doMesh2TexelUnits();
        else return doMesh1TexelUnits();
    }
    else return doMeshNoBumps();
}
复制代码

凹凸映射,多重纹理

#define TRUE 1
#define FALSE 0

bool Lesson22::isInString(char *string, const char *search) {
    int pos = 0;
    int maxpos = strlen(search) - 1;
    int len = strlen(string);
    char *other;
    for (int i = 0; i < len; i++) {
        if ((i == 0) || ((i > 1) && string[i - 1] == '\n')) {                // 新的扩展名开始与这里
            other = &string[i];
            pos = 0;                            // 开始新的比较
            while (string[i] != '\n') {                    // 比较整个扩展名
                if (string[i] == search[pos]) pos++;            // 下一个字符
                if ((pos > maxpos) && string[i + 1] == '\n') return true;    // 如果整个扩展名相同则成功返回
                i++;
            }
        }
    }
    return false;                                // 没找到
}

bool Lesson22::initMultitexture(void) {
    char *extensions;
    extensions = strdup((char *) glGetString(GL_EXTENSIONS));            // 返回扩展名字符串
    int len = strlen(extensions);
    for (int i = 0; i < len; i++)                            // 使用'\n'分割各个扩展名
        if (extensions[i] == ' ') extensions[i] = '\n';

#ifdef EXT_INFO
    MessageBox(hWnd,extensions,"supported GL extensions",MB_OK | MB_ICONINFORMATION);
#endif

    if (isInString(extensions, "GL_ARB_multitexture")                // 是否支持多重纹理扩展?
        && __ARB_ENABLE                            // 是否使用多重纹理扩展?
        && isInString(extensions, "GL_EXT_texture_env_combine"))        // 是否支持纹理环境混合
    {
        glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTexelUnits);
        glMultiTexCoord1fARB = (PFNGLMULTITEXCOORD1FARBPROC) glutGetProcAddress("glMultiTexCoord1fARB");
        glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) glutGetProcAddress("glMultiTexCoord2fARB");
        glMultiTexCoord3fARB = (PFNGLMULTITEXCOORD3FARBPROC) glutGetProcAddress("glMultiTexCoord3fARB");
        glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC) glutGetProcAddress("glMultiTexCoord4fARB");
        glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) glutGetProcAddress("glActiveTextureARB");
        glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) glutGetProcAddress("glClientActiveTextureARB");
#ifdef EXT_INFO
        MessageBox(hWnd,"The GL_ARB_multitexture 扩展被使用.","支持多重纹理",MB_OK | MB_ICONINFORMATION);
#endif

        return true;
    }
    useMultitexture = false;                            // 如果不支持多重纹理则返回false
    return false;
}

void Lesson22::initLights(void) {
    glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
    glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
    glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
    glEnable(GL_LIGHT1);
}

/*
** 利用freeimage加载bmp图像
** 此函数在Linux系统上可以作为常用util调用
*/
GLboolean Lesson22::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 Lesson22::LoadGLTextures() {
    /**
     * 然后设置一个叫做 Status 的变量。我们使用它来跟踪是否能够载入位图以及能否创建纹理。 Status 缺省设为 FALSE (表示没有载入或创建任何东东)。
     */
    int Status = FALSE;                            // 状态指示器
    /**
     * 现在我们创建存储位图的图像记录。次记录包含位图的宽度、高度和数据。
     */
    AUX_RGBImageRec *TextureImage;                    // 创建纹理的存储空间
    TextureImage = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));

    char *alpha = NULL;

    /**
     * 现在载入位图,并将其转换为纹理。 TextureImage[0]=LoadBMP("Data/NeHe.bmp") 调用 LoadBMP() 的代码。
     * 载入 Data 目录下的 NeHe.bmp 位图文件。如果一切正常,图像数据将存放在 TextureImage[0] 中, Status 被设为 TRUE ,然后我们开始创建纹理。
     */
    // 载入位图,检查有无错误,如果位图没找到则退出
    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Base.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(3, &texture[0]);                    // 创建纹理

        // 创建使用临近过滤器过滤得纹理
        glBindTexture(GL_TEXTURE_2D, texture[0]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // 创建使用线形过滤器过滤得纹理
        glBindTexture(GL_TEXTURE_2D, texture[1]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // 创建使用线形Mipmap过滤器过滤得纹理
        glBindTexture(GL_TEXTURE_2D, texture[2]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data);
    }


    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Bump.bmp", TextureImage)) {
        Status = TRUE;                            // 将 Status 设为 TRUE

        glPixelTransferf(GL_RED_SCALE, 0.5f);                        // Scale RGB By 50%, So That We Have Only
        glPixelTransferf(GL_GREEN_SCALE, 0.5f);                        // Half Intenstity
        glPixelTransferf(GL_BLUE_SCALE, 0.5f);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);    // No Wrapping, Please!
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
        glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, Gray);

        /**
         * 现在使用中 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(3, &bump[0]);                    // 创建纹理

        // Create Nearest Filtered Texture
        glBindTexture(GL_TEXTURE_2D, bump[0]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // Create Linear Filtered Texture
        glBindTexture(GL_TEXTURE_2D, bump[1]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // Create MipMapped Texture
        glBindTexture(GL_TEXTURE_2D, bump[2]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data);

        for (int i = 0; i < 3 * TextureImage->sizeX * TextureImage->sizeY; i++)        // Invert The Bumpmap
            TextureImage->data[i] = 255 - TextureImage->data[i];

        glGenTextures(3, invbump);                                // Create Three Textures

        // Create Nearest Filtered Texture
        glBindTexture(GL_TEXTURE_2D, invbump[0]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // Create Linear Filtered Texture
        glBindTexture(GL_TEXTURE_2D, invbump[1]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                     TextureImage->data);

        // Create MipMapped Texture
        glBindTexture(GL_TEXTURE_2D, invbump[2]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB8, TextureImage->sizeX, TextureImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data);

        glPixelTransferf(GL_RED_SCALE, 1.0f);                // Scale RGB Back To 100% Again
        glPixelTransferf(GL_GREEN_SCALE, 1.0f);
        glPixelTransferf(GL_BLUE_SCALE, 1.0f);
    }

    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/OpenGL_Alpha.bmp", TextureImage)) {
        Status = TRUE;                            // 将 Status 设为 TRUE

        alpha = new char[4 * TextureImage->sizeX * TextureImage->sizeY];        // Create Memory For RGBA8-Texture
        for (int a = 0; a < TextureImage->sizeX * TextureImage->sizeY; a++)
            alpha[4 * a + 3] = TextureImage->data[a * 3];                    // Pick Only Red Value As Alpha!
        if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/OpenGL.bmp", TextureImage)) {
            for (int a = 0; a < TextureImage->sizeX * TextureImage->sizeY; a++) {
                alpha[4 * a] = TextureImage->data[a * 3];                    // R
                alpha[4 * a + 1] = TextureImage->data[a * 3 + 1];                // G
                alpha[4 * a + 2] = TextureImage->data[a * 3 + 2];                // B
            }

            glGenTextures(1, &glLogo);                            // Create One Textures

            // Create Linear Filtered RGBA8-Texture
            glBindTexture(GL_TEXTURE_2D, glLogo);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGBA,
                         GL_UNSIGNED_BYTE,
                         alpha);
            delete alpha;
        }
    }


    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Multi_On_Alpha.bmp", TextureImage)) {
        Status = TRUE;                            // 将 Status 设为 TRUE

        alpha = new char[4 * TextureImage->sizeX * TextureImage->sizeY];        // Create Memory For RGBA8-Texture
        for (int a = 0; a < TextureImage->sizeX * TextureImage->sizeY; a++)
            alpha[4 * a + 3] = TextureImage->data[a * 3];                    // Pick Only Red Value As Alpha!
        if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Multi_On.bmp", TextureImage)) {
            for (int a = 0; a < TextureImage->sizeX * TextureImage->sizeY; a++) {
                alpha[4 * a] = TextureImage->data[a * 3];                    // R
                alpha[4 * a + 1] = TextureImage->data[a * 3 + 1];                // G
                alpha[4 * a + 2] = TextureImage->data[a * 3 + 2];                // B
            }

            glGenTextures(1, &multiLogo);                            // Create One Textures

            // Create Linear Filtered RGBA8-Texture
            glBindTexture(GL_TEXTURE_2D, multiLogo);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGBA,
                         GL_UNSIGNED_BYTE,
                         alpha);
            delete alpha;
        }
    }

    /**
     * 最后返回状态变量。如果一切OK,变量 Status 的值为 TRUE 。否则为 FALSE 。
     */
    return Status;
}


/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
GLvoid Lesson22::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();                            // 重置模型观察矩阵
}

void Lesson22::doCube(void) {
    int i;
    glBegin(GL_QUADS);
    // Front Face
    glNormal3f(0.0f, 0.0f, +1.0f);
    for (i = 0; i < 4; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Back Face
    glNormal3f(0.0f, 0.0f, -1.0f);
    for (i = 4; i < 8; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Top Face
    glNormal3f(0.0f, 1.0f, 0.0f);
    for (i = 8; i < 12; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Bottom Face
    glNormal3f(0.0f, -1.0f, 0.0f);
    for (i = 12; i < 16; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Right face
    glNormal3f(1.0f, 0.0f, 0.0f);
    for (i = 16; i < 20; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Left Face
    glNormal3f(-1.0f, 0.0f, 0.0f);
    for (i = 20; i < 24; i++) {
        glTexCoord2f(data[5 * i], data[5 * i + 1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    glEnd();
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * if (!LoadGLTextures()) 这行代码调用上面讲的子例程载入位图并生成纹理。如果因为任何原因 LoadGLTextures() 调用失败,接着的一行返回FALSE。如果一切OK,并且纹理创建好了,我们启用2D纹理映射。
 * 如果您忘记启用的话,您的对象看起来永远都是纯白色,这一定不是什么好事。
 * @return
 */
int Lesson22::InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    multitextureSupported = initMultitexture();
    if (!LoadGLTextures()) return false;                // Jump To Texture Loading Routine

    glEnable(GL_TEXTURE_2D);                            // Enable Texture Mapping
    glShadeModel(GL_SMOOTH);                            // Enable Smooth Shading
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);                // Black Background
    glClearDepth(1.0f);                                    // Depth Buffer Setup
    glEnable(GL_DEPTH_TEST);                            // Enables Depth Testing
    glDepthFunc(GL_LEQUAL);                                // The Type Of Depth Testing To Do
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);    // Really Nice Perspective Calculations

    initLights();                                        // Initialize OpenGL Light
    return TRUE;                                // 初始化 OK
}

void Lesson22::VMatMult(GLfloat *M, GLfloat *v) {
    GLfloat res[3];
    res[0] = M[0] * v[0] + M[1] * v[1] + M[2] * v[2] + M[3] * v[3];
    res[1] = M[4] * v[0] + M[5] * v[1] + M[6] * v[2] + M[7] * v[3];
    res[2] = M[8] * v[0] + M[9] * v[1] + M[10] * v[2] + M[11] * v[3];;
    v[0] = res[0];
    v[1] = res[1];
    v[2] = res[2];
    v[3] = M[15];                                            // Homogenous Coordinate
}

void Lesson22::SetUpBumps(GLfloat *n, GLfloat *c, GLfloat *l, GLfloat *s, GLfloat *t) {
    GLfloat v[3];                            // Vertex From Current Position To Light
    GLfloat lenQ;                            // Used To Normalize

    // Calculate v From Current Vector c To Lightposition And Normalize v
    v[0] = l[0] - c[0];
    v[1] = l[1] - c[1];
    v[2] = l[2] - c[2];
    lenQ = (GLfloat) sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
    v[0] /= lenQ;
    v[1] /= lenQ;
    v[2] /= lenQ;
    // Project v Such That We Get Two Values Along Each Texture-Coordinat Axis.
    c[0] = (s[0] * v[0] + s[1] * v[1] + s[2] * v[2]) * MAX_EMBOSS;
    c[1] = (t[0] * v[0] + t[1] * v[1] + t[2] * v[2]) * MAX_EMBOSS;
}

void Lesson22::doLogo() {
    glDepthFunc(GL_ALWAYS);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glEnable(GL_BLEND);
    glDisable(GL_LIGHTING);
    glLoadIdentity();
    glBindTexture(GL_TEXTURE_2D, glLogo);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(0.23f, -0.4f, -1.0f);
    glTexCoord2f(1.0f, 0.0f);
    glVertex3f(0.53f, -0.4f, -1.0f);
    glTexCoord2f(1.0f, 1.0f);
    glVertex3f(0.53f, -0.25f, -1.0f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(0.23f, -0.25f, -1.0f);
    glEnd();

    if (useMultitexture) {
        glBindTexture(GL_TEXTURE_2D, multiLogo);
        glBegin(GL_QUADS);
        glTexCoord2f(0.0f, 0.0f);
        glVertex3f(-0.53f, -0.4f, -1.0f);
        glTexCoord2f(1.0f, 0.0f);
        glVertex3f(-0.33f, -0.4f, -1.0f);
        glTexCoord2f(1.0f, 1.0f);
        glVertex3f(-0.33f, -0.3f, -1.0f);
        glTexCoord2f(0.0f, 1.0f);
        glVertex3f(-0.53f, -0.3f, -1.0f);
        glEnd();
    }
    glDepthFunc(GL_LEQUAL);
}

bool Lesson22::doMesh1TexelUnits() {
    GLfloat c[4] = {0.0f, 0.0f, 0.0f, 1.0f};                    // Holds Current Vertex
    GLfloat n[4] = {0.0f, 0.0f, 0.0f, 1.0f};                    // Normalized Normal Of Current Surface
    GLfloat s[4] = {0.0f, 0.0f, 0.0f, 1.0f};                    // s-Texture Coordinate Direction, Normalized
    GLfloat t[4] = {0.0f, 0.0f, 0.0f, 1.0f};                    // t-Texture Coordinate Direction, Normalized
    GLfloat l[4];                                        // Holds Our Lightposition To Be Transformed Into Object Space
    GLfloat Minv[16];                                    // Holds The Inverted Modelview Matrix To Do So.
    int i;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Clear The Screen And The Depth Buffer

    // Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
    // Simply Build It By Doing All Transformations Negated And In Reverse Order.
    glLoadIdentity();
    glRotatef(-yrot, 0.0f, 1.0f, 0.0f);
    glRotatef(-xrot, 1.0f, 0.0f, 0.0f);
    glTranslatef(0.0f, 0.0f, -z);
    glGetFloatv(GL_MODELVIEW_MATRIX, Minv);
    glLoadIdentity();
    glTranslatef(0.0f, 0.0f, z);

    glRotatef(xrot, 1.0f, 0.0f, 0.0f);
    glRotatef(yrot, 0.0f, 1.0f, 0.0f);

    // Transform The Lightposition Into Object Coordinates:
    l[0] = LightPosition[0];
    l[1] = LightPosition[1];
    l[2] = LightPosition[2];
    l[3] = 1.0f;                                            // Homogenous Coordinate
    VMatMult(Minv, l);

/*	PASS#1: Use Texture "Bump"
			No Blend
			No Lighting
			No Offset Texture-Coordinates */
    glBindTexture(GL_TEXTURE_2D, bump[filter]);
    glDisable(GL_BLEND);
    glDisable(GL_LIGHTING);
    doCube();

/* PASS#2:	Use Texture "Invbump"
			Blend GL_ONE To GL_ONE
			No Lighting
			Offset Texture Coordinates
			*/
    glBindTexture(GL_TEXTURE_2D, invbump[filter]);
    glBlendFunc(GL_ONE, GL_ONE);
    glDepthFunc(GL_LEQUAL);
    glEnable(GL_BLEND);

    glBegin(GL_QUADS);
    // Front Face
    n[0] = 0.0f;
    n[1] = 0.0f;
    n[2] = 1.0f;
    s[0] = 1.0f;
    s[1] = 0.0f;
    s[2] = 0.0f;
    t[0] = 0.0f;
    t[1] = 1.0f;
    t[2] = 0.0f;
    for (i = 0; i < 4; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glTexCoord2f(data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Back Face
    n[0] = 0.0f;
    n[1] = 0.0f;
    n[2] = -1.0f;
    s[0] = -1.0f;
    s[1] = 0.0f;
    s[2] = 0.0f;
    t[0] = 0.0f;
    t[1] = 1.0f;
    t[2] = 0.0f;
    for (i = 4; i < 8; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glTexCoord2f(data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Top Face
    n[0] = 0.0f;
    n[1] = 1.0f;
    n[2] = 0.0f;
    s[0] = 1.0f;
    s[1] = 0.0f;
    s[2] = 0.0f;
    t[0] = 0.0f;
    t[1] = 0.0f;
    t[2] = -1.0f;
    for (i = 8; i < 12; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glTexCoord2f(data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Bottom Face
    n[0] = 0.0f;
    n[1] = -1.0f;
    n[2] = 0.0f;
    s[0] = -1.0f;
    s[1] = 0.0f;
    s[2] = 0.0f;
    t[0] = 0.0f;
    t[1] = 0.0f;
    t[2] = -1.0f;
    for (i = 12; i < 16; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glTexCoord2f(data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Right Face
    n[0] = 1.0f;
    n[1] = 0.0f;
    n[2] = 0.0f;
    s[0] = 0.0f;
    s[1] = 0.0f;
    s[2] = -1.0f;
    t[0] = 0.0f;
    t[1] = 1.0f;
    t[2] = 0.0f;
    for (i = 16; i < 20; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glTexCoord2f(data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Left Face
    n[0] = -1.0f;
    n[1] = 0.0f;
    n[2] = 0.0f;
    s[0] = 0.0f;
    s[1] = 0.0f;
    s[2] = 1.0f;
    t[0] = 0.0f;
    t[1] = 1.0f;
    t[2] = 0.0f;
    for (i = 20; i < 24; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glTexCoord2f(data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    glEnd();

/* PASS#3:	Use Texture "Base"
			Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
			Lighting Enabled
			No Offset Texture-Coordinates
			*/
    if (!emboss) {
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        glBindTexture(GL_TEXTURE_2D, texture[filter]);
        glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
        glEnable(GL_LIGHTING);
        doCube();
    }

    xrot += xspeed;
    yrot += yspeed;
    if (xrot > 360.0f) xrot -= 360.0f;
    if (xrot < 0.0f) xrot += 360.0f;
    if (yrot > 360.0f) yrot -= 360.0f;
    if (yrot < 0.0f) yrot += 360.0f;

/*	LAST PASS:	Do The Logos! */
    doLogo();

    return true;                                        // Keep Going
}

bool Lesson22::doMesh2TexelUnits(void) {

    GLfloat c[4] = {0.0f, 0.0f, 0.0f, 1.0f};                    // holds current vertex
    GLfloat n[4] = {0.0f, 0.0f, 0.0f, 1.0f};                    // normalized normal of current surface
    GLfloat s[4] = {0.0f, 0.0f, 0.0f, 1.0f};                    // s-texture coordinate direction, normalized
    GLfloat t[4] = {0.0f, 0.0f, 0.0f, 1.0f};                    // t-texture coordinate direction, normalized
    GLfloat l[4];                                        // holds our lightposition to be transformed into object space
    GLfloat Minv[16];                                    // holds the inverted modelview matrix to do so.
    int i;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Clear The Screen And The Depth Buffer

    // Build Inverse Modelview Matrix First. This Substitutes One Push/Pop With One glLoadIdentity();
    // Simply Build It By Doing All Transformations Negated And In Reverse Order.
    glLoadIdentity();
    glRotatef(-yrot, 0.0f, 1.0f, 0.0f);
    glRotatef(-xrot, 1.0f, 0.0f, 0.0f);
    glTranslatef(0.0f, 0.0f, -z);
    glGetFloatv(GL_MODELVIEW_MATRIX, Minv);
    glLoadIdentity();
    glTranslatef(0.0f, 0.0f, z);

    glRotatef(xrot, 1.0f, 0.0f, 0.0f);
    glRotatef(yrot, 0.0f, 1.0f, 0.0f);

    // Transform The Lightposition Into Object Coordinates:
    l[0] = LightPosition[0];
    l[1] = LightPosition[1];
    l[2] = LightPosition[2];
    l[3] = 1.0f;                                            // Homogenous Coordinate
    VMatMult(Minv, l);

/*	PASS#1: Texel-Unit 0:	Use Texture "Bump"
							No Blend
							No Lighting
							No Offset Texture-Coordinates
							Texture-Operation "Replace"
			Texel-Unit 1:	Use Texture "Invbump"
							No Lighting
							Offset Texture Coordinates
							Texture-Operation "Replace"
*/
    // TEXTURE-UNIT #0
    glActiveTextureARB(GL_TEXTURE0_ARB);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, bump[filter]);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);
    // TEXTURE-UNIT #1:
    glActiveTextureARB(GL_TEXTURE1_ARB);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, invbump[filter]);
    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);
    glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD);
    // General Switches:
    glDisable(GL_BLEND);
    glDisable(GL_LIGHTING);
    glBegin(GL_QUADS);
    // Front Face
    n[0] = 0.0f;
    n[1] = 0.0f;
    n[2] = 1.0f;
    s[0] = 1.0f;
    s[1] = 0.0f;
    s[2] = 0.0f;
    t[0] = 0.0f;
    t[1] = 1.0f;
    t[2] = 0.0f;
    for (i = 0; i < 4; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, data[5 * i], data[5 * i + 1]);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Back Face
    n[0] = 0.0f;
    n[1] = 0.0f;
    n[2] = -1.0f;
    s[0] = -1.0f;
    s[1] = 0.0f;
    s[2] = 0.0f;
    t[0] = 0.0f;
    t[1] = 1.0f;
    t[2] = 0.0f;
    for (i = 4; i < 8; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, data[5 * i], data[5 * i + 1]);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Top Face
    n[0] = 0.0f;
    n[1] = 1.0f;
    n[2] = 0.0f;
    s[0] = 1.0f;
    s[1] = 0.0f;
    s[2] = 0.0f;
    t[0] = 0.0f;
    t[1] = 0.0f;
    t[2] = -1.0f;
    for (i = 8; i < 12; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, data[5 * i], data[5 * i + 1]);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Bottom Face
    n[0] = 0.0f;
    n[1] = -1.0f;
    n[2] = 0.0f;
    s[0] = -1.0f;
    s[1] = 0.0f;
    s[2] = 0.0f;
    t[0] = 0.0f;
    t[1] = 0.0f;
    t[2] = -1.0f;
    for (i = 12; i < 16; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, data[5 * i], data[5 * i + 1]);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Right Face
    n[0] = 1.0f;
    n[1] = 0.0f;
    n[2] = 0.0f;
    s[0] = 0.0f;
    s[1] = 0.0f;
    s[2] = -1.0f;
    t[0] = 0.0f;
    t[1] = 1.0f;
    t[2] = 0.0f;
    for (i = 16; i < 20; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, data[5 * i], data[5 * i + 1]);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    // Left Face
    n[0] = -1.0f;
    n[1] = 0.0f;
    n[2] = 0.0f;
    s[0] = 0.0f;
    s[1] = 0.0f;
    s[2] = 1.0f;
    t[0] = 0.0f;
    t[1] = 1.0f;
    t[2] = 0.0f;
    for (i = 20; i < 24; i++) {
        c[0] = data[5 * i + 2];
        c[1] = data[5 * i + 3];
        c[2] = data[5 * i + 4];
        SetUpBumps(n, c, l, s, t);
        glMultiTexCoord2fARB(GL_TEXTURE0_ARB, data[5 * i], data[5 * i + 1]);
        glMultiTexCoord2fARB(GL_TEXTURE1_ARB, data[5 * i] + c[0], data[5 * i + 1] + c[1]);
        glVertex3f(data[5 * i + 2], data[5 * i + 3], data[5 * i + 4]);
    }
    glEnd();

/* PASS#2	Use Texture "Base"
			Blend GL_DST_COLOR To GL_SRC_COLOR (Multiplies By 2)
			Lighting Enabled
			No Offset Texture-Coordinates
			*/
    glActiveTextureARB(GL_TEXTURE1_ARB);
    glDisable(GL_TEXTURE_2D);
    glActiveTextureARB(GL_TEXTURE0_ARB);
    if (!emboss) {
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
        glBindTexture(GL_TEXTURE_2D, texture[filter]);
        glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
        glEnable(GL_BLEND);
        glEnable(GL_LIGHTING);
        doCube();
    }

    xrot += xspeed;
    yrot += yspeed;
    if (xrot > 360.0f) xrot -= 360.0f;
    if (xrot < 0.0f) xrot += 360.0f;
    if (yrot > 360.0f) yrot -= 360.0f;
    if (yrot < 0.0f) yrot += 360.0f;

/* LAST PASS:	Do The Logos! */
    doLogo();

    return true;                                        // Keep Going
}

bool Lesson22::doMeshNoBumps(void) {

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    // Clear The Screen And The Depth Buffer
    glLoadIdentity();                                    // Reset The View
    glTranslatef(0.0f, 0.0f, z);

    glRotatef(xrot, 1.0f, 0.0f, 0.0f);
    glRotatef(yrot, 0.0f, 1.0f, 0.0f);
    if (useMultitexture) {
        glActiveTextureARB(GL_TEXTURE1_ARB);
        glDisable(GL_TEXTURE_2D);
        glActiveTextureARB(GL_TEXTURE0_ARB);
    }
    glDisable(GL_BLEND);
    glBindTexture(GL_TEXTURE_2D, texture[filter]);
    glBlendFunc(GL_DST_COLOR, GL_SRC_COLOR);
    glEnable(GL_LIGHTING);
    doCube();

    xrot += xspeed;
    yrot += yspeed;
    if (xrot > 360.0f) xrot -= 360.0f;
    if (xrot < 0.0f) xrot += 360.0f;
    if (yrot > 360.0f) yrot -= 360.0f;
    if (yrot < 0.0f) yrot += 360.0f;

/* LAST PASS:	Do The Logos! */
    doLogo();

    return true;                                        // Keep Going
}

int Lesson22::DrawGLScene(GLvoid)                                // Here's Where We Do All The Drawing
{
    if (bumps) {
        if (useMultitexture && maxTexelUnits > 1)
            return doMesh2TexelUnits();
        else return doMesh1TexelUnits();
    } else return doMeshNoBumps();
}
复制代码

球面映射

#define TRUE 1
#define FALSE 0

/*
** 利用freeimage加载bmp图像
** 此函数在Linux系统上可以作为常用util调用
*/
GLboolean Lesson23::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;
}

/**
 * 下一部分代码载入位图(调用上面的代码)并转换成纹理。现在载入一个位图,并用它创建三种不同的纹理。这段代码调用前面的代码载入位图,并将其转换成3个纹理。Status 变量跟踪纹理是否已载入并被创建了。
 * @return
 */
int Lesson23::LoadGLTextures() {
    /**
     * 然后设置一个叫做 Status 的变量。我们使用它来跟踪是否能够载入位图以及能否创建纹理。 Status 缺省设为 FALSE (表示没有载入或创建任何东东)。
     */
    int Status = FALSE;                            // 状态指示器
    /**
     * 现在我们创建存储位图的图像记录。次记录包含位图的宽度、高度和数据。
     */
    AUX_RGBImageRec *TextureImage, *TextureImage2;                    // 创建纹理的存储空间
    TextureImage = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));
    TextureImage2 = (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/BG.bmp", TextureImage)
        && LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Reflect.bmp", TextureImage2)) {
        Status = TRUE;                            // 将 Status 设为 TRUE
        /**
         * 现在我们已经将图像数据载入TextureImage[0]。我们将用它来创建3个纹理。下面的行告诉OpenGL我们要创建三个纹理,它们将存放在texture[0], texture[1] 和 texture[2] 中。
         */
        glGenTextures(6, &texture[0]);                    // 创建纹理
        setTexture(TextureImage);
        setTexture(TextureImage2);
    }
    /**
     * 现在我们释放前面用来存放位图数据的内存。我们先查看位图数据是否存放在处。如果是的话,再查看数据是否已经存储。如果已经存储的话,删了它。接着再释放 TextureImage[0] 图像结构以保证所有的内存都能释放。
     */
    freeTexture(TextureImage);
    freeTexture(TextureImage2);
    /**
     * 最后返回状态变量。如果一切OK,变量 Status 的值为 TRUE 。否则为 FALSE 。
     */
    return Status;
}

void Lesson23::setTexture(AUX_RGBImageRec *TextureImage) {
    // 创建 Nearest 滤波贴图
    glBindTexture(GL_TEXTURE_2D, texture[0]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                 TextureImage->data);

    // 创建线性滤波纹理
    glBindTexture(GL_TEXTURE_2D, texture[1]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
                 TextureImage->data);

    // 创建 MipMapped 纹理
    glBindTexture(GL_TEXTURE_2D, texture[2]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);

    /**
     * 下面一行生成 mipmapped 纹理。我们使用三种颜色(红,绿,蓝)来生成一个2D纹理。TextureImage[0]->sizeX 是位图宽度,extureImage[0]->sizeY 是位图高度,GL_RGB意味着我们依次使用RGB色彩。
     * GL_UNSIGNED_BYTE 意味着纹理数据的单位是字节。TextureImage[0]->data指向我们创建纹理所用的位图。
     */
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage->sizeX, TextureImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE,
                      TextureImage->data);
}

void Lesson23::freeTexture(AUX_RGBImageRec *TextureImage) {
    if (TextureImage)                            // 纹理是否存在
    {
        if (TextureImage->data)                    // 纹理图像是否存在
        {
            free(TextureImage->data);                // 释放纹理图像占用的内存
        }

        free(TextureImage);                        // 释放图像结构
    }
}

/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
GLvoid Lesson23::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 Lesson23::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);            // 告诉系统对透视进行修正

    /**
     * 现在开始设置光源。下面下面一行设置环境光的发光量,光源light1开始发光。这一课的开始处我们我们将环境光的发光量存放在LightAmbient数组中。
     * 现在我们就使用此数组(半亮度环境光)。在int InitGL(GLvoid)函数中添加下面的代码。
     */
    glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);                // 设置环境光

    /**
     * 接下来我们设置漫射光的发光量。它存放在LightDiffuse数组中(全亮度白光)。
     */
    glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);                // 设置漫射光

    /**
     * 然后设置光源的位置。位置存放在 LightPosition 数组中(正好位于木箱前面的中心,X-0.0f,Y-0.0f,Z方向移向观察者2个单位<位于屏幕外面>)。
     */
    glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);            // 设置光源位置

    /**
     * 最后,我们启用一号光源。我们还没有启用GL_LIGHTING,所以您看不见任何光线。记住:只对光源进行设置、定位、甚至启用,光源都不会工作。除非我们启用GL_LIGHTING。
     */
    glEnable(GL_LIGHT1);                            // 启用一号光源
    glEnable(GL_LIGHTING);                      // 启用光源

    /**
     * 第一行代码将初始化二次曲面并且创建一个指向改二次曲面的指针,如果改二次曲面不能被创建的话,那么该指针就是NULL。
     * 第二行代码将在二次曲面的表面创建平滑的法向量,这样当灯光照上去的时候将会好看些。另外一些可能的取值是:GLU_NONE和GLU_FLAT。最后我们使在二次曲面表面的纹理映射有效。
     */
    quadratic = gluNewQuadric();                // 创建二次几何体
    gluQuadricNormals(quadratic, GLU_SMOOTH);        // 使用平滑法线
    gluQuadricTexture(quadratic, GL_TRUE);        // 使用纹理

    /**
     * 让OpenGL自动为我们计算使用球体映射时,顶点的纹理坐标。
     */
    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);			// 设置s方向的纹理自动生成
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);			// 设置t方向的纹理自动生成
    return TRUE;                                // 初始化 OK
}

GLvoid Lesson23::glDrawCube()                    // 绘制立方体
{
    /**
     * 把法线的范围从[-1,1]缩放到[-0.5,0.5]。如果法向量太大的话,会产生一些块状效果,影响视觉效果。
     */
    glBegin(GL_QUADS);
    // 前面
    glNormal3f( 0.0f, 0.0f, 0.5f);
    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);
    // 背面
    glNormal3f( 0.0f, 0.0f,-0.5f);
    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);
    // 上面
    glNormal3f( 0.0f, 0.5f, 0.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);
    // 下面
    glNormal3f( 0.0f,-0.5f, 0.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);
    // 右面
    glNormal3f( 0.5f, 0.0f, 0.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);
    // 左面
    glNormal3f(-0.5f, 0.0f, 0.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();
}

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

    glTranslatef(0.0f,0.0f,z);

    glEnable(GL_TEXTURE_GEN_S);						// 自动生成s方向纹理坐标
    glEnable(GL_TEXTURE_GEN_T);						// 自动生成t方向纹理坐标

    glBindTexture(GL_TEXTURE_2D, texture[filter+(filter+1)]);		// 绑定纹理
    glPushMatrix();
    glRotatef(xrot,1.0f,0.0f,0.0f);
    glRotatef(yrot,0.0f,1.0f,0.0f);
    switch(object)
    {
        case 0:
            glDrawCube();
            break;
        case 1:
            glTranslatef(0.0f,0.0f,-1.5f);					// 创建圆柱
            gluCylinder(quadratic,1.0f,1.0f,3.0f,32,32);
            break;
        case 2:
            gluSphere(quadratic,1.3f,32,32);					// 创建球
            break;
        case 3:
            glTranslatef(0.0f,0.0f,-1.5f);					// 创建圆锥
            gluCylinder(quadratic,1.0f,0.0f,3.0f,32,32);
            break;
    };

    glPopMatrix();
    glDisable(GL_TEXTURE_GEN_S);						// 禁止自动生成纹理坐标
    glDisable(GL_TEXTURE_GEN_T);

    glBindTexture(GL_TEXTURE_2D, texture[filter*2]);	// This Will Select The BG Maps...
    glPushMatrix();
    glTranslatef(0.0f, 0.0f, -24.0f);
    glBegin(GL_QUADS);
    glNormal3f( 0.0f, 0.0f, 1.0f);
    glTexCoord2f(0.0f, 0.0f); glVertex3f(-13.3f, -10.0f,  10.0f);
    glTexCoord2f(1.0f, 0.0f); glVertex3f( 13.3f, -10.0f,  10.0f);
    glTexCoord2f(1.0f, 1.0f); glVertex3f( 13.3f,  10.0f,  10.0f);
    glTexCoord2f(0.0f, 1.0f); glVertex3f(-13.3f,  10.0f,  10.0f);
    glEnd();
    glPopMatrix();

    xrot+=xspeed;
    yrot+=yspeed;
    return TRUE;                                //  一切 OK
}
复制代码

扩展,剪裁和TGA图像文件的加载

#define TRUE 1
#define FALSE 0

/**
 * 这个部分的代码将要加载一个TGA文件并把它转换为纹理。必须注意的是这部分代码只能加载24/32位的不压缩的TGA文件。
 * 这个函数包含两个参数,一个保存载入的图像,一个为将载入的文件名。TGA文件包含一个12个字节的文件头,载入图像后,我们用type来设置图像中像素格式在OpenGL中的对应。
 * 如果是24位的图像我们使用GL_RGB,如果是32位的图像我们使用GL_RGBA。
 */
bool Lesson24::LoadTGA(TextureImage *texture, char *filename) {
    GLubyte TGAheader[12] = {0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0};            // 无压缩的TGA文件头
    GLubyte TGAcompare[12];                        // 保存读入的文件头信息
    GLubyte header[6];                        // 保存最有用的图像信息,宽,高,位深
    GLuint bytesPerPixel;                        // 记录每个颜色所占用的字节数
    GLuint imageSize;                        // 记录文件大小
    GLuint temp;                            // 临时变量
    GLuint type = GL_RGBA;                        // 设置默认的格式为GL_RGBA,即32位图像

    FILE *file = fopen(filename, "rb");                        // 打开一个TGA文件

    if (file == NULL ||                            // 文件存在么?
        fread(TGAcompare, 1, sizeof(TGAcompare), file) != sizeof(TGAcompare) ||    // 是否包含12个字节的文件头?
        memcmp(TGAheader, TGAcompare, sizeof(TGAheader)) != 0 ||    // 是否是我们需要的格式?
        fread(header, 1, sizeof(header), file) != sizeof(header))            // 如果是读取下面六个图像信息
    {
        if (file == NULL)                            // 文件不存在返回错误
            return false;
        else {
            fclose(file);                        // 关闭文件返回错误
            return false;
        }
    }

    texture->width = header[1] * 256 + header[0];                    // 记录文件高度
    texture->height = header[3] * 256 + header[2];                    // 记录文件宽度

    if (texture->width <= 0 ||                        // 宽度是否小于0
        texture->height <= 0 ||                        // 高度是否小于0
        (header[4] != 24 && header[4] != 32))                        // TGA文件是24/32位?
    {
        fclose(file);                                // 如果失败关闭文件,返回错误
        return false;
    }

    texture->bpp = header[4];                            // 记录文件的位深
    bytesPerPixel = texture->bpp / 8;                            // 记录每个象素所占的字节数
    imageSize = texture->width * texture->height * bytesPerPixel;                // 计算TGA文件加载所需要的内存大小

    texture->imageData = (GLubyte *) malloc(imageSize);                // 分配内存去保存TGA数据

    if (texture->imageData == NULL ||                        // 系统是否分配了足够的内存?
        fread(texture->imageData, 1, imageSize, file) != imageSize)        // 是否成功读入内存?
    {
        if (texture->imageData != NULL)                    // 是否有数据被加载
            free(texture->imageData);                    // 如果是,则释放载入的数据

        fclose(file);                            // 关闭文件
        return false;                            // 返回错误
    }

    for (GLuint i = 0; i < int(imageSize); i += bytesPerPixel)                // 循环所有的像素
    {                                    // 交换R和B的值
        temp = texture->imageData[i];
        texture->imageData[i] = texture->imageData[i + 2];
        texture->imageData[i + 2] = temp;
    }

    fclose(file);                                // 关闭文件

    // 创建纹理
    glGenTextures(1, &texture[0].texID);                        // 创建纹理,并记录纹理ID

    glBindTexture(GL_TEXTURE_2D, texture[0].texID);                // 绑定纹理
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);        // 设置过滤器为线性过滤
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    if (texture[0].bpp == 24)                                // 是否为24位图像?
    {
        type = GL_RGB;                                // 如果是设置类型为GL_RGB
    }

    glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE,
                 texture[0].imageData);

    return true;                                    // 纹理绑定完成,成功返回
}

GLvoid Lesson24::BuildFont(GLvoid)                                // 创建字体显示列表
{
    base = glGenLists(256);                            // 创建256个显示列表
    glBindTexture(GL_TEXTURE_2D, textures[0].texID);                // 绑定纹理
    for (int loop1 = 0; loop1 < 256; loop1++)                    // 循环创建256个显示列表
    {
        float cx = float(loop1 % 16) / 16.0f;                    // 当前字符的X位置
        float cy = float(loop1 / 16) / 16.0f;                    // 当前字符的Y位置

        glNewList(base + loop1, GL_COMPILE);                    // 开始创建显示列表
        glBegin(GL_QUADS);                        // 创建一个四边形用来包含字符图像
        glTexCoord2f(cx, 1.0f - cy - 0.0625f);            // 左下方纹理坐标
        glVertex2d(0, 16);                    // 左下方坐标
        glTexCoord2f(cx + 0.0625f, 1.0f - cy - 0.0625f);        // 右下方纹理坐标
        glVertex2i(16, 16);                    // 右下方坐标
        glTexCoord2f(cx + 0.0625f, 1.0f - cy - 0.001f);        // 右上方纹理坐标
        glVertex2i(16, 0);                    // 右上方坐标
        glTexCoord2f(cx, 1.0f - cy - 0.001f);            // 左上方纹理坐标
        glVertex2i(0, 0);                    // 左上方坐标
        glEnd();                            // 四边形创建完毕
        glTranslated(14, 0, 0);                    // 向右移动14个单位
        glEndList();                            // 结束创建显示列表
    }
}

GLvoid Lesson24::glPrint(GLint x, GLint y, int set, const char *fmt, ...) {
    char text[1024];                            // 保存我们的字符
    va_list ap;                                // 指向第一个参数

    if (fmt == NULL)                                // 如果要显示的字符为空则返回
        return;

    va_start(ap, fmt);                                // 开始分析参数,并把结果写入到text中
    vsprintf(text, fmt, ap);
    va_end(ap);

    if (set > 1)                                // 如果字符集大于1则使用第二个字符集
    {
        set = 1;
    }

    glEnable(GL_TEXTURE_2D);                            // 使用纹理映射
    glLoadIdentity();                                // 重置视口矩阵
    glTranslated(x, y, 0);                            // 平移到(x,y,0)处
    glListBase(base - 32 + (128 * set));                        // 选择字符集

    glScalef(1.0f, 2.0f, 1.0f);                            // 沿Y轴放大一倍

    glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);                // 把字符写入到屏幕
    glDisable(GL_TEXTURE_2D);                            // 禁止纹理映射
}

/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
GLvoid Lesson24::ReSizeGLScene(GLsizei width, GLsizei height)                // 重置OpenGL窗口大小
{
    swidth = width;                                    // 设置剪切矩形为窗口大小
    sheight = height;
    if (height == 0)                                    // 防止高度为0时,被0除
    {
        height = 1;
    }
    glViewport(0, 0, width, height);                            // 设置窗口可见区
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0f, 640, 480, 0.0f, -1.0f, 1.0f);                        // 设置视口大小为640x480
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * if (!LoadGLTextures()) 这行代码调用上面讲的子例程载入位图并生成纹理。如果因为任何原因 LoadGLTextures() 调用失败,接着的一行返回FALSE。如果一切OK,并且纹理创建好了,我们启用2D纹理映射。
 * 如果您忘记启用的话,您的对象看起来永远都是纯白色,这一定不是什么好事。
 * @return
 */
int Lesson24::InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    if (!LoadTGA(&textures[0],
                 "/home/zpw/Documents/CLionProjects/opengl_study/Data/Font.tga"))                        // 载入字体纹理
    {
        return false;                                // 载入失败则返回
    }

    BuildFont();                                    // 创建字体

    glShadeModel(GL_SMOOTH);                                // 使用平滑着色
    glClearColor(0.0f, 0.0f, 0.0f, 0.5f);                        // 设置黑色背景
    glClearDepth(1.0f);                                // 设置深度缓存中的值为1
    glBindTexture(GL_TEXTURE_2D, textures[0].texID);                    // 绑定字体纹理
    return TRUE;                                // 初始化 OK
}

/**
 * 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。
 * 如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。
 * @return
 */
int Lesson24::DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    char *token;                                    // 保存扩展字符串
    int cnt = 0;                                    // 纪录扩展字符串的个数

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);                    // 清楚背景和深度缓存

    glColor3f(1.0f, 0.5f, 0.5f);                                // 设置为红色
    glPrint(50, 16, 1, "Renderer");
    glPrint(80, 48, 1, "Vendor");
    glPrint(66, 80, 1, "Version");

    glColor3f(1.0f, 0.7f, 0.4f);                            // 设置为橘黄色
    glPrint(200, 16, 1, (char *) glGetString(GL_RENDERER));                // 显示OpenGL的实现组织
    glPrint(200, 48, 1, (char *) glGetString(GL_VENDOR));                // 显示销售商
    glPrint(200, 80, 1, (char *) glGetString(GL_VERSION));                // 显示当前版本

    glColor3f(0.5f, 0.5f, 1.0f);                            // 设置为蓝色
    glPrint(192, 432, 1, "NeHe Productions");                    // 在屏幕的底端写上NeHe Productions字符串

    glLoadIdentity();                                // 重置模型变换矩阵
    glColor3f(1.0f, 1.0f, 1.0f);                            // 设置为白色
    glBegin(GL_LINE_STRIP);
    glVertex2d(639, 417);
    glVertex2d(0, 417);
    glVertex2d(0, 480);
    glVertex2d(639, 480);
    glVertex2d(639, 128);
    glEnd();
    glBegin(GL_LINE_STRIP);
    glVertex2d(0, 128);
    glVertex2d(639, 128);
    glVertex2d(639, 1);
    glVertex2d(0, 1);
    glVertex2d(0, 417);
    glEnd();

    glScissor(1, int(0.135416f * sheight), swidth - 2, int(0.597916f * sheight));    // 定义剪裁区域
    glEnable(GL_SCISSOR_TEST);                            // 使用剪裁测试

    char *text = (char *) malloc(strlen((char *) glGetString(GL_EXTENSIONS)) + 1);        // 为保存OpenGL扩展的字符串分配内存空间
    strcpy(text, (char *) glGetString(GL_EXTENSIONS));                // 返回OpenGL扩展字符串

    token = strtok(text, " ");                                // 按空格分割text字符串,并把分割后的字符串保存在token中
    while (token != NULL)                                    // 如果token不为NULL
    {
        cnt++;                                    // 增加计数器
        if (cnt > maxtokens)                                // 纪录最大的扩展名数量
        {
            maxtokens = cnt;
        }
        glColor3f(0.5f, 1.0f, 0.5f);                        // 设置颜色为绿色
        glPrint(0, 96 + (cnt * 32) - scroll, 0, "%i", cnt);                // 绘制第几个扩展名
        glColor3f(1.0f, 1.0f, 0.5f);                        // 设置颜色为黄色
        glPrint(50, 96 + (cnt * 32) - scroll, 0, token);                // 输出第i个扩展名
        token = strtok(NULL, " ");                            // 查找下一个扩展名
    }
    glDisable(GL_SCISSOR_TEST);                                // 禁用剪裁测试

    free(text);                                    // 释放分配的内存
    glFlush();                                    // 执行所有的渲染命令

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

变形和从文件中加载3D物体

#define TRUE 1
#define FALSE 0

void Lesson25::readstr(FILE *f, char *string)                            // 读取一行字符
{
    do {
        fgets(string, 255, f);                        // 最多读取255个字符
    } while ((string[0] == '/') || (string[0] == '\n'));                // 遇到回车则停止读取
    return;                                    // 返回
}

void Lesson25::objload(char *name, OBJECT *k)                            // 从文件加载一个模型
{
    int ver;                                // 保存顶点个数
    float rx, ry, rz;                                // 保存模型位置
    FILE *filein;                                // 打开的文件句柄
    char oneline[255];                            // 保存255个字符

    filein = fopen(name, "rt");                            // 打开文本文件,供读取

    readstr(filein, oneline);                            // 读入一行文本
    sscanf(oneline, "Vertices: %d\n", &ver);                    // 搜索字符串"Vertices: ",并把其后的顶点数保存在ver变量中
    k->verts = ver;                                // 设置模型的顶点个数
    objallocate(k, ver);                            // 为模型数据分配内存
    for (int i = 0; i < ver; i++)                                // 循环所有的顶点
    {
        readstr(filein, oneline);                            // 读取一行数据
        sscanf(oneline, "%f %f %f", &rx, &ry, &rz);                    // 把顶点数据保存在rx,ry,rz中
        k->points[i].x = rx;                            // 保存当前顶点的x坐标
        k->points[i].y = ry;                            // 保存当前顶点的y坐标
        k->points[i].z = rz;                            // 保存当前顶点的z坐标
    }
    fclose(filein);                                    // 关闭文件

    if (ver > maxver) maxver = ver;                                // 记录最大的顶点数
}

VERTEX Lesson25::calculate(int i)                                    // 计算第i个顶点每次变换的位移
{
    VERTEX a;
    a.x = (sour->points[i].x - dest->points[i].x) / steps;
    a.y = (sour->points[i].y - dest->points[i].y) / steps;
    a.z = (sour->points[i].z - dest->points[i].z) / steps;
    return a;
}

/**
 * 下面的函数用来为模型分配保存顶点数据的内存空间
 */
void Lesson25::objallocate(OBJECT *k, int n) {
    k->points = (VERTEX *) malloc(sizeof(VERTEX) * n);                    // 分配n个顶点的内存空间
}

void Lesson25::objfree(OBJECT *k) {
    free(k->points);
}

GLvoid Lesson25::ReSizeGLScene(GLsizei width, GLsizei height)                // 重置OpenGL窗口大小
{
    if (height == 0)                                        // Prevent A Divide By Zero By
    {
        height = 1;                                        // Making Height Equal One
    }

    glViewport(0, 0, width, height);                        // Reset The Current Viewport

    glMatrixMode(GL_PROJECTION);                        // Select The Projection Matrix
    glLoadIdentity();                                    // Reset The Projection Matrix

    // Calculate The Aspect Ratio Of The Window
    gluPerspective(45.0f, (GLfloat) width / (GLfloat) height, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);                            // Select The Modelview Matrix
    glLoadIdentity();
}

int Lesson25::InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);                        // 设置半透明混合模式
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);                    // 设置清除色为黑色
    glClearDepth(1.0);                                // 设置深度缓存中值为1
    glDepthFunc(GL_LESS);                            // 设置深度测试函数
    glEnable(GL_DEPTH_TEST);                            // 启用深度测试
    glShadeModel(GL_SMOOTH);                            // 设置着色模式为光滑着色
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

    /**
     * 加载我们的模型物体,需要注意的是各个模型必须要有相同的顶点,才能一一对应,并应用变形。
     */
    maxver = 0;                                    // 初始化最大顶点数为0
    objload("/home/zpw/Documents/CLionProjects/opengl_study/Data/Sphere.txt", &morph1);                        // 加载球模型
    objload("/home/zpw/Documents/CLionProjects/opengl_study/Data/Torus.txt", &morph2);                        // 加载圆环模型
    objload("/home/zpw/Documents/CLionProjects/opengl_study/Data/Tube.txt", &morph3);                        // 加载立方体模型

    /**
     * 第四个模型不从文件读取,我们在(-7,-7,-7)-(7,7,7)之间随机生成模型点,它和我们载如的模型都一样具有486个顶点。
     */
    objallocate(&morph4, 486);                            // 为第四个模型分配内存资源
    for (int i = 0; i < 486; i++)                            // 随机设置486个顶点
    {
        morph4.points[i].x = ((float) (rand() % 14000) / 1000) - 7;
        morph4.points[i].y = ((float) (rand() % 14000) / 1000) - 7;
        morph4.points[i].z = ((float) (rand() % 14000) / 1000) - 7;
    }

    /**
     * 初始化中间模型为球体,并把原和目标模型都设置为球
     */
    objload("/home/zpw/Documents/CLionProjects/opengl_study/Data/Sphere.txt", &helper);
    sour = &morph1;//初始图形
    dest = &morph2;//变换厚的模型
    return TRUE;                                // 初始化 OK
}

int Lesson25::DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);                // 清空缓存
    glLoadIdentity();                                // 重置模型变换矩阵
    glTranslatef(cx, cy, cz);                            // 平移和旋转
    glRotatef(xrot, 1, 0, 0);
    glRotatef(yrot, 0, 1, 0);
    glRotatef(zrot, 0, 0, 1);

    xrot += xspeed;
    yrot += yspeed;
    zrot += zspeed;                    // 根据旋转速度,增加旋转角度

    GLfloat tx, ty, tz;                                // 顶点临时变量
    VERTEX q;                                    // 保存中间计算的临时顶点

    glBegin(GL_POINTS);                                // 点绘制开始
    for (int i = 0; i < morph1.verts; i++)                        // 循环绘制模型1中的每一个顶点
    {
        if (morph) q = calculate(i); else q.x = q.y = q.z = 0;                // 如果启用变形,则计算中间模型
        helper.points[i].x -= q.x;
        helper.points[i].y -= q.y;
        helper.points[i].z -= q.z;
        tx = helper.points[i].x;                        // 保存计算结果到x,y,z变量中
        ty = helper.points[i].y;
        tz = helper.points[i].z;

        glColor3f(0, 1, 1);                        // 设置颜色
        glVertex3f(tx, ty, tz);                    // 绘制顶点
        glColor3f(0, 0.5f, 1);                    // 把颜色变蓝一些
        tx -= 2 * q.x;
        ty -= 2 * q.y;
        ty -= 2 * q.y;                // 如果启用变形,则绘制2步后的顶点
        glVertex3f(tx, ty, tz);
        glColor3f(0, 0, 1);                        // 把颜色变蓝一些
        tx -= 2 * q.x;
        ty -= 2 * q.y;
        ty -= 2 * q.y;                // 如果启用变形,则绘制2步后的顶点
        glVertex3f(tx, ty, tz);
    }
    glEnd();

    // 如果启用变形则把变形步数增加
    if (morph && step <= steps) {
        step++;
    } else {
        morph = FALSE;
        sour = dest;
        step = 0;
    }

    return TRUE;                                //  一切 OK
}
复制代码
关注下面的标签,发现更多相似文章
评论