圖形學之C語言OpenGL實現三角形繞其中心旋轉,點選右鍵選單改變大小和顏色,鍵盤按鍵控制旋轉的開始和暫停等功能
一、 實驗要求
1.使用滑鼠選擇視窗內任意位置畫N個等腰三角形。
2.要畫的三角形大小及顏色通過右鍵彈出選單選定(任選M個顏色)。
3.設定三角形繞其中心旋轉的開始、停止、旋轉方向的功能鍵,並通過相應按鍵控制三角形旋轉。
二、 完成情況
由於沒能實現在滑鼠任意選定的位置畫三角形並且讓其旋轉(分開很好實現),於是便簡化了一下,在固定位置畫N個三角形,並讓其繞中心點旋轉。可以通過右鍵選單退出、改變大小和改變顏色。同時還可以通過空格鍵控制旋轉的開始和停止,通過C字母鍵控制旋轉的方向(順時針或逆時針)。
三、 各功能介紹
1. 畫等腰三角形並旋轉:
為了方便確定三角形的中點,我畫的是特殊的等腰三角形——等邊三角形。設其一頂點的座標為 ( cos(spin) , sin(spin) ),則另外兩點的座標為 ( cos(spin-120) ,sin(spin-120) ) 和 (cos(spin+120) , sin(spin+120) ),其中spin為轉過的角度。旋轉時只要改變 spin 的值即可。
程式碼:
void display()
{
glClear (GL_COLOR_BUFFER_BIT);
glPushMatrix();//將當前矩陣壓棧(儲存現場)
glBegin(GL_TRIANGLES);
//設頂點座標為(cos(spin),sin(spin)),其他兩點座標可由spin +/- 120°後得到
glVertex2f(cos(DR*spin)*size+30,sin(DR*spin)*size+30);
glVertex2f(cos(DR*(spin+120))*size+30,sin(DR*(spin+120))*size+30);
glVertex2f(cos(DR*(spin-120))*size+30,sin(DR*(spin-120))*size+30);
glVertex2f(cos(DR*spin)*size+10,sin(DR*spin)*size-30);
glVertex2f(cos(DR*(spin+120))*size+10,sin(DR*(spin+120))*size-30);
glVertex2f(cos(DR*(spin-120))*size+10,sin(DR*(spin-120))*size-30);
glVertex2f(cos(DR*spin)*size-20,sin(DR*spin)*size+20);
glVertex2f(cos(DR*(spin+120))*size-20,sin(DR*(spin+120))*size+20);
glVertex2f(cos(DR*(spin-120))*size-20,sin(DR*(spin-120))*size+20);
glVertex2f(cos(DR*spin)*size-35,sin(DR*spin)*size-20);
glVertex2f(cos(DR*(spin+120))*size-35,sin(DR*(spin+120))*size-20);
glVertex2f(cos(DR*(spin-120))*size-35,sin(DR*(spin-120))*size-20);
glEnd();
glPopMatrix();
glutSwapBuffers();//雙快取技術的函式,作用為交換兩個緩衝區的指標
}
效果:
2. 右鍵彈出選單:
要設定一個退出選項以及改變大小子選單和顏色子選單,直接利用程式碼寫出即可。改變大小時,可以讓全域性變數 size 增大或縮小一倍。改變顏色時可以用glColor3f 函式將前景色改變為相應的顏色。
程式碼:
/*右鍵選單*/
/*大小子選單*/
int sub_menu1= glutCreateMenu(size_menu);
glutAddMenuEntry("Increasesize",2);
glutAddMenuEntry("Decreasesize",3);
/*顏色子選單*/
intsub_menu2 = glutCreateMenu(color_menu);
glutAddMenuEntry("red",4);
glutAddMenuEntry("blue",5);
glutAddMenuEntry("yellow",6);
/*主選單*/
glutCreateMenu(main_menu);
glutAddMenuEntry("Quit",1);
glutAddSubMenu("Size",sub_menu1); //新增子選單
glutAddSubMenu("Color",sub_menu2); //新增子選單
glutAttachMenu(GLUT_RIGHT_BUTTON);
效果:
3. 鍵盤控制旋轉的開始停止以及旋轉方向:
分別設了兩個全域性變數:flag和dir,取值為 0 或 1,按鍵一次值就隨之改變一次。分別用來控制旋轉的開始和停止以及旋轉的方向。旋轉的開始和結束可以用glutIdleFunc函式傳入不同的引數實現。旋轉的方向可以通過改變 spin 角度增加還是減少來實現。
程式碼:
void myKey(unsigned char key,intx,int y)
{ //鍵盤
if(key=='')
{//如果按下空格鍵,則控制旋轉和暫停
flag^=1;
if(flag)glutIdleFunc(spinDisplay);
elseglutIdleFunc(NULL);
}
//按下C字母改變旋轉方向
elseif(key=='c'||key=='C') dir^=1;
}
四、 難點分析
最困難的部分就是用滑鼠點選N個位置畫三角形和三角形旋轉分別都好實現,但是一旦放到一起就會出各種錯誤,只能簡化了一下要求。
其次就是三角形繞其中心旋轉的實現,一開始是從網上找的一個函式,呼叫之後可以繞x、y、z軸旋轉,但是效果不好,不是繞中心旋轉,後來自己模仿PPT上面正方形旋轉的案例自己改動了一下。
其他的之前的程式碼中都有類似的案例,不算太難,模仿一下就可以了。
五、 總程式碼
#include <stdlib.h>
#include <GL/glut.h>
#include <math.h>
#define DR 3.14159/180.0
int N=4;
int num=0;
int dir=1;
int flag=1;
double size=10;
static GLfloat spin = 0.0;
GLfloat x, y;
int doubleb;
void triangle(GLfloat x,GLfloat y)
{ //畫三角形,實際沒用到……
glPushMatrix();//將當前矩陣壓棧(儲存現場)
glBegin(GL_TRIANGLES);
glVertex2f(x,y+size/2);
glVertex2f(x-size/2,y-size/2);
glVertex2f(x+size/2,y-size/2);
glEnd();
glPopMatrix();
glutSwapBuffers();//雙快取技術的函式,作用為交換兩個緩衝區的指標
}
void display()
{
glClear (GL_COLOR_BUFFER_BIT);
glPushMatrix();//將當前矩陣壓棧(儲存現場)
glBegin(GL_TRIANGLES);
//設頂點座標為(cos(spin),sin(spin)),其他兩點座標可由spin +/- 120°後得到
glVertex2f(cos(DR*spin)*size+30,sin(DR*spin)*size+30);
glVertex2f(cos(DR*(spin+120))*size+30,sin(DR*(spin+120))*size+30);
glVertex2f(cos(DR*(spin-120))*size+30,sin(DR*(spin-120))*size+30);
glVertex2f(cos(DR*spin)*size+10,sin(DR*spin)*size-30);
glVertex2f(cos(DR*(spin+120))*size+10,sin(DR*(spin+120))*size-30);
glVertex2f(cos(DR*(spin-120))*size+10,sin(DR*(spin-120))*size-30);
glVertex2f(cos(DR*spin)*size-20,sin(DR*spin)*size+20);
glVertex2f(cos(DR*(spin+120))*size-20,sin(DR*(spin+120))*size+20);
glVertex2f(cos(DR*(spin-120))*size-20,sin(DR*(spin-120))*size+20);
glVertex2f(cos(DR*spin)*size-35,sin(DR*spin)*size-20);
glVertex2f(cos(DR*(spin+120))*size-35,sin(DR*(spin+120))*size-20);
glVertex2f(cos(DR*(spin-120))*size-35,sin(DR*(spin-120))*size-20);
glEnd();
glPopMatrix();
glutSwapBuffers();//雙快取技術的函式,作用為交換兩個緩衝區的指標
}
void spinDisplay (void)
{ //角度變化
if(dir) spin = spin + 0.05;
else spin=spin - 0.05;
if (spin > 360.0) spin = spin - 360.0;
glutSetWindow(doubleb);
glutPostRedisplay();
}
void myinit ()
{ //初始化
glViewport(0,0,500,500);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0,(GLdouble)500,0.0,(GLdouble)500,-1.0,1.0);
glMatrixMode(GL_MODELVIEW);
glClearColor(0.0,0.0,0.0,1.0);
glColor3f(1.0,1.0,1.0); //前景色為白色
}
void myMouse(int btn,int state,int x,int y)
{ //滑鼠
if(btn==GLUT_LEFT_BUTTON && state==GLUT_UP)
{//點選滑鼠左鍵畫三角形,但沒起到作用
if(num<N) triangle(x,y);
num++;
}
}
void myKey(unsigned char key,int x,int y)
{ //鍵盤
if(key==' ')
{ //如果按下空格鍵,則控制旋轉和暫停
flag^=1;
if(flag) glutIdleFunc(spinDisplay);
else glutIdleFunc(NULL);
}
//按下C字母改變旋轉方向
else if(key=='c'||key=='C') dir^=1;
}
void myReshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-50.0, 50.0, -50.0*(GLfloat)h/(GLfloat)w,
50.0*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-50.0*(GLfloat)w/(GLfloat)h,
50.0*(GLfloat)w/(GLfloat)h, -50.0, 50.0, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity ();
}
void myTimer(int v)
{
glutSetWindow(doubleb);
glutPostRedisplay();
glutTimerFunc(100, myTimer, v);
}
void main_menu(int id)
{ //主選單
switch(id)
{
case 1: exit(0);
break;
}
}
void size_menu(int id)
{ //大小選單
switch(id)
{
case 2: size = 2*size;
break;
case 3: if(size>1) size = size/2;
break;
}
}
void color_menu(int id)
{ //顏色選單
switch(id)
{
case 4: glColor3f(1.0,0.0,0.0); //紅色
break;
case 5: glColor3f(0.0,0.0,1.0); //藍色
break;
case 6: glColor3f(1.0,1.0,0.0); //黃色
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc,argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(800,800);
glutInitWindowPosition(0,0);
doubleb=glutCreateWindow("Triangle");
/*右鍵選單*/
/*大小子選單*/
int sub_menu1 = glutCreateMenu(size_menu);
glutAddMenuEntry("Increase size",2);
glutAddMenuEntry("Decrease size",3);
/*顏色子選單*/
int sub_menu2 = glutCreateMenu(color_menu);
glutAddMenuEntry("red",4);
glutAddMenuEntry("blue",5);
glutAddMenuEntry("yellow",6);
/*主選單*/
glutCreateMenu(main_menu);
glutAddMenuEntry("Quit",1);
glutAddSubMenu("Size", sub_menu1); //新增子選單
glutAddSubMenu("Color", sub_menu2); //新增子選單
glutAttachMenu(GLUT_RIGHT_BUTTON);
myinit ();
glutDisplayFunc(display);
glutMouseFunc (myMouse); //滑鼠事件
glutKeyboardFunc(myKey); //鍵盤事件
glutReshapeFunc (myReshape);
glutIdleFunc (spinDisplay);
glutTimerFunc(100,myTimer,60);
glutMainLoop();
return 0;
}