用OpenGL實現動態的立體時鐘

(在學期末作的圖形學課程設計,特將學習心得整理以下)html

1、設計思路

1,設計一個平面的時鐘;git

按照 鐘面——>中心點——>刻度——>時針——>分針——>秒針 的順序繪製。github

2,利用紋理貼圖的知識使平面時鐘變成立體的時鐘;數組

3,設置鍵盤交互;緩存

4,測試,修改,整理代碼。函數

 

2、部分代碼設計

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進行立方體表面紋理貼圖---不會飛的章魚oop

 

3、完整代碼以下

Github地址學習

#include "stdafx.h"
#include<Windows.h>
#include<GL\glut.h>
#include<GL\GLAUX.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;
}

 

平面效果:測試

 

動態效果:ui

 

4、總結

這次設計主要用了紋理貼圖和二維繪圖的知識。

我還記得最開始設計時鐘時,背景圖是黑色的,並且圖片也是隨便貼了一張上去,給指導老師看過了後,他評價道:「你可否讓我看起來你像是作了個時鐘?好比把背景顏色調一調,紋理換一換。」

我恍然大悟。是啊!作課程設計原本也是一件藝術品,要用心設計,才能讓有興趣的人願意爲此駐足欣賞。

相關文章
相關標籤/搜索