本節是OpenGL學習的第三個課時,下面介紹如何運用顯示窗體的視口和裁剪區域:
(1)知識點引入:
1)問題現象:
當在窗體中繪制圖形后,拉伸窗體圖形形狀會發生變化:
#include <GL/glut.h> #include <math.h> const float Pi = 3.1415926f; const int n = 1000; const float R = 0.8f; void init(void) { glClearColor(0.0,0.0,0.0,0.0);//設背景色為黑色 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-1.0f,1.0f,-1.0f,1.0f); } void paintCircle() { int i; glColor3f(1.0, 1.0, 1.0);//紅色的圓 glBegin(GL_POLYGON); for (i = 0; i<n; ++i) glVertex2f(R*cos(2 * Pi / n*i) , R*sin(2 * Pi / n*i)); glEnd(); glFlush(); } int main(int argv,char **argc) { glutInit(&argv,argc); glutInitDisplayMode(GLUT_RGB|GLUT_SINGLE); glutInitWindowSize(300,300); glutInitWindowPosition(800,300); init(); glutCreateWindow("檢測形狀變化"); glutDisplayFunc(paintCircle); glutMainLoop(); }


2)問題產生的原因:
沒有正確設置投影矩陣。默認的是透視投影矩陣且高寬比為1。因此高寬比改變了,投影就會變形。因此只要高寬比改變了,投影就應該重新計算。
3)解決辦法:
每當窗口的大小改變時,視口和裁剪區域必須重新定義,以適應新的窗口大小。只有這樣,才能夠使窗口中顯示的圖像保持原來的形狀,而不發生扭曲:
#include <GL/glut.h> #include <math.h> const float Pi = 3.1415926f; const int n = 1000; const float R = 30.0f; void RenderScene() { glClear(GL_COLOR_BUFFER_BIT); // 把當前繪圖顏色設置為紅色 glColor3f(1.0f, 0.0f, 0.0f); // OpenGL命令,用當前的繪圖顏色繪制一個填充矩形 glRectf(-50.0f, 50.0f, 50.0f, -50.0f); // 刷新繪圖命令,此時所有未執行的OpenGL命令被執行 glFlush(); int i; glColor3f(0.0, 1.0, 0.0);//綠色的圓 glBegin(GL_POLYGON); for (i = 0; i<n; ++i) glVertex2f(R*cos(2 * Pi / n*i), R*sin(2 * Pi / n*i)); glEnd(); glFlush(); } // 設置渲染狀態 void SetupRC() { // 設置用於清除窗口的顏色 glClearColor(0.0f, 0.0f, 1.0f, 1.0f); } // 當窗口大小改變時由GLUT函數庫調用 void ChangeSize(GLsizei w, GLsizei h) { // GLfloat aspectRatio; // 防止被0所除 if (0 == h){ h = 1; } // 設置視口為窗口的大小 glViewport(0, 0, w, h); // 選擇投影矩陣,並重置坐標系統 glMatrixMode(GL_PROJECTION); glLoadIdentity(); // 計算窗口的縱橫比(像素比) aspectRatio = (GLfloat)w / (GLfloat)h; // 定義裁剪區域(根據窗口的縱橫比,並使用正投影) if (w <= h) { glOrtho(-100.0, 100.0, -100 / aspectRatio, 100 / aspectRatio, 1.0, -1.0); } else { glOrtho(-100.0 * aspectRatio, 100.0 *aspectRatio, -100.0, 100.0, 1.0, -1.0); } // 選擇模型視圖矩陣,並重置坐標系統 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA); glutInitWindowSize(300,300); glutInitWindowPosition(400,300); glutCreateWindow("SubstainSize"); glutDisplayFunc(RenderScene); // 設置當窗口的大小發生變化時的回調函數 glutReshapeFunc(ChangeSize); // 設置渲染狀態 SetupRC(); // 啟動GLUT框架的運行,一經調用便不再返回,直到程序終止 glutMainLoop(); return 0; }


(2)代碼解釋:
1)void glutReshapeFunc( void(*func) (int width,int height) )
GLUT定義了當窗口大小改變時glutReshapeFunc()函數應該被調用。此外,這個函數還會在窗口初次被創建時調用,保證初始化窗口不是正方形的時候渲染也不會變形出錯。
2)自定義函數ChangeSize(GLsizei w, GLsizei h)
API中規定此函數要做的工作有:
1.計算高寬比(wight/height)。(注意為了計算正確,我們必須保證高度不為0。)
2.用函數glViewport把視口設置為整個窗口。
3.設置當前矩陣為投影矩陣,這個矩陣定義了一個可視空間(viewing volume),再調用一個單位矩陣來初始化投影矩陣。
4.根據窗口的縱橫比定義裁剪區域,並使用正投影。
5.選擇模型視圖矩陣,並重置坐標系統。
另外此函數還可以用gluPerspective配合gluLookAt()來編寫,同樣能達到目的(http://blog.csdn.net/nauty_li/article/details/2227143)。
3)SetupRC()設置渲染狀態:
在渲染新的圖形時,需要做一些准備工作。在本例中做的工作就是重設背景色,防止上一張的圖像對即將繪制的產生影響。
4)void glViewport( GLint x, GLint y, GLsizei width, GLsizei height);
x, y Specify the lower left corner of the viewport rectangle, in pixels. The initial value is (0,0).
width, height Specify the width and height of the viewport. When a GL context is first attached to a window, width and height are set to the dimensions of that window.
glViewport specifies the affine transformation of xx and yy from normalized device coordinates to window coordinates.
調用glViewPort函數來決定視見區域,告訴OpenGL應把渲染之后的圖形繪制在窗體的哪個部位。當視見區域是整個窗體時,OpenGL將把渲染結果繪制到整個窗口。
glViewport()函數可以實現拆分窗口的功能。
(3)相關知識:
1)定義視口(窗口內部的渲染區域)
void glViewport(GLint x, GLint y, GLsizei width, GLsizeiheight);
其中,x,y參數指定了窗口內部視口的左下角位置,width和height參數指定了視口的大小(以屏幕像素為單位)。
2)定義裁剪區域
對裁剪區域進行重新定義,使縱橫比保持不變,窗口仍然維持在原來的形狀。也就是根據新窗口大小的縱橫比(像素之比,使用屏幕坐標系統),重新定義裁剪區域的縱橫比(邏輯單位之比,使用笛卡爾坐標系統),使裁剪區域與視口的縱橫比保持一致,這就是保持圖像形狀不變的關鍵所在。
我們在裁剪區域中使用了正投影:
void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far);
注:縱橫比指的是垂直方向上一個單位長度內的像素數量與水平方向上一個單位長度內的像素數量之比。
3)GL_PROJECTION和GL_MODELVIEW和GL_TEXTURE(矩陣變換http://blog.sina.com.cn/s/blog_537cc4d9010172o9.html)
glMatrixMode就是對接下來要做什么進行一下聲明,這幾個都是它的參數。
GL_PROJECTION:投影的意思,就是要對投影相關進行操作。也就是把物體投影到一個平面上,就像我們照相一樣,把3維物體投到2維的平面上。這樣,接下來的語句可以是跟透視相關的函數,比如glFrustum()或gluPerspective()。
GL_MODELVIEW:這個是對模型視景的操作,接下來的語句描繪一個以模型為基礎的適應,這樣來設置參數,接下來用到的就是像gluLookAt()這樣的函數。
GL_TEXTURE:就是對紋理相關進行操作。
(4)運用模型視景和裁剪區域的實例:
#include <GL/glut.h> //不顯示控制台窗口 #pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"") void paint() { //glViewport的四個參數,前兩個代表模型視景的起點坐標,后兩個代表視景的寬度和高度 glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 1.0, 0.0); //畫分割線,分成四個視見區 glViewport(0, 0, 400, 400); //多組雙頂點線段,點成對出現 glBegin(GL_LINES); glVertex2f(-1.0, 0); glVertex2f(1.0, 0); glVertex2f(0.0, -1.0); glVertex2f(0.0, 1.0); glEnd(); //定義在左下角 glColor3f(0.0, 1.0, 0.0); glViewport(0, 0, 200, 200); glBegin(GL_POLYGON); glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glVertex2f(0.5, -0.5); glEnd(); //定義在右上角 glColor3f(0.0, 0.0, 1.0); glViewport(200, 200, 200, 200); glBegin(GL_POLYGON); glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glVertex2f(0.5, -0.5); glEnd(); //定義在左上角 glColor3f(1.0, 0.0, 0.0); glViewport(0, 200, 200, 200); glBegin(GL_POLYGON); glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glVertex2f(0.5, -0.5); glEnd(); //定義在右下角 glColor3f(1.0, 1.0, 0.0); glViewport(200, 0, 200, 200); glBegin(GL_POLYGON); glVertex2f(-0.5, -0.5); glVertex2f(-0.5, 0.5); glVertex2f(0.5, 0.5); glVertex2f(0.5, -0.5); glEnd(); glFlush(); } void init() { glClear(GL_COLOR_BUFFER_BIT); //灰色作為填充背景 glClearColor(0.5, 0.5, 0.5, 0.5); glMatrixMode(GL_PROJECTION); glLoadIdentity(); //定義裁剪區域 gluOrtho2D(-1.0, 1.0, -1.0, 1.0); } int main(int argc, char ** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowPosition(400, 200); glutInitWindowSize(400, 400); glutCreateWindow("視口和裁剪區域應用"); init();//和下面一行的順序可以顛倒,還可以省略,省略后采用默認設置,背景為黑色 glutDisplayFunc(paint); glutMainLoop(); }

