opengl 計算機圖形學 第三版 第二部分 第三章更多的繪圖工具ios
3.1 概述c++
第2章中 咱們繪圖使用的是屏幕窗口的基礎座標系 以像素爲單位 算法
屏幕座標從左下角x從0延伸到screenWidth-1 y從0向上延伸到screenHeight-1 只能使用非負的x和ywindows
程序中用於描述對象幾何信息 此過程爲建模任務數組
在屏幕中如何將這個對象 按照必定比例顯示出來 是一個觀看的任務緩存
使用最適合與手中問題的座標系來描述對象,而且能夠自動的縮放和平移圖片,使得其能正確地在屏幕窗口中顯示,這個描述對象的空間被稱爲世界座標系(場景中的物體在實際世界中的座標)。在合適的單位下,它就是數學中經常使用的笛卡爾xy座標系。框架
世界窗口:在世界座標系中定義一個對齊的矩形 (矩形的邊與座標軸平行) 不必定是屏幕函數
世界窗口指明瞭要繪製世界的哪一部分。對此能夠理解爲,任何在窗口中的部分須要被繪製,而窗口外的部分被裁減並不被繪製。 opengl會自動的裁減。工具
顯示器的屏幕窗口上定義一個對齊的矩形的視口,不一樣於世界窗口 opengl會自動創建世界窗口和視口的變換(包括縮放和平移)。oop
對象在世界窗口中顯示的部分會被自動的映射到視口中,也就是被映射到屏幕座標中。
對齊的矩形做爲窗口和視口(屏幕窗口)的形狀緣由:
1.矩形僅須要四個參數描述
2.檢測一個點的位置(窗口內外) 很容易,能夠簡化裁減算法。
3.對齊的矩形爲凸 簡化繪製算法
4.經過一個窗口取看被繪製的物體, 並價格窗口中可見的快照放到顯示器的視口中
採用窗口/視口的聯合方式, 當兩個口被指定時,coder能夠編寫一個算法,不考慮圖片的大小和位置,全部潛在的操做(縮放、位置和裁減)都會自動完成,並將圖片放到視口中去。
3.2世界窗口和視口
建立一個函數圖像以後,並不知道 圖像的位置和圖像的大小,因此使用世界窗口就很是的重要
setWindow()用來創建世界窗口
setViewport()來創建視口
代碼中涉及的座標是在天然座標系中操做的 例如x在-4.0~4.0 變化的 關鍵問題是如何縮放和平移不一樣的(x,y) 使得圖像能在屏幕窗口中恰當的顯示。
經過設置一個世界窗口和一個視口,並創建它們之間的一個合適的映射,就能夠完成適當的平移和縮放。視口和窗口是coder 指定的對齊的矩形。
窗口爲世界座標系中
視口爲屏幕窗口的一部分。
前者爲世界窗口;後者爲在屏幕中劃出一部分區域爲屏幕窗口,在其中再畫出一部分爲視口。
3.2.1 窗口到視口的映射
圖中的w和v都是GLfloat類型的數值,表明做標值,其中left 和right爲x軸的值 、 bottom和top爲y軸的值,綜合到一塊兒成爲了對齊的矩形
世界窗口和視口 縱橫比不必定一致 若是不一致會致使圖像的拉伸,因此須要調整世界窗口和視口的縱橫比一致
一個映射或者變換,即窗口到視口的映射,這個映射是基於一個等式,對每個在世界座標下的點(x,y),產生屏幕座標系中的一個點(sx,sy)。
保持比例的性質 有下面的線性性質:
sx=A*x+C
sy=B*y+D
其中A、B、C、D是常數, A B縮放x和y的座標 而 C D用來平移它們。
a 若是x是窗口的左邊界 x=w.left 則sx爲視口的左邊界 sx=v.left
b 若是x是在窗口的右邊界x=w.right,則sx是在視口的右邊界 sx=v.right
c 對於某個比例f 若是x位於窗口的1/f處 則sx位於視口寬度的1/f處
d 若是x在窗口的左邊界外 x<w.left 則sx也在視口的的左邊界外 sx<v.left
設立窗口到視口的映射
opengl經過多個變換完成所須要的映射,自動的傳送給定的每個頂點 經過glVertex2*()命令
二維圖形中,世界窗口由函數gluOrth2D()來設定 原型以下:
void gluOrtho2D(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top);
三維的狀況 還有另外兩個參數須要設定 省略
視口的設定:
void glViewport(GLint x,GLint y,GLint width,GLint eight); 左下角的座標以及寬度和高度
經過矩陣來完成全部的變換,所以gluOrtho2D()的調用必須在glMatrixModel(GL_PROJECTION)和glLoadIdentity()兩個函數以後完成
因此創建窗口設視口之間的聯繫須要以下代碼:
glMatrixModel(GL_PROJECTION;
glLoadIdentity();
gluOrtho2D(0.0,2.0,0.0,1.0); 設置窗口
glViewport(40,60,360,240); 設置視口
在此以後 每一個使用glVertex2*(x,y)發送給opengl的點,會進行等式的映射,而且在窗口的邊界處會被自動裁減掉邊緣。
將設定窗口的函數放在setWindow()函數中 將glViewport() 放在setViewport函數中
void setWindow(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top) 設置窗口
{
glMatrixModel(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(left,right,bottom,top);
}
void setViewport(GLint left,GLint right,GLint bottom,GLint top) 設置視口
{
glViewport(left,bottom,right-left,top-bottom);
}
(1)在main()中
glutInitWindowSize(640,480) 設置窗口大小
沒有使用glViewport的命令 因此使用默認的視口,默認的視口就是整個屏幕窗口。
(2)在myInit()中
glMatrixModel(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,640.0,0.0,480.0); //關鍵的設置 與視口比較 以及縱橫比
上面這些函數都是將世界窗口設置爲對齊的矩形,其兩個角的座標是(0,0)和(640.0,480.0) 正好與視口的大小相等。
案例1:針對同一個圖案,使用不一樣的視口,能夠作到在屏幕窗口上的平鋪
例如a所示的就是平鋪
代碼以下: 將恐龍替換成矩形 代碼以下
setWindow(0,640.0,0,440.0) //設置窗口 for(int i=0,i<5;i++) //每列 { for(int j=0;j<5;j++) //每行 { glViewport(i*64,j*44,64,44) // 視口的移動方向先第0列 再第1列 ... drawPolylineFile("dino.dat");//在繪製一遍 } } 其中dino.dat爲折線的文件
//折線恐龍 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const int screenWidth=250; const int screenHeight=250; void setWindow(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top) //設置窗口 { gluOrtho2D(left,right,bottom,top);//窗口到視口的轉換 } void setViewport(GLint left,GLint right,GLint bottom,GLint top) //設置視口 { glViewport(left,bottom,right-left,top-bottom); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //背景顏色 白色 最亮 glColor3f(0.0f,0.0f,0.0f); //線條顏色爲黑色 glPointSize(10.0); //點像素大小 glMatrixMode(GL_PROJECTION); glLoadIdentity(); setWindow(0,60.0,0,60.0);//設置世界座標系中的窗口 } void draw(void) //畫圖函數 { glBegin(GL_LINE_LOOP); //三角形的點 glVertex2i(10,10); glVertex2i(10,50); glVertex2i(50,50); glEnd(); } void myDisplay(void) //註冊函數 { glClear(GL_COLOR_BUFFER_BIT); for(int i=0;i<5;i++) { for(int j=0;j<5;j++) { glViewport(i*50,j*50,50,50); //設置視口 draw(); } } glFlush(); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(screenWidth,screenHeight); glutInitWindowPosition(100,150); glutCreateWindow("折線矩形"); glutDisplayFunc(myDisplay); myInit(); glutMainLoop(); return 0; }
下圖爲執行效果圖:
b圖爲交替倒置 產生的效果
判斷i+j是偶數和奇數,而後倒轉top和bottom的值 以實現視口的倒轉
代碼以下:
for(int i=0;i<5;i++)//列 { for(int j=0;j<5;j++)//行 { if((i+j)%2==0)//i+j是偶數 setWindow(0.0,640.0,0.0,440.0);//正常的世界窗口 else //i+j爲奇數 setWindow(0.0,640.0,440.0,0.0);//顛倒的世界窗口 glViewport(i*64,j*44,64,44);//設置下一個視口 drawPloylineFile("dino.dat");//再次繪製 } }
完整代碼以下
//折線恐龍 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const int screenWidth=250; const int screenHeight=250; void setWindow(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top) //設置窗口 { glMatrixMode(GL_PROJECTION); //如下這兩句必定要從myInit函數中抽出放到此位置 glLoadIdentity(); gluOrtho2D(left,right,bottom,top); //窗口到視口的轉換 } void setViewport(GLint left,GLint right,GLint bottom,GLint top) //設置視口 { glViewport(left,bottom,right-left,top-bottom); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //背景顏色 白色 最亮 glColor3f(0.0f,0.0f,0.0f); //線條顏色爲黑色 glPointSize(10.0); //點像素大小 } void draw(void) //畫圖函數 { glBegin(GL_LINE_LOOP); glVertex2i(10,10); glVertex2i(10,50); glVertex2i(50,50); glEnd(); } void myDisplay(void) //註冊函數 { glClear(GL_COLOR_BUFFER_BIT); for(int i=0;i<5;i++) { for(int j=0;j<5;j++) { if((i+j)%2==0)//i+j是偶數 setWindow(0.0,60.0,0.0,60.0);//正常的世界窗口 else //i+j爲奇數 setWindow(0.0,60.0,60.0,0.0);//顛倒的世界窗口 glViewport(i*50,j*50,50,50); //設置視口 draw(); } } glFlush(); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(screenWidth,screenHeight); glutInitWindowPosition(100,150); glutCreateWindow("折線矩形"); glutDisplayFunc(myDisplay); myInit(); glutMainLoop(); return 0; }
執行結果的圖片
裁減圖片的某部分
視口不變,改變世界窗口 用以顯示不一樣的圖案
上例中w1 和w2兩個不一樣的窗口 視口不變,當使用w1時屏幕視口中顯示的是w1中所裁切出的圖案,而使用w2時則屏幕顯示的是w2中裁切出的圖案。
代碼框架以下:
setWindow(...); //每張圖片都改變窗口 setViewport(...); //每張圖片使用一樣的視口 hexSwirl(); //調用一樣的畫圖函數
座標系統繪製
//畫笛卡爾座標系 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const int screenWidth=1024; const int screenHeight=768; void setWindow(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(left,right,bottom,top); } void setViewport(GLint left,GLint right,GLint bottom,GLint top) { glViewport(left,bottom,right-left,top-bottom); } void drawpolygon(void) { setWindow(0,screenWidth,0,screenHeight); setViewport(0,screenWidth,0,screenHeight); glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POINTS); for(int x=0;x<=screenWidth;x++) glVertex2i(x,screenHeight/2); for(int y=0;y<=screenHeight;y++) glVertex2i(screenWidth/2,y); glEnd(); glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); glColor3f(0.0f,0.0f,0.0f); glPointSize(3.0); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(screenWidth,screenHeight); glutInitWindowPosition(100,150); glutCreateWindow("畫座標系"); glutDisplayFunc(drawpolygon); myInit(); glutMainLoop(); return 0; }
畫六邊形:
//畫笛卡爾座標系中的六邊形 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; typedef struct point{ GLdouble x; GLdouble y; }point; const int screenWidth=500; const int screenHeight=500; const int N=6; point sp[N]; point temp; //全局變量 double pi=3.1415926; double theta=2*pi/N; double r=150; void moveto(GLdouble x,GLdouble y) //設置當前的筆觸到座標點 { temp.x=x; temp.y=y; } void lineto(GLdouble x,GLdouble y)//從當前的點畫線到指定的座標點 兩點間的連線 { glBegin(GL_LINES); glVertex2d(temp.x,temp.y); glVertex2d(x,y); glEnd(); //glFlush(); temp.x=x; //從新設置當前虛擬筆觸的座標值 temp.y=y; } void setWindow(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top) //設置窗口 { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(left,right,bottom,top); } void setViewport(GLint left,GLint right,GLint bottom,GLint top) //設置視口 { glViewport(left,bottom,right-left,top-bottom); } int drawpolygon(struct point p[],GLdouble theta,GLdouble r)//注意傳遞的參數 接收的參數 { setWindow(0,screenWidth,0,screenHeight); setViewport(0,screenWidth,0,screenHeight); glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POINTS); for(int x=0;x<=screenWidth;x++) //畫座標系 glVertex2i(x,screenHeight/2); for(int y=0;y<=screenHeight;y++) glVertex2i(screenWidth/2,y); for(int i=0;i<N;i++) { double x=r*cos(i*theta)+screenWidth/2; //計算六個點的座標 並畫出 double y=r*sin(i*theta)+screenHeight/2; glVertex2i(x,y); p[i].x=x; p[i].y=y; } glEnd(); for(int i=0;i<N;i++) //畫線循環 { moveto(sp[i].x,sp[i].y); //利用全局變量 來畫六邊形 lineto(sp[(i+1)%6].x,sp[(i+1)%6].y);//取模算餘數 } glFlush(); return 0; } void mydisplay(void) //註冊的函數 { // drawpolygon(sp,theta,r); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); glColor3f(0.0f,0.0f,0.6f); glPointSize(6.0); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(screenWidth,screenHeight); glutInitWindowPosition(100,150); glutCreateWindow("畫六邊形"); glutDisplayFunc(mydisplay); myInit(); glutMainLoop(); return 0; }
結果以下:
多個六邊形
//多六邊形 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; typedef struct point{ GLdouble x; GLdouble y; }point; const int screenWidth=500; const int screenHeight=500; const int N=6; point sp[N]; point temp; //全局變量 double pi=3.1415926; double theta=2*pi/N; double r=240; void moveto(GLdouble x,GLdouble y) //設置當前的筆觸到座標點 { temp.x=x; temp.y=y; } void lineto(GLdouble x,GLdouble y)//從當前的點畫線到指定的座標點 兩點間的連線 { glBegin(GL_LINES); glVertex2d(temp.x,temp.y); glVertex2d(x,y); glEnd(); //glFlush(); temp.x=x; //從新設置當前虛擬筆觸的座標值 temp.y=y; } void setWindow(GLdouble left,GLdouble right,GLdouble bottom,GLdouble top) //設置窗口 { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(left,right,bottom,top); } void setViewport(GLint left,GLint right,GLint bottom,GLint top) //設置視口 { glViewport(left,bottom,right-left,top-bottom); } int drawpolygon(struct point p[],GLdouble tempt,GLdouble tempr)//注意傳遞的參數 接收的參數 { //setWindow(0,screenWidth,0,screenHeight); //setViewport(0,200,0,200); glBegin(GL_POINTS); //for(int x=0;x<=screenWidth;x++) //畫座標系 //glVertex2i(x,screenHeight/2); //for(int y=0;y<=screenHeight;y++) //glVertex2i(screenWidth/2,y); for(int i=0;i<N;i++) { double x=tempr*cos(i*tempt)+screenWidth/2; //計算六個點的座標 並畫出 double y=tempr*sin(i*tempt)+screenHeight/2; glVertex2i(x,y); p[i].x=x; p[i].y=y; } glEnd(); for(int i=0;i<N;i++) //畫線循環 { moveto(sp[i].x,sp[i].y); //利用全局變量 來畫六邊形 lineto(sp[(i+1)%6].x,sp[(i+1)%6].y);//取模算餘數 } // for(int j=0;j<N;j++) return 0; } void mydisplay(void) //註冊的函數 { glClear(GL_COLOR_BUFFER_BIT); setWindow(0,screenWidth,0,screenHeight); setViewport(0,screenWidth,0,screenHeight); while(r>0) //循環畫六邊形 drawpolygon(sp,theta,r-=10); glFlush(); cout<<sp[1].x<<endl; } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); glColor3f(0.0f,0.0f,0.6f); glPointSize(3.0); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowSize(screenWidth,screenHeight); glutInitWindowPosition(100,150); glutCreateWindow("畫六邊形"); glutDisplayFunc(mydisplay); myInit(); glutMainLoop(); return 0; }
結果:
縮放和漫遊
將窗口變小則用於放大對象 相反窗口變大 至關於對象縮小。
有時候漫遊也叫全視
在動畫中放大圖片
同心的多個窗口 固定的縱橫比 窗口逐漸縮小 造成動畫,每一個窗口中顯示的圖片都是一幀 此過程相似與放大圖像
繪製每一幀 都須要清一次屏幕 同時窗口愈來愈小 代碼以下:
動畫1:
float cx=0.3,cy=0.2;//窗口中心 float H,W=1.2, aspect=0.7;//窗口性質 set the viewport //設置視口 for(int frame=0;frame<NumFrames;frame++)//循環顯示每一幀圖像 { clear the screen //每次循環都清除以前的圖案 W*=0.7; H=W*aspect; //保持縱橫比 setWindow(cx-W,cx+W,cy-H,cy+H);//設置下一個窗口 hexSwirl(); }
動畫2:
動畫1的方式存在的問題
過程以下:
迅速擦除當前的圖像;而後新圖像被緩慢的重繪,由於僅僅有一個圖像的緩存
解決上面問題的方法就是在用戶瀏覽當前圖像時,在一個「屏幕外內存」的地方繪製新的圖像,而後將新圖像顯示在顯示器上。
opengl提供了雙緩存來完成任務,使用一個內存來模擬一個額外的屏幕,這個屏幕不顯示在真實的顯示器上,並全部的繪製都在這個緩存中進行。以後glutSwapBuufers()函數會將緩存中的圖片轉移到屏幕窗口上。
設置單緩存:glutInitDisplayModel(GLUT_SINGLE | GLUT_RGB);
設置成雙緩存:glutInitDisplayModel(GLUT_DOUBLE | GLUT_RGB);
練習:3.2.2迴旋的旋渦
3.3 裁減線
圖形學中的基本任務,用於保持對象在給定的區域外的部分不被繪製
opengl環境中,每一個對象都會被一個特殊的算法,自動的裁減到世界窗口 裁減器
3.3.1裁減一條線
cohen-sutherland 裁減器 計算p1和p2兩個端點的線段的哪一部分位於世界窗口以內,並返回位於世界窗口內的部分的端點
函數 int clipSegment(Point2& p1,Point2& p2,RealRect window) 參數爲兩個二維的點以及一個對齊的矩形
功能爲將p1和p2爲端點的線段裁減到窗口邊界處。若是線段的某一部分位於窗口內,則將新的端點存放在p1 和p2中,而且函數返回1,說明線段的某些部分是可見的。若是這條線溫泉被裁減掉了,函數返回0,說明沒有任何部分可見。
案例1:
典型的裁減器的狀況 : clipsegment()函數對每條線段執行下列事件中的一件。
1.若是整條線段都在窗口內(例如線段cd) 函數返回1
2.整條線段在窗口外 例如線段AB 函數返回0
3.一個端點在窗口外,一個在窗口內 例如ED 函數將一個端點裁減 設置新的端點 而後返回1
4.兩個端點都在窗口外, 可是此線段的一部分位於窗口內 例如線段AE 函數將裁減兩個端點 並返回1
還有可能線段位於窗口的左側 右側 上面或者下面等等。
爲了判斷線段的位置 而且作出相應的裁減 cohen-sutherland提供了一個快速的分治算法。
3.3.2 cohen-sutherland裁減算法
平凡接受和平凡拒絕兩種常見狀況
圖中的AB整個線段位於窗口內 所以AB被平凡的接受,而不須要裁減, 窗口很大時,大部分狀況都是平凡接受。
圖中CD整個線段 兩個端點都位於W的另外一條邊外,則線段CD整個都在窗口外 此時CD就是平凡的拒絕。 窗口很小時,大部分狀況都是平凡拒絕。
檢測平凡接受或者平凡拒絕
僅僅檢測端點便可
對線段的每個端點計算一個「窗口內部/外部編碼」,用於後續的測試。
四個編碼的位置信息從左到右分別爲 left above right below 分別表明 在左面 上面 下面 右面;
t表明true f表明false
若是點在窗口內部,則編碼表示爲FFFF;
下圖爲點與窗口的位置關係 的9種可能性
平凡接受:兩個碼字都是FFFF;
平凡拒絕:兩個碼字在某一位元素上都是T 兩個點都是在窗口左邊,或者都在它上面,等等
使用c/c++的位操做函數 能夠實現碼字的計算和測試。
沒有平凡接受和拒絕時的截斷
若是沒有平凡接受或者拒絕時,此線段必定會被某個邊界分紅兩個部分
do
{
設置p1 和p2的碼字;
if(平凡接受) return 1;
if(平凡拒絕) return 0;
將線段在下一個窗口邊界處截斷;(從新設置p1或p2端點的位置)丟掉外面的部分;
}while(1);
最多四次循環就會終止,針對每一個點 對齊矩形的四條邊都會進行檢測 因此須要四次循環。也就會出現平凡接受或者平凡拒絕的狀況。
已知:p1和p2的座標x和y w.right
未知:A點的x=w.right y=p1.y-d
d/dely=e/delx
delx=p2.x-p1.x;.........1
dely=p2.y-p1.y;........2
e=p1.x-w.right;.........3
由1 2 3能夠計算出d的值 由此就能夠計算出A點的y值
因此新p1.y (點A)
p1.y+=(w.right-p1.x)*dely/delx
剩餘的窗口的三個邊界的裁減方法相似。
對於水平的線 dely=0 ;對於垂直的線 delx=0; 分母爲0的狀況不會發生
僞代碼2:
int clipSegment(Point & p1,Point2& p2,RealRect W) { do{ if(平凡接受) return 1; if(平凡拒絕) return 0; if(p1在窗口外面) { if(p1在窗口左邊) 用左邊界截斷,更新p1; else if(p1在窗口右邊) 用右邊界截斷,更新p1; else if(p1在窗口下面) 用下邊界截斷,更新p1; else if(p1在窗口上面) 用上邊界截斷,更新p1; } else //p2在窗口外面 { if(p2在窗口左邊) 用左邊界截斷,更新p2; else if(p2在窗口右邊) 用右邊界截斷,更新p2; else if(p2在窗口下面) 用下邊界截斷,更新p2; else if(p2在窗口上面) 用上邊界截斷,更新p2; } }while(1); }
每次執行循環時,每一個端點的碼字都會被從新計算和測試,每次都會對平凡接受和拒絕進行檢測 ,若是失敗,算法會檢測p1是否在窗口外,若是p1在窗口內,那麼p2必定在窗口外,針對在窗口外的點進行按邊裁減。
裁減的順序爲左,右,下,上。
第一次:p1變爲A
第二次:p2變爲B
第三次:A變爲C
第四次: B變爲D
練習:3.3.1 手工模擬clipSegment (略)
3.3.2 除以0的狀況永遠不會發生。
3.4正多邊形、圓和圓弧
3.4.1正多邊形
全部邊相等 內角相等 且邊只能在端點處接觸,稱n條邊的正多邊形爲正n邊形。頂點等距離排放
半徑爲R的外接圓的圓心位於遠點,而第一個點p0位於x軸的正方向,其它的頂點位於下面等式指定的位置:pi=(R*cos(i*a),R*sin(i*a)), i=1,2...5
其中a=2π/6=π/3。
1.若是圓心的座標爲(x,y),只要在上式中座標中分別加上x和y便可。
2.若是將正六邊形縮放S倍,只須要將R乘上S。
3.旋轉只須要在cos和sin函數的參數中增長A便可
3.4.2正n邊形的變種
將正n邊形中的各個頂點均相連 會獲得花環
花環.cpp
//繪製sinc函數 #include <windows.h> #include <iostream> #include <math.h> #include <gl/GL.h> #include <gl/GLU.h> #include <gl/GLUT.h> class GLintPoint //點類 { public: GLint x,y; }; class Point2 //線段端點類 { public: float x,y; void set(float dx,float dy){x=dx;y=dy;} //內聯函數 且重載 void set(Point2 &p){x=p.x;y=p.y;} //內聯函數 重載 設置端點 Point2(float xx,float yy){x=xx,y=yy;} //構造函數 非默認 Point2(){x=y=0;} //默認構造函數 }; Point2 currPos; //定義兩個端點類的對象 Point2 CP; void moveTo(Point2 p) //移動當前的虛擬筆觸 到一個端點 { CP.set(p); //設置當前點的座標 } void lineTo(Point2 p) //從當前點的位置cp 到參數給定的點劃線 { glBegin(GL_LINES); glVertex2f(CP.x,CP.y); glVertex2f(p.x,p.y); glEnd(); glFlush(); CP.set(p); //***從新設置當前的點 cp } //myInit void myInit(void) //初始化 窗口的個參數 背景顏色 線條顏色 以及寬度等等 { glClear(GL_COLOR_BUFFER_BIT); glClearColor(1.0,1.0,1.0,0.0); glColor3f(0.0f,0.0f,1.0f); } void rosette(int N,float radius) //觸發的畫圖函數 參數爲點數 和 半徑值 { Point2 * pointlist=new Point2[N]; //建立數組用來存儲線段的端點 GLfloat theta=(2.0f*3.1415926536)/N; //360度除以點數 算出角度數 for(int c=0;c<N;c++) { pointlist[c].set(radius*sin(theta*c),radius*cos(theta*c));//計算每一個點的x和y值 而且填入對象數組 } for(int i=0;i<N;i++)//將數組中的全部點都鏈接起來 { for(int j=0;j<N;j++) { moveTo(pointlist[i]); lineTo(pointlist[j]); } } } void render()//渲染 { glClear(GL_COLOR_BUFFER_BIT);//清除背景顏色 glViewport(10,10,600,600); //設置視口 ***** x,y width height rosette(10,.50f); //畫圖 實參傳送 glFlush(); //傳送給opengl } //main int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glutInitWindowSize(600,600); glutCreateWindow("rosette"); glutDisplayFunc(render); myInit(); glutMainLoop(); return 0; }
特例: 5花環 的特性
體現不少黃金分割率φ
每條線段要比短於它的線段長φ倍,並且因爲五角星的邊又構成了一個內五邊形,能夠實現一個無窮的嵌套。
例子:3.4.2基於兩個同心多邊形的圖形
外半徑是R,內半徑是fR ,f是某個因子。每一個圖都使用了一個正n邊形的變種,其半徑在內徑和外徑之間交替。
圖3.29a 代碼示例:
//3.2.9圖a #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const int N=6; const double pi=3.1415926; const double theta=2*pi/N; const double R=200; const double r=50; const double screenWidth=500; const double screenHeight=500; class point { public: GLdouble x; GLdouble y; void setp(GLdouble _x,GLdouble _y){x=_x;y=_y;}; }; point sp; void moveTo(GLdouble x,GLdouble y) { sp.setp(x,y); } void lineTo(GLdouble x,GLdouble y) { glBegin(GL_LINES); glVertex2d(sp.x,sp.y); glVertex2d(x,y); glEnd(); sp.x=x; sp.y=y; } void photo(void) { class point p1[6]; class point p2[6]; glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POINTS); for(int i=0;i<N;i++) { GLdouble Rx=R*cos(i*theta)+screenWidth/2; GLdouble Ry=R*sin(i*theta)+screenHeight/2; p1[i].x=Rx; p1[i].y=Ry; GLdouble rx=r*cos(i*theta)+screenWidth/2; GLdouble ry=r*sin(i*theta)+screenHeight/2; p2[i].x=rx; p2[i].y=ry; glVertex2d(p1[i].x,p1[i].y); glVertex2d(p2[i].x,p2[i].y); } glEnd(); for(int i=0;i<N;i+=2) { moveTo(p1[i].x,p1[i].y); lineTo(p1[(i+2)%N].x,p1[(i+2)%N].y); } for(int i=0;i<N;i+=2) { moveTo(p1[i].x,p1[i].y); lineTo(p2[(i+1)%N].x,p2[(i+1)%N].y); moveTo(p2[i+1].x,p2[i+1].y); lineTo(p1[(i+2)%N].x,p1[(i+2)%N].y); } glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glColor3f(0.0f,0.2f,0.6f); //point color glPointSize(3.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("內外圓多邊形"); glutDisplayFunc(photo); myInit(); glutMainLoop(); return 0; }
結果
3.2.9圖B 僅將a圖代碼中的N改成10便可
//3.2.9圖b #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const int N=10; const double pi=3.1415926; const double theta=2*pi/N; const double R=200; const double r=50; const double screenWidth=500; const double screenHeight=500; class point { public: GLdouble x; GLdouble y; void setp(GLdouble _x,GLdouble _y){x=_x;y=_y;}; }; point sp; void moveTo(GLdouble x,GLdouble y) { sp.setp(x,y); } void lineTo(GLdouble x,GLdouble y) { glBegin(GL_LINES); glVertex2d(sp.x,sp.y); glVertex2d(x,y); glEnd(); sp.x=x; sp.y=y; } void photo(void) { class point p1[N]; class point p2[N]; glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POINTS); for(int i=0;i<N;i++) { GLdouble Rx=R*cos(i*theta)+screenWidth/2; GLdouble Ry=R*sin(i*theta)+screenHeight/2; p1[i].x=Rx; p1[i].y=Ry; GLdouble rx=r*cos(i*theta)+screenWidth/2; GLdouble ry=r*sin(i*theta)+screenHeight/2; p2[i].x=rx; p2[i].y=ry; glVertex2d(p1[i].x,p1[i].y); glVertex2d(p2[i].x,p2[i].y); } glEnd(); for(int i=0;i<N;i+=2) { moveTo(p1[i].x,p1[i].y); lineTo(p1[(i+2)%N].x,p1[(i+2)%N].y); } for(int i=0;i<N;i+=2) { moveTo(p1[i].x,p1[i].y); lineTo(p2[(i+1)%N].x,p2[(i+1)%N].y); moveTo(p2[i+1].x,p2[i+1].y); lineTo(p1[(i+2)%N].x,p1[(i+2)%N].y); } glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glColor3f(0.0f,0.2f,0.6f); //point color glPointSize(3.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("內外圓多邊形"); glutDisplayFunc(photo); myInit(); glutMainLoop(); return 0; }
代碼結果
3.29圖c 修改N 去掉外框線
//3.2.9圖c #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const int N=14; const double pi=3.1415926; const double theta=2*pi/N; const double R=200; const double r=50; const double screenWidth=500; const double screenHeight=500; class point { public: GLdouble x; GLdouble y; void setp(GLdouble _x,GLdouble _y){x=_x;y=_y;}; }; point sp; void moveTo(GLdouble x,GLdouble y) { sp.setp(x,y); } void lineTo(GLdouble x,GLdouble y) { glBegin(GL_LINES); glVertex2d(sp.x,sp.y); glVertex2d(x,y); glEnd(); sp.x=x; sp.y=y; } void photo(void) { class point p1[N]; class point p2[N]; glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POINTS); for(int i=0;i<N;i++) { GLdouble Rx=R*cos(i*theta)+screenWidth/2; GLdouble Ry=R*sin(i*theta)+screenHeight/2; p1[i].x=Rx; p1[i].y=Ry; GLdouble rx=r*cos(i*theta)+screenWidth/2; GLdouble ry=r*sin(i*theta)+screenHeight/2; p2[i].x=rx; p2[i].y=ry; glVertex2d(p1[i].x,p1[i].y); glVertex2d(p2[i].x,p2[i].y); } glEnd(); for(int i=0;i<N;i+=2) { moveTo(p1[i].x,p1[i].y); lineTo(p2[(i+1)%N].x,p2[(i+1)%N].y); moveTo(p2[i+1].x,p2[i+1].y); lineTo(p1[(i+2)%N].x,p1[(i+2)%N].y); } glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glColor3f(0.0f,0.2f,0.6f); //point color glPointSize(3.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("內外圓多邊形"); glutDisplayFunc(photo); myInit(); glutMainLoop(); return 0; }
結果圖
3.29圖d 多加上一個畫內圓的代碼段
//3.2.9圖c #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const int N=14; const double pi=3.1415926; const double theta=2*pi/N; const double R=200; const double r=50; const double screenWidth=500; const double screenHeight=500; class point { public: GLdouble x; GLdouble y; void setp(GLdouble _x,GLdouble _y){x=_x;y=_y;}; }; point sp; void moveTo(GLdouble x,GLdouble y) { sp.setp(x,y); } void lineTo(GLdouble x,GLdouble y) { glBegin(GL_LINES); glVertex2d(sp.x,sp.y); glVertex2d(x,y); glEnd(); sp.x=x; sp.y=y; } void photo(void) { class point p1[N]; class point p2[N]; glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POINTS); for(int i=0;i<N;i++) { GLdouble Rx=R*cos(i*theta)+screenWidth/2; GLdouble Ry=R*sin(i*theta)+screenHeight/2; p1[i].x=Rx; p1[i].y=Ry; GLdouble rx=r*cos(i*theta)+screenWidth/2; GLdouble ry=r*sin(i*theta)+screenHeight/2; p2[i].x=rx; p2[i].y=ry; glVertex2d(p1[i].x,p1[i].y); glVertex2d(p2[i].x,p2[i].y); } glEnd(); for(int i=0;i<N;i+=2)//畫外框 { moveTo(p1[i].x,p1[i].y); lineTo(p1[(i+2)%N].x,p1[(i+2)%N].y); } for(int i=0;i<N;i+=2)//畫內角 { moveTo(p1[i].x,p1[i].y); lineTo(p2[(i+1)%N].x,p2[(i+1)%N].y); moveTo(p2[i+1].x,p2[i+1].y); lineTo(p1[(i+2)%N].x,p1[(i+2)%N].y); } //畫內圓 glBegin(GL_POINTS); for(double i=0.0;i<=2*pi;i+=0.05) glVertex2d(r*cos(i)+screenWidth/2,r*sin(i)+screenHeight/2); glEnd(); glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glColor3f(0.0f,0.2f,0.6f); //point color glPointSize(3.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("內外圓多邊形"); glutDisplayFunc(photo); myInit(); glutMainLoop(); return 0; }
練習3.4.1
3.4.2證實
圖片3.30 某大學的標誌
給定一點
//3.4.3 校標 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const double screenWidth=500; const double screenHeight=500; int j=0;//聲明一個全局變量 class point { public: GLdouble x; GLdouble y; void setp(GLdouble _x,GLdouble _y){x=_x;y=_y;}; }; void photo(void) { point p[6]; //定義對象數組 而且初始化 p[0].x=0; p[0].y=20; p[1].x=-100; p[1].y=80; p[2].x=-100; p[2].y=200; p[3].x=0; p[3].y=140; p[4].x=100; p[4].y=200; p[5].x=100; p[5].y=80; int N=3; double pi=3.1415926; double theta=2*pi/N; //開始畫圖 glClear(GL_COLOR_BUFFER_BIT); for(int j=0;j<3;j++) //j定義爲全局變量 不然不能經過編譯 { glBegin(GL_LINE_LOOP); for(int i=0;i<6;i++)
glVertex2d((cos(theta*j)*p[i].x-sin(theta*j)*p[i].y)+screenWidth/2,(sin(theta*j)*p[i].x+cos(theta*j)*p[i].y)+screenHeight/2); glEnd(); } glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glColor3f(0.0f,0.2f,0.6f); //point color glPointSize(3.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("校標"); glutDisplayFunc(photo); myInit(); glutMainLoop(); return 0; }
3.4.3 繪製圓與圓弧
圓心:c
半徑:R
開始角度:a
展開角度:b
b爲負值 則順時針畫圖
b爲正值 則逆時針畫圖
展開的角度是360度
drawCirlce() 經過指定圓心和半徑來繪製,還有其餘的方法來描述圓,常見的方式是:
1.已知圓心以及一個院上的點。 利用圓心以及一個圓上的點 計算兩點的距離即半徑
2.給定圓必須通過的三個點。 三個不共線的點能夠惟一肯定一個圓
畫圓案例2
//3.4.3 圓外切 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const double screenWidth=500; const double screenHeight=500; class point { public: GLdouble x; GLdouble y; void setp(GLdouble _x,GLdouble _y){x=_x;y=_y;}; }; void photo(void) { double r=100; double x; int w=150; int h=120; glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POINTS); for(x=-100;x<100;x+=0.05) { glVertex2d(x+w,sqrt(r*r-x*x)+h); glVertex2d(x+w,-sqrt(r*r-x*x)+h); } glVertex2d(w,h); glEnd(); glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glColor3f(0.0f,0.2f,0.6f); //point color glPointSize(3.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("畫弧和圓"); glutDisplayFunc(photo); myInit(); glutMainLoop(); return 0; }
畫圓案例3
//3.4.3 圓外切 給定一個兩個圓心 和兩個圓的交點 畫兩個圓和一條切線 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const double screenWidth=500; const double screenHeight=500; int w=80;//位移 int h=80; class point { public: GLdouble x; GLdouble y; void setp(GLdouble _x,GLdouble _y){x=_x;y=_y;}; }; double getr(point p1,point p2)//獲取半徑的平方 { double r=sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y)); return r; } void line(point p1,point p2)//p1爲圓心,p2爲圓上點 直線過p2點 { double k,b; k=-(p2.x-p1.x)/(p2.y-p1.y); b=p2.y-k*p2.x; glBegin(GL_POINTS); for(double x=10;x<200;x++) glVertex2d(x+w,k*x+b+h); glEnd(); } void photo(point p1,point p2) //根據兩個點畫圓 p1爲圓心 p2爲圓上的點 { double r=getr(p1,p2); double x; glBegin(GL_POINTS); for(x=p1.x-r;x<=p1.x+r;x+=0.05) { glVertex2d(x+w,sqrt(r*r-(x-p1.x)*(x-p1.x))+p1.y+h); glVertex2d(x+w,-sqrt(r*r-(x-p1.x)*(x-p1.x))+p1.y+h); } glVertex2d(p1.x+w,p1.y+h); glEnd(); } void mydisplay(void) { class point p1;//初始化 三個對象 p1.x=50; p1.y=50; point p2; p2.x=120; p2.y=120; point p3; p3.x=180; p3.y=160; glClear(GL_COLOR_BUFFER_BIT); photo(p1,p2); photo(p3,p2); line(p1,p2); glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glColor3f(0.0f,0.2f,0.6f); //point color glPointSize(3.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("畫弧和圓"); glutDisplayFunc(mydisplay); myInit(); glutMainLoop(); return 0; }
執行結果:
3.4.4曲線的逐次細化
逐次細化一個簡單的曲線,能夠遞歸造成很是複雜的曲線。 例如koch曲線 逐漸逼近真實的曲線
將kn的每一條線段平均分爲3的段,而後將中間的那段用一個等邊三角形來提升代替,採用遞歸的方式,每一線段的長度變爲原來的4/3,因此總的曲線長度也是它上一代的4/3.所以kn的總長度爲(4/3)的n次冪。
3.5曲線的參數形式
描述曲線的形狀主要有兩種方法:
隱式法和參數形式
隱式法:使用函數f(x,y)來描述曲線 而且提供xy之間的關係
點在曲線上的條件:f(x,y)=0
一類曲線 曲線的內部和外部是有意義的,此種狀況下 f(x,y)也被叫作內部-外部函數 其意義是:
f(x,y)=0 對應全部在曲線上的點 (x,y)
f(x,y)>0 對全部在曲線外的點(x,y)
f(x,y)<0 對全部在曲線內的點(x,y)
曲線對x是單值 那麼 g(x)對於每個x 僅有一個g(x) 能夠寫成f(x,y)=y-g(x)
曲線對y是單值, 存在函數h() 使得曲線上的全部點知足x=h(y)
還有對於x和y都不是單值的:f(x,y)=0 不能寫成y=g(x) 或者 x=h(y)的形式 能夠表示成:
y=+-根號下(r^2-x^2) 這裏爲兩個函數 一個是正號,另外一個是負號
3.5.1曲線的參數形式
當參數取不一樣值的時候,會產生出曲線上不一樣的點。推薦使用 以時間t的運動軌跡
粒子沿着曲線運動的路徑由兩個函數x()和y()來肯定,若是是三維 則有三個函數 x() y() z() 他們肯定粒子在時刻t的位置。
參數t一般被看做是時間,而曲線自己就是粒子隨着時間在某一個區間內的變化所通過的點。
一般證實隱式形式和參數形式相同 將參數形式帶入隱式形式便可 即從參數形式求隱式形式
3.5.2 繪製參數形式
根據p(t)=(x(t),y(t)) 其中t從0變化到T 咱們只須要用很是緊湊的間隔採集p(t)的樣本 而後鏈接成折線 近似的逼近曲線
時間數組 t[i] i=0 1 2 3...
glBegin(GL_LINES); for(int i=0;i<n;i++) glVertex2f(x(t[i]),y(t[i])); glEnd();
案例繪製橢圓
glBegin(GL_LINES); for(double t=0;t<2*pi;t+=2*pi/n)//將2pi n等分 越細分曲線越光滑 glVertex2f(W*cos(t),H*sin(t)); glEnd();
3.5.4 樣例曲線代碼
//3.4.3 圓外切 給定一個兩個圓心 和兩個圓的交點 畫兩個圓和一條切線 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const double screenWidth=500; const double screenHeight=500; int w=180;//位移 int h=80; const double pi=3.141592; const int n=360; void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_LINE_LOOP); for(double t=0.0;t<2*pi;t+=2*pi/n) glVertex2d(w*cos(t)+screenWidth/2,h*sin(t)+screenHeight/2); glEnd(); glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glColor3f(0.0f,0.2f,0.6f); //point color glPointSize(3.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("畫橢圓"); glutDisplayFunc(mydisplay); myInit(); glutMainLoop(); return 0; }
執行結果
3.5.5 畫圖
//3.4.3 圓外切 給定一個兩個圓心 和兩個圓的交點 畫兩個圓和一條切線 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const double screenWidth=500; const double screenHeight=500; int w=180;//位移 int h=140; const float f=h/w; double d=0.5; double t=d*f; const double pi=3.141592; const int n=360; const float color1[3]={1.0,1.0,1.0}; const float color2[3]={0.0,0.0,0.0}; void drawEllipse(int w,int h,const float * clr)//畫圓並填充 { if(w!=h) { glBegin(GL_POINTS); for(;w>0&&h>0;w-=d,h-=t) { glColor3f(clr[0],clr[1],clr[2]); for(double t=0.0;t<2*pi;t+=2*pi/n) glVertex2d(w*cos(t)+screenWidth/2,h*sin(t)+screenHeight/2); } glEnd(); } if(w==h) { glBegin(GL_POINTS); for(;w>0&&h>0;w-=d) { glColor3f(clr[0],clr[1],clr[2]); for(double t=0.0;t<2*pi;t+=2*pi/n) glVertex2d(w*cos(t)+screenWidth/2,w*sin(t)+screenHeight/2); } glEnd(); } } void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT); drawEllipse(w,h,color2); drawEllipse(h-5,h-5,color1); drawEllipse(h-10,h-50,color2); drawEllipse(h-55,h-55,color1); glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glPointSize(3.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("畫橢圓"); myInit(); glutDisplayFunc(mydisplay); glutMainLoop(); return 0; }
執行結果
3.5.3極座標形式
用來表示和繪製一些有趣曲線
極座標畫圓
//3.4.3 圓外切 給定一個兩個圓心 和兩個圓的交點 畫兩個圓和一條切線 #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include <iostream> #include <math.h> using namespace std; const double screenWidth=500; const double screenHeight=500; double pi=3.1415926; void drawEllipse(double k)//極座標畫圓 { double theta=0; glBegin(GL_POINTS); for(;theta<=2*pi;theta+=0.01) { glVertex2d(k*cos(theta)+screenWidth/2,k*sin(theta)+screenHeight/2); } glEnd(); } void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT); drawEllipse(200); glFlush(); } void myInit(void) { glClearColor(1.0,1.0,1.0,0.0); //background color glPointSize(3.0); glColor3f(1.0,0.0,0.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0,500,0.0,500); } int main(int argc,char ** argv) { glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB); glutInitWindowPosition(0,0); glutInitWindowSize(500,500); glutCreateWindow("極座標畫圓"); glutDisplayFunc(mydisplay); myInit(); glutMainLoop(); return 0; }
執行結果
極座標畫心形
替換上面的函數代碼段
void drawEllipse(double k)//極座標畫圓 { double theta=0; glBegin(GL_POINTS); for(;theta<=2*pi;theta+=0.01) { double f=k*(1+cos(theta)); glVertex2d(f*cos(theta)+screenWidth/3,f*sin(theta)+screenHeight/2); } glEnd(); }
執行結果
玫瑰曲線 圖3.45
代碼段替換
void drawEllipse(double k,int n)//極座標畫圓 n爲花瓣 { double theta=0; glBegin(GL_LINES); for(;theta<=2*pi;theta+=0.00001) { double f=k*cos(n*theta); glVertex2d(f*cos(theta)+screenWidth/2,f*sin(theta)+screenHeight/2); glVertex2d(f*cos(theta+0.00001)+screenWidth/2,f*sin(theta+0.01)+screenHeight/2); } glEnd(); } void mydisplay(void) { glClear(GL_COLOR_BUFFER_BIT); drawEllipse(200,8); glFlush(); }
執行結果
阿基米德螺旋線
替換代碼段
void drawEllipse(double k,int n)//極座標畫圓 n爲圈數 { double theta=0; glBegin(GL_POINTS); for(;theta<=n*pi;theta+=0.01) { double f=k*theta; glVertex2d(f*cos(theta)+screenWidth/2,f*sin(theta)+screenHeight/2); } glEnd(); }
以上各種函數以下:
圓錐曲線代碼
void drawEllipse(double e)//極座標畫圓 n爲圈數 { double theta=0; glBegin(GL_POINTS); for(;theta<=2*pi;theta+=0.0001) { double f1=1/(1-e*cos(theta)); //double f2=1/(1+e*cos(theta)); glVertex2d(f1*cos(theta)+screenWidth/4,f1*sin(theta)+screenHeight/2); //glVertex2d(f2*cos(theta)+screenWidth/2,f2*sin(theta)+screenHeight/2); } glEnd(); }
對數螺旋線
void drawEllipse(double e)//極座標畫圓 n爲圈數 調用時 e的值須要大於1 { double theta=0; double k=0.8; double a=0.6; glBegin(GL_POINTS); for(;theta<=16*pi;theta+=0.01) { double f1=k*pow(e,a*theta);//e的a*theta次冪 //double f2=1/(1+e*cos(theta)); glVertex2d(f1*cos(theta)+screenWidth/2,f1*sin(theta)+screenHeight/2); //glVertex2d(f2*cos(theta)+screenWidth/2,f2*sin(theta)+screenHeight/2); } glEnd(); }
雙曲線
void drawEllipse(double e,double p)//極座標畫圓 n爲圈數 e大於1 { double theta=0; glBegin(GL_POINTS); for(;theta<=2*pi;theta+=0.001) { double f1=e*p/(1-e*cos(theta));//圓錐曲線通用方程 p爲焦點到準線的距離 f1表示數學中極座標的徑向距離 glVertex2d(f1*cos(theta)+screenWidth/2,f1*sin(theta)+screenHeight/2); } glEnd(); glBegin(GL_LINES); //x軸 glVertex2d(0,250); glVertex2d(500,250); glEnd(); glBegin(GL_LINES); //y軸 glVertex2d(250,0); glVertex2d(250,500); glEnd(); }
由上例可知 雙曲線的右焦點爲極座標系的極點 用到的公式以下
同理可得橢圓的極座標畫圖代碼及圖像
void drawEllipse(double e,double p)//極座標畫圓 n爲圈數 e小於1 { double theta=0; glBegin(GL_POINTS); for(;theta<=2*pi;theta+=0.001) { double f1=e*p/(1-e*cos(theta));//圓錐曲線通用方程 p爲焦點到準線的距離 f1表示數學中極座標的徑向距離 glVertex2d(f1*cos(theta)+screenWidth/2,f1*sin(theta)+screenHeight/2); } glEnd(); glBegin(GL_LINES); //x軸 glVertex2d(0,250); glVertex2d(500,250); glEnd(); glBegin(GL_LINES); //y軸 glVertex2d(250,0); glVertex2d(250,500); glEnd(); }
能夠看出 橢圓的左焦點爲 極座標的極點
同理上面代碼中e=1時 是拋物線 開口向右 極點爲焦點