1. 程式人生 > >圖形學之C語言OpenGL實現三角形繞其中心旋轉,點選右鍵選單改變大小和顏色,鍵盤按鍵控制旋轉的開始和暫停等功能

圖形學之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;
}