用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 ( < ptr->tm_hour&&ptr->tm_hour < )
{
return((Pi / ) - ((float)ptr->tm_hour + Mymin(ptr) / 60.0) / 12.0 * * Pi);
}
else{
return((Pi / ) - ((ptr->tm_hour - 12.0 + Mymin(ptr) / 60.0) / ) * * 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进行立方体表面纹理贴图---不会飞的章鱼
三、完整代码如下
#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 = ; // X 旋转量
GLfloat yrot = ; // Y 旋转量
GLfloat zrot = ; // Z 旋转量
GLuint texture[]; // 存储一个纹理---数组 const GLfloat Pi = 3.1415926536;
const GLfloat R = 0.8f;
const int n = ;
static GLfloat angle = * 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[];
memset(TextureImage, , sizeof(void *) * ); // 将指针设为 NULL
// 载入位图,检查有无错误,如果位图没找到则退出
if (TextureImage[] = 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, , , TextureImage[]->sizeX, TextureImage[]->sizeY, , 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); // 线形滤波
} //释放图像的内存,因为已经生成纹理了,没用了
if (TextureImage[]) // 纹理是否存在
{
if (TextureImage[]->data) // 纹理图像是否存在
{
free(TextureImage[]->data); // 释放纹理图像占用的内存
}
free(TextureImage[]); // 释放图像结构
}
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[]); // 选择纹理
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[]);
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 / ) - (((float)ptr->tm_sec) / ) * * Pi);
} float Mymin(struct tm *ptr)
{
return((Pi / ) - ((ptr->tm_min + (Mysecond(ptr) / )) / ) * * Pi);
} float Myhour(struct tm *ptr)
{
if ( < ptr->tm_hour&&ptr->tm_hour < )
{
return((Pi / ) - ((float)ptr->tm_hour + Mymin(ptr) / 60.0) / 12.0 * * Pi);
}
else{
return((Pi / ) - ((ptr->tm_hour - 12.0 + Mymin(ptr) / 60.0) / ) * * 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, 1.0);
glColor4f(1.0, 0.0, 0.0, 0.5); //洋红色
glBegin(GL_POLYGON);
for (int i = ; i < n; i++){
glVertex2f(R*cos( * Pi / n*i), R*sin( * Pi / n*i));
}
glEnd(); //刻度
glColor4f(1.0, 1.0, 1.0, 0.5); //白色
glBegin(GL_POINTS);
glPointSize(5.0f);
for (int j = ; j < ; j++)
{
glVertex2f(0.75*cos( * Pi / * j), 0.75*sin( * Pi / * 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[], "clock3.bmp", ); //载入纹理贴图
LoadGLTextures(&texture[], "clock3.bmp", );
} //当窗口大小改变时,会调用这个函数
void reshape(GLsizei w, GLsizei h)
{
//这里小说明一下:矩阵模式是不同的,他们各自有一个矩阵。投影相关
//只能用投影矩阵。
glViewport(, , w, h); //设置视口
glMatrixMode(GL_PROJECTION); //设置矩阵模式为投影变换矩阵,
glLoadIdentity(); //变为单位矩阵
gluPerspective(, (GLfloat)w / h, , ); //设置投影矩阵
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 -= (( * Pi) / );
Sleep();
if (angle < 0.0f){
angle = * 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(, ); //设置窗口在屏幕中的位置
glutInitWindowSize(, ); //设置窗口的大小
glutCreateWindow("OpenGL时钟"); //设置窗口的标题
init(); //初始化资源,这里一定要在创建窗口以后,不然会无效。
LoadGLTextures(&texture[], "clock3.bmp", );
LoadGLTextures(&texture[], "clock3.bmp", );
glutDisplayFunc(&myDisplay); //调用画图函数
glutIdleFunc(&myIdle);
glutReshapeFunc(reshape); //绘制图形时的回调
glutKeyboardFunc(keyboard);
glutMainLoop(); //进行一个消息循环。显示窗口,并等待窗口关闭后才会返回 return ;
}
平面效果:
动态效果:
四、总结
此次设计主要用了纹理贴图和二维绘图的知识。
我还记得最开始设计时钟时,背景图是黑色的,而且图片也是随便贴了一张上去,给指导老师看过了后,他评价道:“你能否让我看起来你像是做了个时钟?比如把背景颜色调一调,纹理换一换。”
我恍然大悟。是啊!做课程设计本来也是一件艺术品,要用心设计,才能让有兴趣的人愿意为此驻足欣赏。
用OpenGL实现动态的立体时钟的更多相关文章
- 贝塞尔曲线.简单推导与用opengl实现动态画出。
在opengl中,我们可以用少许的参数来描述一个曲线,其中贝塞尔曲线算是一种很常见的曲线控制方法,我们先来看维基百科里对贝塞尔曲线的说明: 线性贝塞尔曲线 给定点P0.P1,线性贝塞尔曲线只是一条两点 ...
- js 实现动态的图片时钟
效果如下图 附件有图片 http://files.cnblogs.com/files/biyongyao/时钟.rar 源代码 <!DOCTYPE html> <html> ...
- 用OpenGL实现跳跃的立体小球
一.目的 掌握OpenGL中显示列表对象的使用方法. 二.示例代码 Github地址 #include "stdafx.h" #include <GL/glut.h> ...
- 一个很好的网站 有3D漂浮框 有动态小人数字时钟
http://www.cnblogs.com/jingmoxukong/p/7867397.html
- Android OpenGL库加载过程源码分析
Android系统采用OpenGL绘制3D图形,使用skia来绘制二维图形:OpenGL源码位于: frameworks/native/opengl frameworks/base/opengl 本文 ...
- [学习资料] Tiny210(S5PV210) u-boot移植
Tiny210(S5PV210) u-boot移植http://www.microoh.com/bbs/forum.php?mod=viewthread&tid=254&fromuid ...
- linux 时间管理——概念、注意点(一)【转】
转自:http://www.cnblogs.com/openix/p/3324243.html 参考:1.http://bbs.eyeler.com/thread-69-1-1.html ...
- VR 相关专业词汇
最近在看 SIGGRAPH2015 有关 VR Display and Interaction 的几篇文章,之前从来没看过有关方面的 paper,一看才发现专业词汇太多了,根本不懂啊,幸亏 Paper ...
- Android 实例子源代码文件下载地址380个合集
android 城市列表特效 - 触摸查找源码 .rar: http://www.t00y.com/file/64337887 android 日记系统源码(数据库的基本操作) .rar: htt ...
随机推荐
- loj#2016. 「SCOI2016」美味
题目链接 loj#2016. 「SCOI2016」美味 题解 对于不带x的怎么做....可持久化trie树 对于带x,和trie树一样贪心 对于答案的二进制位,从高往低位贪心, 二进制可以表示所有的数 ...
- BZOJ4714 : 旋转排列
对于每个$k$,问题等价于求有多少置换满足: 1.存在一个循环长度为$k$ 2.任意一个循环长度$\geq 2$ 枚举这种环的个数$t$: 设$g_t$表示至少有$kt$个人分成$t$个长度为$k$的 ...
- java三大特性--继承
定义: 继承就是子类继承父类的特征和行为,使得子类具有父类的各种属性和方法,使得子类具有父类相同的行为. 继承的好处: 有效实现代码复用,避免重复代码的出现. 让类与类之间产生了关系,是多态的前提. ...
- 奇怪吸引子---LorenaMod1
奇怪吸引子是混沌学的重要组成理论,用于演化过程的终极状态,具有如下特征:终极性.稳定性.吸引性.吸引子是一个数学概念,描写运动的收敛类型.它是指这样的一个集合,当时间趋于无穷大时,在任何一个有界集上出 ...
- SqlSession介绍
SqlSession是MyBatis的关键对象,是执行持久化操作的对象,类似于JDBC中的Connection.它是应用程序与持久存储层之间执行交互操作的一个单线程对象,也是MyBatis执行持久化操 ...
- 虚拟机下CentOS7开启SSH连接
在虚拟机(Vmware Workstation)下,安装了CentOS7,现在想通过SSH工具连接虚拟机中的CentOS7 1. 首先,要确保CentOS7安装了 openssh-server,在 ...
- android ndk-build 编译静态库libxx.a 以及Android studio openssl 静态库配置(cmake)
android ndk-build 编译静态库libxx.a 需求场景: 目前有安卓编码好的现在的openssl的两个.a,我们需要调用openssl的函数,并把功能再封装成.a; 这样使用时,在an ...
- 树莓派3中编译Opencv3.4.10
一.命令 -dev libv4l-dev libavcodec-dev libavformat-dev libswscale-dev cd wget http://sourceforge.net/pr ...
- 基于Python3.6使用Django框架连接mysql数据库的驱动模块安装解决办法
解决办法1 使用PyMySQL模块,直接使用pip install pymysql即可. 参考文章:https://www.cnblogs.com/wcwnina/p/8719482.html 原文内 ...
- [Aaronyang] 写给自己的WPF4.5 笔记11[自定义控件-AyImageButton的过程 1/4]
我的文章一定要对读者负责-否则不是好文章 ---- www.ayjs.net aaronyang技术分享 文章导航: 介绍vs2013 WPF开发,属性代码相关技巧 实战AyImage ...