首先給出繪制效果圖:

說明:
本系統繪制了一個真實感的三維場景,並實現場景漫游。使用按鍵↑、↓、←、→或W、S、A、D控制運動方向,PgDn和PgUp可以改變觀察者的高度,鼠標控制轉向,按鍵‘F’可以打開和關閉“霧氣”,Esc退出程序。
本場景中繪制了牆壁與地面、天空、石柱、箱子、玻璃球、雪人、霧等對象,下面將依次分析它們的設計思路,這里將使用相同繪制技術的對象放到一起說明。
1、 牆壁、地面、箱子
它們的基本操作對四邊形的紋理映射,將一幅紋理圖的四個頂點坐標分別映射到四邊形的四個頂點上即可。OpenGL就自動通過插值填充多邊形內部的紋理。由於在OpenGL中紋理坐標是一個點的屬性,故需要在繪制點之前指定該點的紋理坐標。另外在繪制四邊形時要保證繪制順序的一致,這里統一采用逆時針順序繪制。紋理坐標范圍在[0,1]之間,坐標超過1則采用重復的方式(OpenGL默認處理方式)。這樣可以在映射時按四邊形的比例設置紋理坐標,從而防止紋理圖過度拉伸造成的變形。一個四邊形的紋理映射步驟為:
glBindTexture(GL_TEXTURE_2D,texture[0]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(30.0f,0.0f, -170.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(30.0f,0.0f, -150.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(30.0f,20.0f,-150.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(30.0f,20.0f,-170.0f);
glEnd();
實驗效果如下:

圖1.牆壁、地面、箱子
2、 石柱、雪人
石柱和雪人都屬於二次幾何體的繪制和紋理映射,其中雪人由圓柱、球體、圓盤、圓錐構成。OpenGL提供了這些基本幾何體的繪制函數,而且在紋理映射時我們采用自動生成紋理坐標的方法,所以這一部分工作量其實很少。另外,為了給雪人的身體、鼻子等部位設置顏色時,我們需要關閉紋理映射、並更改材質的屬性(物體的顏色是由光源和材質共同作用的結果)。對於石柱,我們加上了繞自身局部坐標Y軸的勻速旋轉。實驗效果如下:


圖2.雪人 圖3.石柱
我們使用Glut庫提供的如下函數繪制二次幾何體:
gluCylinder:繪制圓柱和圓柱
gluSphere:繪制球體
gluDisk:繪制圓盤
使用gluQuadricTexture設置自動計算紋理坐標。
3、 玻璃球
在繪制玻璃球時,為了在球面上反射出周圍的場景,我們使用了環境映射。由於我們能夠在三維場景中漫游,這就需要球面上反射出的場景是隨着視點移動而變化的,這一需求可以利用“渲染到紋理”(RTT)技術實現。其基本思想是:首先繪制出環境中的所有物體(除了該玻璃球),接着將繪制得到的場景圖制作成紋理,在繪制該玻璃球時將此紋理圖貼在球面上即可。
我們使用的核心函數是glCopyTexImage2D,該函數可以將幀緩存中的顏色值復制到紋理緩存中。實驗發現該函數性能較低,會拖慢整個程序的速度,為了平衡繪制質量和速度,可以只拷貝一部分像素值。實驗效果如下:

圖4.玻璃球
4、 霧氣
OpenGL中提供了完整的霧化接口,我們只需要選擇合適的霧氣的混合因子、密度、顏色、起始位置等。我們的霧氣設置如下:
GLfloat fogColor[4]={0.5f, 0.5f, 0.5f, 0.5f};
glFogi(GL_FOG_MODE,GL_EXP2);//模式
glFogfv(GL_FOG_COLOR,fogColor);//顏色
glFogf(GL_FOG_DENSITY, 0.004f);//密度
glFogf(GL_FOG_START, 5.0f);//開始距離
glFogf(GL_FOG_END, 300.0f);//結束距離
glHint(GL_FOG_HINT,GL_NICEST);//霧化效果
實驗效果如下:

圖5.霧氣
5、 天空
天空包含雲、星星、閃電三個對象。我們要實現的目標是:雲在空中飄動、且能夠遮蓋住空中的星星,每間隔一段時間都會有閃電和雷聲。


圖6.雲圖 圖7.二值圖
為了實現雲的飄動,我們在設置雲圖紋理坐標時添加一個變量,該變量從0遞增至1,超過1時做減1操作,這樣就可以模擬雲的移動。如下的roll即為該變量:
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 3.0f-roll);glVertex3f(-50.0f, 100.0f, -300.0f);
glTexCoord2f(1.0f, 3.0f-roll);glVertex3f( 50.0f, 100.0f, -300.0f);
glTexCoord2f(1.0f, 0.0f-roll);glVertex3f( 50.0f, 100.0f, 300.0f);
glTexCoord2f(0.0f, 0.0f-roll);glVertex3f(-50.0f, 100.0f, 300.0f);
glEnd();
每繪制一定幀數后,我們將一幅閃電紋理圖映射到天空,使用簡單的相加混合,且在首次繪制閃電時調用音頻播放函數,播放一段雷聲,音頻調用函數為PlaySound。實驗效果如下:

圖8.天空
