zoukankan      html  css  js  c++  java
  • 用OpenGL实现动态的立体时钟

    (在学期末做的图形学课程设计,特将学习心得整理如下)

    一、设计思路

    1,设计一个平面的时钟;

    按照 钟面——>中心点——>刻度——>时针——>分针——>秒针 的顺序绘制。

    2,利用纹理贴图的知识使平面时钟变成立体的时钟;

    3,设置键盘交互;

    4,测试,修改,整理代码。

    二、部分代码设计

    1,键盘交互

    void keyboard(unsigned char key, int x, int y)
    {
        switch (key)
        {
        case 'x':    //当按下键盘上d时,以沿X轴旋转为主
            xrot += 6.0f;   //设置旋转增量
            glutPostRedisplay();   //重绘函数
            break;
        case 'y':
            yrot += 6.0f;
            glutPostRedisplay();
            break;
        case 'z':
            zrot += 6.0f;
            glutPostRedisplay();
            break;
        default:
            break;
        }
    }

    2,时针绘制(秒针、分针类似)

    float Myhour(struct tm *ptr)
    {
        if (0 < ptr->tm_hour&&ptr->tm_hour < 12)
        {
            return((Pi / 2) - ((float)ptr->tm_hour + Mymin(ptr) / 60.0) / 12.0 * 2 * Pi);
        }
        else{
            return((Pi / 2) - ((ptr->tm_hour - 12.0 + Mymin(ptr) / 60.0) / 12) * 2 * Pi);
        }
    }
    
        glLineWidth(5.0f);  //设置线的宽度
        glColor4f(1.0, 0.0, 1.0, 0.5);  //洋红色
        glBegin(GL_LINES);  //画线函数
        glRotatef((angle / 3600.0), 0.0, 0.0, 1.0);
        glVertex2f(0.0, 0.0);
        glVertex2f(cos(Myhour(ptr))*R*0.55, sin(Myhour(ptr))*R*0.55);
        glEnd();

    3,纹理贴图

    请参照这篇文章:用OpenGL进行立方体表面纹理贴图---不会飞的章鱼

    三、完整代码如下

    Github地址

    #include "stdafx.h"
    #include<Windows.h>
    #include<GLglut.h>
    #include<GLGLAUX.H>
    #include<stdio.h>
    #include<stdlib.h>
    #include<math.h>
    #include<time.h>
    
    #pragma comment(lib, "glut32.lib")
    #pragma comment(lib, "glaux.lib")
    
    GLfloat  xrot = 0;   // X 旋转量
    GLfloat  yrot = 0;   // Y 旋转量
    GLfloat  zrot = 0;   // Z 旋转量
    GLuint  texture[1];  // 存储一个纹理---数组
    
    const GLfloat Pi = 3.1415926536;
    const GLfloat R = 0.8f;
    const int n = 200;
    static GLfloat angle = 2 * Pi;
    
    //载入位图图象到内存
    AUX_RGBImageRec *LoadBMP(CHAR *Filename)
    {
        FILE *File = NULL;         // 文件句柄
        if (!Filename)          // 确保文件名已提供
        {
            return NULL;         // 如果没提供,返回 NULL
        }
        File = fopen(Filename, "r");       // 尝试打开文件
        if (File)           // 判断文件是否存在
        {
            fclose(File);         // 关闭句柄
            return auxDIBImageLoadA(Filename);    // 载入位图并返回指针
        }
        return NULL;          // 如果载入失败,返回 NULL
    }
    
    //载入位图并转换成纹理
    //参数:纹理指针、bmp文件名、用户指定的纹理编号
    int LoadGLTextures(GLuint *texture, char *bmp_file_name, int texture_id)
    {
        int Status = FALSE;         // 状态指示器
        // 创建纹理的存储空间
        AUX_RGBImageRec *TextureImage[1];
        memset(TextureImage, 0, sizeof(void *) * 1);   // 将指针设为 NULL
        // 载入位图,检查有无错误,如果位图没找到则退出
        if (TextureImage[0] = LoadBMP(bmp_file_name))
        {
            Status = TRUE;         // 将 Status 设为 TRUE
            //生成(generate)纹理
            glGenTextures(texture_id, texture); //&texture[0]);     
            //绑定2D纹理对象
            glBindTexture(GL_TEXTURE_2D, *texture); //texture[0]);
            //关联图像数据与纹理对象
            glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
            //图形绘制时所使用的滤波器参数
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 线形滤波
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 线形滤波
        }
    
        //释放图像的内存,因为已经生成纹理了,没用了
        if (TextureImage[0])        // 纹理是否存在
        {
            if (TextureImage[0]->data)      // 纹理图像是否存在
            {
                free(TextureImage[0]->data);    // 释放纹理图像占用的内存
            }
            free(TextureImage[0]);       // 释放图像结构
        }
        else
            printf("纹理不存在");
        return Status;          // 返回 Status
    }
    
    void DrawCube(void)         // 从这里开始进行所有的绘制
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存
        glLoadIdentity();         // 重置当前的模型观察矩阵
        glTranslatef(0.0f, 0.0f, -5.0f);         // 移入屏幕 5 个单位
        glRotatef(xrot, 1.0f, 0.0f, 0.0f);         // 绕X轴旋转
        glRotatef(yrot, 0.0f, 1.0f, 0.0f);         // 绕Y轴旋转
        glRotatef(zrot, 0.0f, 0.0f, 1.0f);         // 绕Z轴旋转
        glColor4f(1.0, 1.0, 1.0, 1.0);
        glBindTexture(GL_TEXTURE_2D, texture[0]);      // 选择纹理
        glBegin(GL_QUADS);
        // 前面
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的左下
        glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); // 纹理和四边形的右下
        glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); // 纹理和四边形的右上
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的左上
        // 后面
        glEnd();
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glBindTexture(GL_TEXTURE_2D, texture[1]);
        glBegin(GL_QUADS);
    
        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); // 纹理和四边形的左下
        // 顶面
        glEnd();
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glBegin(GL_QUADS);
    
        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); // 纹理和四边形的右上
        // 底面
        glEnd();
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glBegin(GL_QUADS);
    
        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); // 纹理和四边形的右下
        // 右面
        glEnd();
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glBegin(GL_QUADS);
    
        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); // 纹理和四边形的左下
        // 左面
        glEnd();
        glColor4f(1.0, 1.0, 0.0, 1.0);
        glBegin(GL_QUADS);
    
        glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // 纹理和四边形的左下
        glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的右下
        glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的右上
        glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // 纹理和四边形的左上
        glEnd();
    
        glFlush(); //glutSwapBuffers();
    }
    
    float Mysecond(struct tm *ptr)
    {
        return((Pi / 2) - (((float)ptr->tm_sec) / 60) * 2 * Pi);
    }
    
    float Mymin(struct tm *ptr)
    {
        return((Pi / 2) - ((ptr->tm_min + (Mysecond(ptr) / 60)) / 60) * 2 * Pi);
    }
    
    float Myhour(struct tm *ptr)
    {
        if (0 < ptr->tm_hour&&ptr->tm_hour < 12)
        {
            return((Pi / 2) - ((float)ptr->tm_hour + Mymin(ptr) / 60.0) / 12.0 * 2 * Pi);
        }
        else{
            return((Pi / 2) - ((ptr->tm_hour - 12.0 + Mymin(ptr) / 60.0) / 12) * 2 * Pi);
        }
    }
    
    void myDisplay(void)
    {
        struct tm *ptr;  //获取系统时间
        time_t it;
        it = time(NULL);
        ptr = localtime(&it);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);  //清除颜色
        glLoadIdentity();
        glTranslatef(0.0f, 0.0f, -5.0f);
    
        DrawCube();//钟盘
        glEnable(GL_POINT_SMOOTH);
        glEnable(GL_LINE_SMOOTH);
        glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
        glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glPushMatrix();
        glTranslatef(0, -0.0, 1.0);
        glColor4f(1.0, 0.0, 0.0, 0.5);  //洋红色
        glBegin(GL_POLYGON);
        for (int i = 0; i < n; i++){
            glVertex2f(R*cos(2 * Pi / n*i), R*sin(2 * Pi / n*i));
        }
        glEnd();
    
        //刻度
        glColor4f(1.0, 1.0, 1.0, 0.5);  //白色
        glBegin(GL_POINTS);
        glPointSize(5.0f);
        for (int j = 0; j < 12; j++)
        {
            glVertex2f(0.75*cos(2 * Pi / 12 * j), 0.75*sin(2 * Pi / 12 * j));
        }
        glEnd();
    
        //表盘上的中心点
        glPointSize(5.0f);
        glColor4f(0.0, 0.0, 0.0, 0.2);
        glBegin(GL_POINTS);
        glVertex2f(0.0, 0.0);
        glEnd();
    
        //时针
        glLineWidth(5.0f);  //设置线的宽度
        glColor4f(1.0, 0.0, 1.0, 0.5);  //洋红色
        glBegin(GL_LINES);  //画线函数
        glRotatef((angle / 3600.0), 0.0, 0.0, 1.0);
        glVertex2f(0.0, 0.0);
        glVertex2f(cos(Myhour(ptr))*R*0.55, sin(Myhour(ptr))*R*0.55);
        glEnd();
    
        //分针
        glLineWidth(5.0f);
        glColor4f(0.0, 1.0, 0.0, 0.5);  //绿色
        glBegin(GL_LINES);
        glRotatef((angle / 60.0), 0.0, 0.0, 1.0);
        glVertex2f(0.0, 0.0);
        glVertex2f(cos(Mymin(ptr))*R*0.65, sin(Mymin(ptr))*R*0.65);
        glEnd();
    
        //秒针
        glLineWidth(3.0f);
        glColor4f(0.0, 0.0, 1.0, 0.5);  //蓝色
        glBegin(GL_LINES);
        glRotatef(angle, 0.0, 0.0, 1.0);
        glVertex2f(0.0, 0.0);
        glVertex2f(cos(Mysecond(ptr))*R*0.85, sin(Mysecond(ptr))*R*0.85);
        glEnd();
        glPopMatrix();
        glutSwapBuffers();//    glFlush();  //保证前面的OpenGL命令立即执行,而不是让它们在缓冲区中等待
    }
    
    void init(void)
    {
        glClearColor(1.0, 1.0, 1.0, 1.0);  //清理颜色,为白色,(也可认为是背景颜色)
        glCullFace(GL_BACK);
        //背面裁剪(背面不可见)
        glEnable(GL_CULL_FACE);
        //启用裁剪
        glEnable(GL_TEXTURE_2D);
        LoadGLTextures(&texture[0], "clock3.bmp", 1);            //载入纹理贴图
        LoadGLTextures(&texture[1], "clock3.bmp", 2);
    }
    
    //当窗口大小改变时,会调用这个函数
    void reshape(GLsizei w, GLsizei h)
    {
        //这里小说明一下:矩阵模式是不同的,他们各自有一个矩阵。投影相关
        //只能用投影矩阵。
        glViewport(0, 0, w, h);        //设置视口
        glMatrixMode(GL_PROJECTION);    //设置矩阵模式为投影变换矩阵,
        glLoadIdentity();                //变为单位矩阵
        gluPerspective(60, (GLfloat)w / h, 0, 1000);    //设置投影矩阵
        glMatrixMode(GL_MODELVIEW);        //设置矩阵模式为视图矩阵(模型)
        glLoadIdentity();                //变为单位矩阵
    }
    
    //键盘输入事件函数
    void keyboard(unsigned char key, int x, int y)
    {
        switch (key)
        {
        case 'x':    //当按下键盘上d时,以沿X轴旋转为主
            xrot += 6.0f;   //设置旋转增量
            glutPostRedisplay();   //重绘函数
            break;
        case 'y':
            yrot += 6.0f;
            glutPostRedisplay();
            break;
        case 'z':
            zrot += 6.0f;
            glutPostRedisplay();
            break;
        default:
            break;
        }
    }
    
    void myIdle(void)
    {
        angle -= ((2 * Pi) / 60);
        Sleep(1000);
        if (angle < 0.0f){
            angle = 2 * Pi;
        }
        myDisplay();
    }
    
    int main(int argc, char *argv[])
    {
        glutInit(&argc, argv);  //对GLUT进行初始化
        //    glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);  //设置显示方式
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);//设置双缓存
        //GLUT_RGB表示使用RGB颜色,GLUT_SINGLE表示使用单缓冲
        glutInitWindowPosition(200, 200);  //设置窗口在屏幕中的位置
        glutInitWindowSize(500, 500);  //设置窗口的大小
        glutCreateWindow("OpenGL时钟");  //设置窗口的标题
        init();  //初始化资源,这里一定要在创建窗口以后,不然会无效。
        LoadGLTextures(&texture[0], "clock3.bmp", 1);
        LoadGLTextures(&texture[1], "clock3.bmp", 2);
        glutDisplayFunc(&myDisplay);  //调用画图函数
        glutIdleFunc(&myIdle);
        glutReshapeFunc(reshape);                //绘制图形时的回调
        glutKeyboardFunc(keyboard);
        glutMainLoop();  //进行一个消息循环。显示窗口,并等待窗口关闭后才会返回
    
        return 0;
    }

    平面效果:

    动态效果:

    四、总结

    此次设计主要用了纹理贴图和二维绘图的知识。

    我还记得最开始设计时钟时,背景图是黑色的,而且图片也是随便贴了一张上去,给指导老师看过了后,他评价道:“你能否让我看起来你像是做了个时钟?比如把背景颜色调一调,纹理换一换。”

    我恍然大悟。是啊!做课程设计本来也是一件艺术品,要用心设计,才能让有兴趣的人愿意为此驻足欣赏。

  • 相关阅读:
    JVM学习笔记(三)——类加载机制
    JVM学习笔记(二)——垃圾收集器和内存分配策略
    JVM学习笔记(一) ——Java虚拟机内存结构
    KMP算法及next数组优化
    C Primer Plus课后编程习题
    【小白出错日记】C语言篇
    C语言学习重点提纲
    编译原理学习-形式语言 乔姆斯基文法
    3.11上午课程重点
    FPGA学习-PS2接口
  • 原文地址:https://www.cnblogs.com/OctoptusLian/p/7367641.html
Copyright © 2011-2022 走看看