glViewport()函數和glOrtho()函數的用法

來源:互聯網
上載者:User

 

glViewport()函數和glOrtho()函數的理解

在OpenGL中有兩個比較重要的投影變換函數,glViewport和glOrtho.
glOrtho是建立一個正交平行的視景體。 一般用於物體不會因為離螢幕的遠近而產生大小的變換的情況。比如,常用的工程中的製圖等。需要比較精確的顯示。 而作為它的對立情況, glFrustum則產生一個透視投影。這是一種類比真是生活中,人們視野觀測物體的真實情況。例如:觀察兩條平行的火車到,在過了很遠之後,這兩條鐵軌是會相交於一處的。還有,離眼睛近的物體看起來大一些,遠的物體看起來小一些。
glOrtho(left, right, bottom, top, near, far), left表示視景體左面的座標,right表示右面的座標,bottom表示下面的,top表示上面的。這個函數簡單理解起來,就是一個物體擺在那裡,你怎麼去截取他。這裡,我們先拋開glViewport函數不看。先單獨理解glOrtho的功能。 假設有一個球體,半徑為1,圓心在(0, 0, 0),那麼,我們設定glOrtho(-1.5, 1.5, -1.5, 1.5, -10, 10);就表示用一個寬高都是3的框框把這個球體整個都裝了進來。 
如果設定glOrtho(0.0, 1.5, -1.5, 1.5, -10, 10);就表示用一個寬是1.5, 高是3的框框把整個球體的右面裝進來;如果設定glOrtho(0.0, 1.5, 0.0, 1.5, -10, 10);就表示用一個寬和高都是1.5的框框把球體的右上方裝了進來。上述三種情況可以見圖:

從上述三種情況,我們可以大致瞭解glOrtho函數的用法。

---glViewport():
glOrtho函數只是負責使用什麼樣的視景體來截取映像,並不負責使用某種規則把映像呈現在螢幕上。
glViewport主要完成這樣的功能。它負責把視景體截取的映像按照怎樣的高和寬顯示到螢幕上。
比如:如果我們使用glut庫建立一個表單:glutInitWindowSize(500, 500); 然後使用glutReshapeFunc(reshape); reshape代碼如下:

void reshape(int width, int height)
{
    glViewport(0, 0, (GLsizei)width, (GLsizei)height);
    glMatrixModel(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.5, 1.5, -1.5, 1.5, -10, 10);

    ....
}
這樣是可以看到一個正常的球體的。但是,如果我們建立表單時glutInitWindowSize(800, 500),那麼看到的映像就是變形的。上述情況見圖。

 
因為我們是用一個正方形截面的視景體截取的映像,但是展開到螢幕上顯示的時候,就變成了glViewport(0, 0, 800, 500);也就是顯示屏變寬了, 倒是顯示的時候把一個正方形的映像“活生生的給拉寬了”。就會產生變形。這樣,就需要我們調整我們的OpenGL顯示屏了。我們可以不用800那麼寬,因為我們是用的正方形的視景體,所以雖然表單是800寬,但是我們只用其中的500就夠了。修改一下程式。
void reshape(int width, int height)
{
    int dis = width < height ? width : height;
    glViewport(0, 0, dis, dis);   /*這裡dis應該是500*/

    glMatrixModel(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.5, 1.5, -1.5, 1.5, -10, 10);
    .....
}

 

OK. 如果你能看明白我寫的內容。你可能對glViewport函數有個大致的瞭解。

不過,我們採用上面的辦法,就是只使用了原來螢幕的一部分(寬度從501到800 我們沒有用來顯示映像)。如果我們想用整個OpenGL螢幕顯示映像,但是又不使映像變形怎麼辦?
那就只能修改glOrtho函數了。也就是說,我們使用一個和表單一樣比例的視景體(而不再是正方形的視景體)來截取映像。例如,對於(800, 500)的表單,我們使用glOrtho(-1.5 * 800/500, 1.5 * 800/500, -1.5, 1.5, -10, 10),就是截取的時候,我們就使用一個“扁扁”的視景體截取,那麼,顯示的到OpenGL螢幕時(800, 500),我們只要正常把這個扁扁的截取映像顯示(扁扁的截取映像是指整個截取的映像,包括球形四周的黑色部分。 球形還是正常圓形的),就可以了。如:
void reshape(int width , int height)
{
    glViewport(width, height); //按照表單大小製作OpenGL螢幕
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (width <= height)
        glOrtho(-1.5, 1.5, -1.5 * (GLfloat)height/(GLfloat)width, 1.5 * (GLfloat)height/(GLfloat)width, -10.0, 10.0);
    else
        glOrtho(-1.5*(GLfloat)width/(GLfloat)height, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);

    ....
}

 

另外,關於glViewport()函數,我們還可以用來調整映像的解析度。例如,保持目前的表單大小不變,我們如果用這個size來只顯示整個物體的一部分,那麼映像的解析度就必然會增大。例如:
void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(0, 1.5, 0, 1.5 * (GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
        glOrtho(0, 1.5*(GLfloat)w/(GLfloat)h, 0, 1.5, -10.0, 10.0);
}
可以把解析度擴大4倍。

 

而如果再修改一下glViewport(0, 0, 2 * (GLsizei)w, 2 * (GLsizei)h); 則可以把解析度擴大16倍。

 

完整的測試程式:

/*Build on ubuntu 9.04*/

#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glut.h>

void init(void)
{
    GLfloat mat_specular[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat mat_shininess[] = {50.0};
    GLfloat light_position[] = {1.0, 1.0f, 1.0, 0.0};
    GLfloat white_light[] = {1.0, 1.0, 1.0, 1.0};
    GLfloat lmodel_ambient[] = {0.1, 0.1, 0.1, 1.0};
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glShadeModel(GL_SMOOTH);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, white_light);
    glLightfv(GL_LIGHT0, GL_SPECULAR, white_light);
    glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);

    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glEnable(GL_DEPTH_TEST);
   
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glutSolidSphere(1.0, 20, 16);
    glFlush();
}

void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    if (w <= h)
        glOrtho(-1.5, 1.5, -1.5 * (GLfloat)h/(GLfloat)w, 1.5 * (GLfloat)h/(GLfloat)w, -10.0, 10.0);
    else
        glOrtho(-1.5*(GLfloat)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

int main(int argc, char **argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}

/*CMakeLists.txt*/

PROJECT(s5)
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
ADD_EXECUTABLE(s5 main.cpp)

FIND_PACKAGE(OpenGL)
FIND_PACKAGE(GLUT)

IF(OPENGL_FOUND)
  INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR})
  TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${OPENGL_LIBRARIES})
ELSE(OPENGL_FOUND)
  MESSAGE(FATAL_ERROR "OpenGL not found")
ENDIF(OPENGL_FOUND)

IF(GLUT_FOUND)
  INCLUDE_DIRECTORIES(${GLUT_INCLUDE_DIR})
  TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${GLUT_LIBRARIES})
ELSE(GLUT_FOUND)
ENDIF(GLUT_FOUND)

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.