知識點-計算機圖形學

理解內容

http://staff.ustc.edu.cn/~lgliu/Resources/CG/What_is_CG.htm

定義:計算機圖形學(Computer Graphics 簡稱CG)是一種使用數學算法將二維或三維圖形轉化爲計算機顯示器的柵格形式的科學。

內容:建模、渲染、動畫和人機交互(Modeling, Rendering, Animation, Human-computer Interaction HCI) 。

一、建模
1.計算機輔助設計(CAD)中主流方法是採用NURBS(非均勻有理B-樣條、Bezier曲線曲面)方法。
2.細分曲面造型方法,作爲一種離散迭代的曲面構造方法,其構造過程樸素簡單以及實現容易。
3.利用軟件直接手工建模。現在主流的商業化的三維建模軟件有Autodesk 3D Max和 Maya。
n.三維幾何模型的構建過程中,還會涉及到很多需要處理的幾何問題,比如數據去噪(denoising or smoothing)、補洞(repairing)、簡化(simplification)、層次細節(level of detail)、參數化(parameterization)、變形(deformation or editing)、分割(segmentation)、形狀分析及檢索(shape analysis and retrieval)等。這些問題構成“數字幾何處理”的主要研究內容。
二、渲染
有了三維模型或場景,怎麼把這些三維幾何模型畫出來,產生令人賞心悅目的真實感圖像?這就是傳統的計算機圖形學的核心任務。
因此,如何充分利用GPU的計算特性,結合分佈式的集羣技術,從而來構造低功耗的渲染服務是發展趨勢之一。
三、動畫
動畫是採用連續播放靜止圖像的方法產生物體運動的效果。
四、人機交互
人通過一定的交互方式,讓計算機完成任務。
五、其他內容
虛擬現實,可視化,計算機藝術等。

學習基礎:計算機圖形學是一門與很多學科都交叉的學科方向。

一、數學
包括:微積分、線性代數、矩陣計算、微分幾何、數值計算和分析、計算方法、偏微分方程、微分方程數值解、最優解、概率、統計、計算幾何等。
研究過程中若遇到什麼數學知識再去學相關的知識,學習起來會更有興趣,掌握起來會更快更紮實;學習數學要結合圖形,即“數形結合”,需要有圖形的想象能力;
二、編程
掌握C++編程語言和麪向對象編程思想
三、其他
英語基礎要好,因爲需要大量閱讀英文文獻和進行英文論文的寫作;英文的聽說能力也要好些,因爲要跟國際學者交流討論;
計算機圖形學中的很多算法是真實物理世界的模擬,因此,如果你要進行基於物理的建模和仿真,一些物理知識和理論也需要的,比如力學(動力學,運動學,流體力學)和光學等;
四、教材
最好的學習方法就是在使用中學習。因此,計算機圖形學的學習和研究提供了你學習其他相關知識的好的過程。
計算機圖形學的內容遠比教材中或你想象中的內容多得多。

OpenGL基礎

Open Graphics Library 開放圖形庫,是用於渲染2D、3D矢量圖形的跨語言、跨平臺的應用程序編程接口(API)。常用於CAD、虛擬現實、科學可視化程序和電子遊戲開發。
OpenGL被設計爲只有輸出的,所以它只提供渲染功能。核心API沒有窗口系統、音頻、打印、鍵盤/鼠標或其他輸入設備的概念。

1.1 狀態機

在圖形繪製中,有許多狀態量需要管理,如果線條的顏色,線條的粗細,是否打開紋理映射和是否打開光照等。採用狀態機機制,對狀態量進行管理,即,一個狀態量被設置後,將對隨後的所有繪製指令生效,直到它被再次設置爲止。

  • 修改狀態量
    • 對於只需要打開、關閉的狀態量,如光照、紋理映射、深度檢測等,可以使用函數glEnable() 和glDisable() 。
    • 對於需要特定修改的狀態量,如當前顏色、當前線寬、當前點繪製尺寸、當前頂點法向量、當前定點紋理座標等,則分別提供了狀態量修改函數。glColor()/ glLineWidth()/ glPointSize()/ glNoraml()/ glNormal()/ glTexCoord()。
  • 狀態量查詢
    • 對於可供打開、關閉的狀態量,可以使用函數GLboolean gllsEnabled ( GLenum capability ) 來進行狀態量查詢。
    • 對於由參數定義的狀態量,則可以使用下面的函數獲取其參數。void glGetBooleanv(GLenum pname, GLboolean* params)/ glGetDoublev()/ glGetFloatv()/ glGetIntegerv()。【EG:GLfloat curColor[3] = {0, 0, 0}; glGetFloatv(GL_CURRENT_COLOR, curColor);】
  • 狀態量的保存、恢復
    • glPushAttrib( GLbitfield attribute_mask); glPopAttrib(void);

1.2 語法規則

OpenGL語法遵循以下規則:

  • 所有的OpenGL函數命名都以gl開頭;
  • OpenGL規範中的常量都是以GL_爲前綴的大寫字符串;
  • OpenGL有自己的類型定義,其餘C語言的標準類型有映射關係;
    OpenGL函數通常可以加上以下後綴:
  • 用數字表示該函數的參數個數,如2,3,4;
  • 用字母表示參數的類型,如字母i, f, d, ub, 分別表示整型,浮點型,雙精度浮點數,無符號byte類型;
  • 字母v表示參數是一個數組;
  • e.g.:glColor3f, glColor4fv, glColor3ub;

1.3 二維圖形繪製

繪製二維圖形,並設置其繪製屬性:顏色、線型。填充模式等。

  • 圖元
    • 在OpenGL中,所有顯示的結果都是由一個或多個圖形元素(簡稱圖元)組成。圖元包括了點、線段、多邊形等。
      GL_POINTS
      GL_LINES GL_LINE_STRIP GL_LINE_LOOP
      GL_TRIANGLES GL_TRIANGLE_STRIP GL_TRIANGLE_FAN
      GL_QUADS GL_QUAD_STRIP
      GL_POLYGON
    • 一個創建圖元的方法是調用函數glBegin()。這個函數的唯一參數用於指定將要創建圖元的類型。
    • 所有的圖元都由頂點組成,這些頂點由函數glVertex()指定。
    • 當所有的頂點都被指定後,繪製指定圖元時,需要調用一次函數glEnd()。
    • OpenGL窗口缺省爲從-1到1對應於從左到右,從底到頂。
    • OpenGL繪製多邊形(包含三角形)時需要滿足3個條件:
      簡單性:邊不能自交。
      凸性:多邊形內部任意兩點的連線都要落下多邊形內部。
      平整性:所有頂點都在一個平面。
    • OpenGL並不負責檢查輸入的頂點,這必須由調用方負責檢查。而三角形始終滿足以上條件,因此三角形被各種圖形應用大量使用。
  • 圖元屬性
    • 點劃模式:
      glEnable(GL_LINE_STIPPLE);
      glLineStipple(3, linestipple);
    • 點的尺寸:
      glPointSize(0);
      glGetFloatv(GL_POINT_SIZE, &size);
    • 線寬:GL_LINE_WIDTH
    • 邊標記:
      glEdgeFlag()傳遞GL_TRUE來指定一個標記,則任意一對頂點將繪製一條線段。如果將GL_FALSE傳遞給該函數,則不會有線段被繪製。
    • 多邊形繪製模式:點(頂點)、線段(線框)、填充(多邊形)。glGetIntegerv(GL_POLYGON_MODE,v_2)。GL_FRONT/GL_BACK/GL_FRONT_AND_BACK;
      GL_POINT/GL_LINE/GL_FILL;
    • 多邊形着色模式:
      glShadeModel(GLenum mode); GL_FLAT/GL_SMOOTH;

1.4 三維圖形繪製

繪製過程:場景定義,觀察定義;
glViewport();
glMatrixMode(GL_PROJECTION);
初始化變換:glLoadIdentity()
glPerspective()
gluLookAt()
glMatrixMode(GL_MODELVIEW);
初始化變換:glLoadIdentity()
glRotatef()
glCallList(object)

  • 模型變換
    旋轉 glRotatef(angle, vx, vy, vz); 繞向量旋轉角度
    平移 glTranslate(dx, dy, dz); 平移向量
    縮放 glScalef(sx, sy, sz); 縮放因子
    注意:這裏的旋轉變換和比例縮放變換都是以座標系原點爲中心進行的。
    變換次序:最後指定的變換最先執行,即後進先出。
  • 視點設置:觀察位置+觀察方向+偏轉方向
    gluLookAt();
    eyex,eyey,eyez 觀察位置;
    centerx,centery,centerz 觀察的中心點;
    upx,upy,upz 觀察的向上方向;
    觀察方向 = 觀察中心點 - 觀察位置;
    向上方向決定了偏轉;
  • 投影變換
    模型變換決定了觀察場景,視點設置決定了觀察者的參數。現在要把觀察者觀察到三維場景投影爲屏幕上的圖像,這個過程稱爲投影變換,
    • 正投影
      gluOrtho();glOrtho2D();
      left, right 定義了左右裁剪面
      bottom, top 定義了上下裁剪面
      near, far 定義了前後裁剪面
      定義了三維空間的一個盒子,落在其內部的幾何體將被正投影成像。
    • 透視投影
      gluPerspective();
      定義了三維空間中的四棱錐臺,落在其內部的幾何體將被投影成像。
      這個函數定義了投影提的頂面和底面的夾角、取景器的長寬比例以及前後裁剪平面。
  • 視區變換
    投影變換類似於取景器,最終的顯示將放置在平面的某個區域、該顯示區域的位置和大小則通過視圖變換來指定。
    視區變換作爲觀察定義的最後一步,它對應於觀察成像在屏幕上的位置、顯示的大小。
    glViewport();
    x,y 以像素爲單位指定視區的左上角,初始值爲(0,0);
    width, height 以像素爲單位指定視區的高度和寬度;

1.5 幾何變換

幾何變換是OpenGL繪製流水線中模型變換的核心內容,常被用於構造需要繪製的場景。
平移、旋轉、縮放和斜切都是幾何變換的例子。

  • 放射變換:仿射變換保證了模型變換後的直線平行性,但不保證角度和長度在變換後保持不變。
    右手座標系:|/_ zyx /__
    左手座標系: | yxz
    齊次座標:n維的向量用一個n+1維向量來表示。給出點的齊次表達式[X Y H],就可求得其二維笛卡爾座標,即
    [X Y H]→ = [x y 1], 這個過程稱爲歸一化處理。
    在幾何意義上,相當於把發生在三維空間的變換限制在H=1的平面內。
    變換矩陣:
  • 組合矩陣:變換次序不可交換;變換都是可逆的
  • 3D幾何變換
    齊次座標系:每個笛卡爾座標系點(x,y,z)被4個座標值表示(hx,hy,hz,h)。當w=0時,可以認爲該點落在無窮遠處,從而簡單地認爲是一個方向。
    推導:定義繞z軸旋轉的正方向爲,由正x軸方向先轉到正y軸方向。
    平移:x' = x + a; y' = y + b; z' = z +c; 縮放:x' = ax; y' = by; z' = cz; 斜切:x' = x + ay; y' = y; 旋轉:x' = xcos@ - ysin@; y' = xsin@ + ycos@; z' = z;
    3D變換矩陣:用4X4矩陣和齊次座標重新改寫。
    繞任意點旋轉:平移到原點
    繞任意軸旋轉:平移到原點
  • 改變座標系的變換:
  • OpenGL中的幾何變換
    glPushMatrix();
    glPopMatrix();
    glMultMatrix(); m’ = m * mx
    glLoadMatrixd(); glLoadMatrixf();
    glPush();
    glPop();
    glutSolidCube()

1.6 觀察和投影變換

觀察變換:相機被放置在座標系原點,而相機的朝向和-z方向重合,相機的向上方向和y軸重合。在這個座標框架下,裁剪和投影的過程將大大簡化。
投影變換:gluPerspective(45.0f, 1.0f*w/h, 1.0f, 100.0f);glOrtho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 20.0f);

1.7 光源和材料

創建光源,照亮場景中的材質物體。

  • 光源的四個分量
    漫反射(GL_DIFFUSE),
    環境光(GL_AMBIENT),
    鏡面反射(GL_SPECULAR),
    發射光(GL_EMISSION);
  • 光源的屬性
    glLight*(light, property, value)
    //*代表類型
    //light OpenGL常量GL_LIGHT0到GL_LIGHT7
    //property 需要設置的屬性
    //value 值
  • 屬性:位置
    property:GL_POSITION
    value:浮點數組,包含了光源位置的x,y,z,w值。光源可以是一個位置光源(w>0)或一個方向光源(w=0)。
  • 環境光、漫反射、鏡面反射
    property: GL_AMBIENT/GL_DIFFUSE/GL_SPECULARE;
    value: 浮點數組RGBA值;
  • 屬性:光的衰減
    光的強度和傳播距離的關係;衰減因子0-1,缺省值爲1;
    factor = 1/( GL_CONSTANT_ATTENUATION + GL_LINEAR_ATTENUATION * d + GL_QUADRATIC_ATTENUATION * d * d);這裏d表示光源和頂點間的距離,缺省值(1,0,0)。
    爲了產生距離衰減,必須將現行衰減和二次衰減的洗漱設置爲不等於0。(如0.2f, 0.08f)。
  • 屬性:特殊的光源
    筒燈光源,它的發射方向侷限在一個圓錐內部。
    GL_SPOT_DIRECT:定義發射圓錐體的軸向;
    GL_SPOT_CUTOFF:相對於圓錐軸向的最大光線角度,值爲[0, 90]間的角度。可以根據光線與筒燈軸線的夾角定義光線的衰減。GL_SPOT_ATTENUATION定義了光線的匯聚程度,0爲沒有衰減,128是最大衰減,該值越大,光線則越會匯聚在圓柱的中心部分。
  • 光照模型
    //glLightModel*(property, value);
    //property 將要設置的屬性
    //value 設置的屬性值
    可以使用光照模型,定義一下三個屬性:
    GL_LIGHT_MODEL_AMBIENT全局環境光顏色;RGBA
    GL_LIGHT_MODEL_LOCAL_VIEWER局部或無窮遠的視點;定義了鏡面光分量如何被計算。鏡面高光依賴於頂點和視點的方向,以及頂點和光源的方向。因此高光是依賴於觀察點的。@當使用無限遠視點時,頂點和視點的方向對所有的頂點保持不變,因此不需要被中心計算。@這樣光照的計算較快,這是缺省的視點。而使用局部視點時,視點和頂點的方向需要針對每個頂點計算。這樣會產生更加精確的高光計算模型@
    GL_LIGHT_MODEL_TWO_SIDE希望兩邊被照明或者只有一邊;
  • 材料屬性
    //glMatenal*(face, property, value)
    //face:GL_FRONT,GL_BACK,GL_FRONT_AND_BACK
    定義了物體如何對光線做出反應:吸收、反射等。
    GL_AMBIENT:對環境光反射的顏色。
    GL_DIFFUSER:對漫反射的顏色。
    GL_SPECULAR&GL_SHININESS:鏡面反射的顏色。當光在鏡面或金屬表面產生高光時定義這種發射。光潔度用來定義高光區域的尺寸和高光的強度。
    GL_EMISSION:產生物體似乎發射出光線的效果,用於創建臺燈或燈泡。注意:這些物體似乎放射出光線,但並不對場景產生照明。開發者需要在物體的位置創建一個光源來真正地發射出光線。
  • 光源與材料的數學關係
    全局公式被用來計算一個頂點的顏色:
    color=GLOBAL_AMBIENT+AMBIENT+DIFFUSE+SPECULAR+EMISSION;
    GLOBAL_AMBIENT:光源的環境光分量,被材料的環境光屬性所衰減;GL_LIGHT_MODEL_AMBIENT*GL_AMBIENT(from the material)
    AMBIENT,DIFFUSE,SPECULAR:環境光項是從每個光源來的環境光和光照模型的全局環境光之和;AMBIENT+DIFFUSE+SPECULAR=。GL_SPOT_AXPONENT控制光的就算程度。需要了解這些項依賴於頂點位置和朝向(法向量)。
    EMISSION:發射項爲材料的發射值。 =GL_EMISSION
  • 照亮場景
    創建一個光源,需要三個屬性:GL_POSITION,GL_AMBIENT,GL_DIFFUSE;光源的位置,光源的環境光顏色,光源的顏色;
    float light_diffuse[] = {1.0f, 1.0f, 0.0f, 1.0f};//散射光<=>物體紅色
    float light_ambient[]={1.0f, 0.0f, 0.0f, 1.0f};//環境光<=>物體黃色,直接投射到物體表面
    float light_pos[]={-2.0f, -2.0f, 7.0f, 1.0f};
    glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
    glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
    glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
    glEnable(GL_LIGHT0)//打開光源
    glEnable(GL_LIGHTING);//打開光照
    表面朝向:法向量作用於頂點,多邊形的表面朝向通過他的法向量定義。下面演示如何爲四邊形添加法向量。類似於顏色、紋理座標,應用於一個頂點的法向量爲當前法向量。glNormal3f(x, y, z);立方體有六個面,因此需要對六個面分別指定六個法向量。
    glBegin(GL_QUADS);
    //第一個四邊形
    glNormal3f(1.0, 0.0f, 0.0f):
    glTexCoord2f(0.0f, 1.0f);
    glVertex3d(size/2, size/2, size/2); //V2
    glTexCoord2f(0.0f, 0.0f);
    glVertex3d(size/2, -size/2, size/2); //V1
    glTexCoord2f(1.0f, 0.0f);
    glVertex3d(size/2, -size/2, -size/2); //V3
    glTexCoord2f(1.0f, 1.0f);
    glVertex3d(size/2, size/2, -size/2); //V4
    //第二個四邊形

    glEnd();
  • 創建不同的光源
    點光源、方向光源與筒燈光源,並將同時使用三個不同類型的光源對一個球體進行照明。
    方向光源:w=0;沒有環境光分量,白色漫反射分量,該光源並不指定在特定的空間位置,所有的管線來自無窮遠處,並具有方向(x,y,z),所有的光線平行;
    位置光源:w!=0;黃色的環境光分量,黃色的漫反射分量,位置(x,y,z),光線來自指定的位置(x,y,z),並向各個方向發射;
    筒燈:兩個主要屬性(GL_SPOT_DIRECTION和GL_SPOT_CUTOFF),前者定義了筒燈的主軸方向,而後者定義了筒燈的照射範圍。可以看做是位置光源的特例。
    筒燈衰減:除了3個依賴於光源和照射頂點的距離的衰減分量GL_CONSTANT_ATTENUATION/GL_LINEAR_ATTENUATION/GL_QUADRATIC_ATTENUATION/外,還有一個特別依賴於頂點與筒燈中心軸的距離衰減。該衰減反映了筒燈照明匯聚程度。GL_SPOT_EXPONET指定,取值[0,128],值越大,光越匯聚中軸。
    筒燈光源:需要指定筒燈方向GL_SPOT_DIRECTION和筒燈的夾角GL_SPOT_CUTOFF;定義管線匯聚程度;定義衰減;更新光源的位置、方向和夾角。
  • 材料設置
    環境光和漫反射前面介紹過了,鏡面反射和光潔度可以在物體表面產生高光區域。鏡面反射分量定義了高光的顏色,而光潔度則決定了高光區域的尺寸和高光的強度。
    用指定材料屬性繪製球體:
    float notmat[]
    float mat_ambient[]
    float mat_ambient_color[]
    float mat_diffuse[]
    float mat_specular[]
    float no_shiniess
    float high_shiness
    float mat_emission[]
    //繪製一:只有漫反射,沒有環境光和鏡面
    glPushMatrix();
    glTranslatef(-3.75f, 3.0f, 0.0f);
    glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat);
    glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess);
    glMaterialfv(GL_FRONT, GL_NEMISSION, no_mat);
    gluSphere();
    glPopMatrix();

1.8 紋理映射

使用OpenGL紋理函數在多邊形上貼紋理,自動生成紋理座標,以及如何在同一張多邊形表面同事使用多張紋理。

  • 在OpenGL中,使用紋理映射的基本流程如下:
    從數據文件中裝載紋理數據;
    產生一個紋理標識符;
    選擇當前使用的紋理;
    指定紋理的放大/縮小過濾方式;
    從紋理數據生成一個紋理;
    打開紋理映射;
    繪製場景,爲使用該紋理的模型頂點指定紋理座標;
  • 在多邊形上貼紋理
 unsigned char* LoadBitmapFile(char* filename, BITMAPINFOHEADER* bitmapinfoHeader);//自定義函數
 BITMAPINFOHEADER bitmapinfoHeader; //bitmap信息頭
 unsigned char* bitmapData;//紋理數據
 bitmapData = NULL;
 bitmapData = LoadBitmapFile("world.bmp", &bitmapinfoHeader);//讀取文件數據
 unsigned int texture[2];
 glGenTextures(2, texture);//可以一次生成多個紋理標識符
 glBindTexture(GL_TEXTURE_2D, texture[0]);//指定當前使用的紋理,餘下操作將只作用與該紋理
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//放大:當像素比對應紋理上的像素texel小;(GL_NEAREST沒有過濾,點採樣,取最近的texel值;GL_LINEAR雙線性插值)
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//縮小:當像素比對應紋理上的像素大;(前兩+;GL_NEAREST_MIPMAP_NEAREST取最接近的mipmap的值;GL_LINEAR_MIPMAP_LINEAR三線性插值)
 //下面將從bitmap文件中讀取的圖像文件交給OpenGL紋理,紋理數據在OpenGL內部保存,也可能保存在顯卡上。
 glTexImage2D(GL_TEXTURE_2D, 0, //mipmap層次,通常爲0,表示最上層
 GL_RGB, //希望有紅綠藍數據
 bitmapInfoHeader.biWidth,//紋理寬度,必須是2^n,若有邊框+2
  bitmapInfoHeader.biWidth,//紋理高度,必須是2^n,若有邊框+2
  0, //邊框:0=無邊框;1=有邊框
  GL_RGB,//bitmap數據格式
  GL_UNSIGNED_BYTE,//每個顏色數據的類型
  bitmapData); //bitmap數據指針
  //如果使用了mipmap,則需要對每個mipmap都調用一次上面的函數,並相應地改變mipmap的層次數。
  glEnable(GL_TEXTURE_2D);//繪製前需要打開紋理映射
  //----------------------------------------------------------
  //下面的代碼創建一個四邊形,並將該四邊形的頂點與紋理的角點建立關聯
  glBindTexture(GL_TEXTURE_2D, texture[1];//從已裝載紋理中選擇當前紋理
  glColor3f(1.0f, 1.0f, 1.0f);//將當前顏色設置爲白色
  //繪製四邊形,併爲每個頂點設定紋理座標
  glBegin(GL_QUADS);
  glTexCoord2i(1, 1); glVertex3i(1,1,0);
  glTexCoord2i(1, 0); glVertex3i(1,-1,0);
  glTexCoord2i(0, 0); glVertex3i(-1,-1,0);
  glTexCoord2i(0, 1); glVertex3i(-1,1,0);
  glEnd();
  //紋理座標:X:0~1;Y:0~1, 左下角(0, 0)
  //四邊形頂點:X:-1~1;Y-1~1;Z:-1~1, 左下角(-1,-1,-1)
  • 紋理和光照的混合
    使用參數GL_MODULATE,通過紋理環境模式進行光照、顏色和紋理的混合。
  • 自動生成紋理座標
    爲了實現紋理映射,需要對模型的每個頂點都設置一個紋理座標。
//調用glTexGen(),並使用函數glEnable()打開了模式GL_TEXTURE_GEN_S和GL_TEXTURE_GEN_T;
planeCoefficients = [1, 0, 0, 0];
glTexGen(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
glTexGen(GL_S, GL_OBJECT_PLANE, planCoefficients);
glEnable(GL_TEXTURE_GEN_S);
glBegin(GL_QUADS);
glVertex3f(-3.25, -1, 0);
glVertex3f(-1.25, -1, 0);
glVertex3f(-1.25, 1, 0);
glVertex3f(-3.25, 1, 0);
glEnd();
 //用於控制紋理座標生成的方式:void glTexGeni(GLenum coord, GLenum pname, GLdouble, param); 
 //coord:指定一類紋理座標,必須爲GL_S,GL_T,GL_R,GL_Q之一
 //pname:必須是GL_TEXTURE_GEN_MODE
 //param:紋理生成參數,必須爲GL_OBJECT_LINEAR,GL_EYE_LINEAR,GL_SPHERE_MAP之一;前兩個都是按照頂點到一個平面的距離產生紋理座標

1.平面定義:AX+BY+CZ+D=0;點[x,y,z]是否落在該平面;向量[A,B,C]是該平面的法向量,即決定了平面的朝向;係數D控制該平面到原點的距離;如果[A,B,C]是一個單位向量,則D等於平面到原點的距離;
2.紋理座標生成模式:GL_OBJECT_LINEAR; glTexGenfv(GL_S, GL_OBJECT_PLANE, planeCoefficients); 紋理座標S=A
X+BY+CZ+D,這裏的[x,y,z]爲頂點的座標值,通過函數glVertex()傳遞;可以通過給係數乘以不同的比例來控制紋理重複出現的頻率(即對紋理進行縮放);
3.紋理座標生成模式:GL_EYE_LINEAR; glTexGenfv(GL_S, GL_EYE_PLANE, planeCoefficients); GL_OBJECT_LINEAR模式計算的紋理座標只和函數glVertex()傳遞的值的相關,不會通過變換改變物體或相機發生變化。而GL_EYE_LINEAR模式使用經過了所有ModelView變換後的頂點位置產生紋理座標,即它們使用頂點在平面上的座標產生紋理座標。
4.二維紋理自動生成:上個實例中,始終只對紋理座標S進行了生成。事實上,OpenGL支持對紋理座標S和T的同時自動生成。例如,可以對紋理座標T使用不同的平面方程調用函數glTexGen():

SplaneCoefficients=[1,0,0,0];
glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
glTexGenfv(GL_S,GL_EYE_PLANE,SplaneCoefficients);
glTexGenfv(GL_S,GL_OBJECT_PLANE,SplaneCoefficients);
TplaneCoefficients=[0,1,0,0];
glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR);
glTexGenfv(GL_T,GL_EYE_PLANE,TplaneCoefficients);
glTexGenfv(GL_T,GL_OBJECT_PLANE,TplaneCoefficients);

5.球形映射:環境紋理的一種,常被用於模擬反射的表面。在OpenGL中可以通過紋理座標自動生成GL_SPHERE_MAP來進行控制。在這種模式下,並沒有其他的參數:glTexGeni(GL_S, GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE,GL_SPHERE_MAP); 在球形映射中,將使用從視點到觀察點的向量作爲參考,並將它相對於模型表面法向量進行反射,得到的反射向量將用於從紋理中查詢顏色。該紋理嘉定提供了周圍被反射環境的360度全方位包圍。

  • 多重紋理
    在一次繪製中使用多張紋理,顯卡支持ARB擴展GL_ARB_multitexture.
    1.擴展檢測:首先要做的是檢測多重紋理是否被顯卡支持。
多重紋理是否被支持
constGLubyte *extensions = glGetString(GL_EXTENSIONS); 
bool multiTexturingSupported = strstr((const char*)extensions, "GL_ARB_multitexture") != NULL; 
初始化相關函數
glMultiTexCoord1fARB = (PFENGLMULTITEXCOORD1FARBPROC) wglGetProcAddress("glMutiTexCoord1fARB");
glMutiTexCoord2fARB =  (PFENGLMULTITEXCOORD2FARBPROC) wglGetProcAddress("glMutiTexCoord2fARB");
glMutiTexCoord3fARB =  (PFENGLMULTITEXCOORD3FARBPROC) wglGetProcAddress("glMutiTexCoord3fARB");
glMutiTexCoord4fARB =  (PFENGLMULTITEXCOORD4FARBPROC) wglGetProcAddress("glMutiTexCoord4fARB");
glActiveTextureARB = (PFENGLACTIVETEXTUREARBPROC)wglGetProcAddress("glActiveTextureARB");
glClientActiveTextureARB = (PFENGLCLIENTACTIVETEXTUREARBPROC)wglGetProcAddress("glClientActiveTextureARB");

2.OpenGL擴展:
3.多重紋理擴展:允許使用超過一個紋理單元。一個紋理單元可以用來保存紋理座標、紋理過濾方式、紋理環境狀態、紋理座標自動生成方式等。對每個紋理單元可以對應於一張不同的紋理。多重紋理功能非常強大,允許在一次繪製時使用多張紋理。當物體複雜時,使用多次繪製來實現同樣的效果將耗時更多。
4.紋理單元:開發者可以控制當前使用哪個紋理單元。一個紋理單元通過OpenGL常量GL_TEXTUREi_ARB來訪問。該常量爲一個0到31之間的整數。因此,最多的紋理單元數爲32,但並不是所有的顯卡都能支持這麼多紋理單元(例如,很多顯卡只支持8個紋理單元)。

//爲了查詢可使用的紋理單元數,可以使用函數glGetIntegerv(),並使用參數GL_MAX_TEXTURE_UINTS_ARB;
GLint maxTextureUnits = 0;
glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB, &maxTextureUnits);
//爲了激活一個紋理單元,可以將它的常量作爲參數調用函數glActiveTextureARB();
glActiveTextureARB(GL_TEXTUREi_ARB);
//則當前的紋理單元爲GL_TEXTUREi_ARB, 直到重新調用這個函數激活另一個紋理單元;

5.使用多重紋理:

//在激活了紋理單元后,對於紋理的操作和使用一個紋理類似綁定紋理,指定紋理過濾方式。唯一的不同在於指定頂點的紋理座標,現在需要使用函數glMultiTexCoord** ARB()
glMultiTexCoord**ARB(GL_TEXTUREi_ARB, ...); //其中,參數...爲紋理座標
//演示使用兩張紋理來繪製一個方塊,並且更改不同的模式,使用一張紋理或使用多重紋理。
if (mode == 0){
  glActiveTextureARB(GL_TEXTURE0_ARB);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, texture[0]);
}else{
  if (mode == 1) {
    glActiveTextureARB(GL_TEXTURE0_ARB);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, texture[1]);
 }else{
   glActiveTextureARB(GL_TEXTURE0_ARB);
   glEnable(GL_TEXTURE_2D);
   glBindTexture(GL_TEXTURE_2D, texture[0]);
   glActiveTextureARB(GL_TEXTURE1_ARB);
   glEnable(GL_TEXTURE_2D);
   glBindTexture(GL_TEXTURE_2D, texture[1]);
 }
 //將當前顏色設置爲白色
 if (b['r']) 
 	glColor3f(1.0f, 0.0f, 0.0f);
 else
 	glColor3f(1.0f, 1.0f, 1.0f);
 //繪製四邊形
 //併爲每個頂點設定紋理座標
 glBegin(GL_QUADS);
 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);
 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 1);
 glVertex3i(1, 1, 0);
 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);
 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 0, 0);
  glVertex3i(1, -1, 0);
 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);
 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 0);
  glVertex3i(-1, -1, 0);
 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);
 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, 1, 1);
  glVertex3i(-1, 1, 0);
  glEnd();
  //恢復紋理狀態
  glActiveTextureARB(GL_TEXTURE1_ARB);
  glDisable(GL_TEXTURE_2D);
  glActiveTextureARB(GL_TEXTURE1_ARB);
}

1.9 加速渲染——頂點數組、顯示列表和頂點緩衝對象

前面已經介紹瞭如何使用OPENGL的API進行圖形繪製,但他們的效率存在問題。本章重點介紹OPENGL提供的頂點數組、顯示列表和頂點頂點緩衝對象等三種渲染加速技術,並討論他們的優缺點以及如何根據應用場景的不同進行選擇。

  • 之前
    glBegin()//開始繪製OPENGL
    glNormal()//指定頂點的法向量
    glColor()//指定頂點的顏色
    glTexCood()//指定定點的紋理座標
    glVertex()//指定頂點的空間位置
    … //依次給出其他頂點的屬性
    glEnd()
    這種向OPENGL輸出[繪製指令]的方式被稱爲立即模式。優點便於理解,缺點效率較低,可能會多次重複項OPENGL輸入頂點信息。
  • 頂點數組VertexArray
    提供非立即模式進行渲染,通過將數據準備和繪製指令分離開,減少數據冗餘,提高渲染速度。
使用頂點數組三步:
指定將要使用的頂點數據(包含座標位置、顏色、法向量、紋理座標和邊可見性標記的數組);
將這些頂點數據傳輸給OPENGL;
用這些頂點數據進行繪製(將這些頂點組裝成什麼體素).
  • 指定頂點數據:glEnableClientState()和glDisableClientState()來激活和關閉六個不同類型的數組。同時6個函數用於指定這些數組的精確位置(地址),這樣OPENGL就可以訪問這些數組了。
glVertexPointer(); 指定頂點空間位置
glNormalPointer();指定頂點法向量數組
glColorPointer();指定頂點顏色數組
glIndexPointer();指定顏色索引數組
glTexCoordPointer();指定紋理座標數組
glEdgeFlagPointer();指定邊可見性標記數組

注意這些頂點的屬性數組,存放在應用程序中(系統內存),即客戶端。而OpenGL在服務端訪問他們。這就是爲什麼對頂點數組需要調用特別的glEnableClientState()和glDisbaleClientState()函數,而不是調用glEnable()和glDisable()函數。

#define X 0.5257311f
#define Z 0.85065080f
GLfloat vertices[][3] = {
{-X, 0.0, Z}, {X, 0.0, Z}, {-X, 0.0, -Z}, {X, 0.0, -Z},
{0.0, Z,X},{0.0,Z,-X},{0.0,-Z,X},{0.0,-Z,-X},
{Z,X,0.0},{-Z,X,0.0},{Z,-X,0.0},{-Z,-X,0.0},
};//多面體的頂點數據
//將使用頂點數組來產生座標信息
glEnableClientState(GL_VERTEX_ARRAY);
//將頂點數組提交給OPENGL使用
glVertexPointer(3,GL_FLOAT,0,(GLvoid*)vertices);
  • 進行繪製
    將數據提交給OPENGL後,可以使用函數glArrayElement()進行繪製。glArrayElement(i)將從頂點數組中取出第i個頂點座標使用,而如果其他數組(法向量、紋理座標、顏色等)處於激活狀態,他們的第i個元素也可以被使用。
glBegin(GL_TRANGLES);
//用第1、4、0個座標數據中的元素畫一個三角形
glArrayElement(1);
glArrayElement(4);
glArrayElement(0);
//用第4、9、0個座標數據中的元素畫一個三角形
glArrayElement(4);
glArrayElement(9);
glArrayElement(0);
glEnd();

通過使用下表訪問頂點數據,重複的頂點數據被清除了。但是通過上面的硬編碼來指定頂點的組合對於大型的程序顯然是不合適的。一個較好的方法是將這些下標保存在一個數組中,如下例

GLfloat vertices[][3] = {
{-X, 0.0, Z}, {X, 0.0, Z}, {-X, 0.0, -Z}, {X, 0.0, -Z},
{0.0, Z,X},{0.0,Z,-X},{0.0,-Z,X},{0.0,-Z,-X},
{Z,X,0.0},{-Z,X,0.0},{Z,-X,0.0},{-Z,-X,0.0},
};//多面體的頂點數據
//將每個三角形的下標值(3個)保存在數組中,該下標值將用於從頂點數組獲取數據。
//同時,在其他的地方......
//將使用頂點數組來產生座標信息
glEnableClient(GL_VERTEX_ARRAY);
//將頂點數組提交給OPENGL使用
glVertexPointer(3, GL_FLOAT, 0, (GLvoid*)vertices);
//將下標從數組取出用用於繪製
glBegin(GL_TRIANGLES);
for (int i = 0; i< 20; i++) {
glArrayElement(triangles[i][0]);
glArrayElement(triangles[i][1]);
glArrayElement(triangles[i][2]);
}
glEnd();
//最後,可採用glDrawElements進一步優化
glDrawElements(GL_TRIANGLES, sizeof(triangles)/3, GL_UNSIGNED_INT, triangles);
//數組triangles裏每個元素用來訪問頂點數組
//這段代碼和上面功效相同,且效率更高
  • 顯示列表 Display List
    顯示列表可以看成一串預編譯的OPENGL繪製指令。大部分OPENGL指令,如光照設定、紋理操作、矩陣操作、頂點指令等,都可以通過創建顯示列表來進行預計算。而在調用該顯示列表進行多次繪製時,這些指令不用再次計算,從而實現加速渲染。一次定義,多次繪製。廣泛應用。
#define NEW_LIST 1 
//爲顯示列表定義一個唯一的標識符
//顯示列表開始
glNewList(NEW_LIST, GL_COMPILE);//編譯(但不立即顯示)
//參數二還有哦一個參數GL_COMPILE_AND_EXECUTE
glBegin(...);
...
glEnd();
//許多繪製體素,變換,光照函數
...
glEndList();//顯示列表被創建。
//執行顯示列表
glCallList(NEW_LIST);
  • 頂點緩衝對象 Vertex Buffer Object,VBO
    頂點數組的數據依然駐留在系統內存中,然而現在顯卡有顯存,將數據駐留在顯卡上進行處理,則可以大大加快渲染效率。
    GL_ARB_vertex_buffer_object來支持頂點緩衝對象VBO,VBO使得圖形數據可以保存在服務器的高性能內存上,並提供高效的數據傳輸手段。
    頂點數據可以減少圖形函數的調用次數,同時消除共享頂點的數據冗餘。缺點在於,頂點數據都保存在客戶端,每次訪問這些數據時,他們都需要重新傳輸到服務器端。而現實列表是服務端函數,它沒有數據傳輸方面的缺陷,但是當它被編譯後,就無法修改其內容。
    頂點緩衝對象在服務器端的高性能內存上創建“緩衝對象”,並提供與頂點數據相同的對引用數組的訪問函數,如glVertexPointer(),glNormalPointer()等。頂點緩衝對象中的數據可以通過將其映射到系統內存中進行讀取和更新。
    使用頂點緩衝對象
    • 1.創建頂點緩衝對象
//用glGenBuffersARB()創建一個新的頂點緩衝對象;
//用glBindBuffersARB()綁定一個頂點緩衝對象;
//用glBufferDataARB將頂點數據複製到頂點緩衝對象;
void glGenBuffersARB(GLsize ini,GLuint* ids);//創建緩衝對象個數,保存返回標誌符的數組地址;
void glBindBufferARB(GLenum target, GLuint id);//將緩衝對象與對應的ID建立關聯,target用來標明該緩衝對象用於保存頂點數組數據還是索引數組數據GL_ARRAY_BUFFER_ARB或GL_ELEMENT_ARRAY_BUFFER_ARB。一旦函數調用後,VBO將緩衝對象初始化爲零尺寸,並設置其初始狀態,如使用和訪問屬性等。
void glBufferDataARB(GLenum target, GLsize size, const void* data, GLenum usage);//當緩衝對象初始化後,可以用函數將數據複製到緩衝對象。target取值GL_ARRAY_BUFFER_ARB或GL_ELEMENT_BUFFER_ARB;size是傳送數據的字節數;data指向來源數據的數組;usage指明該緩衝對象將如何使用:靜態、動態、流或讀、複製、繪製。GL_STATIC_DRAW_ARB、GL_DYNAMIC_COPY_ARB、GL_STREAM_READ_ARB(3x3種)。
//靜態static指該緩衝對象中的數據將不會修改(指定一次,修改多次);動態dynamic標明該緩衝對象的數據將經常修改(指定多次,修改多次);流stream表明數據每幀都會修改(指定一次,使用一次);
//繪製draw指數據將會送到圖形處理器用於繪製。讀Read指數據將被客戶端讀取。複製copy表明數據將同時用於繪製和讀取。對VBO來說,只有繪製時有限的,讀和複製爲其他的擴展保留。
//下面的代碼將創建一個存放頂點座標數據的VBO。注意,可以在數據複製到VBO後,立即將頂點數組的內存釋放
GLuint vbold; //VBO標識符
GLfloat* vertices = new GLfloat[vCount*3];//創建一個頂點數組
...
//創建一個新的VBO,並得到它的相關ID
glGenBuffersARB(1, &vboid);
//綁定VBO以使用它的相關ID
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboid);
//傳輸數據到該VBO
glBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, vertices, GL_STATIC_DRAW_ARB);
//複製數據到VBO後就可以將頂點數據釋放了
delete[] vertices;
...
//程序終止時釋放該VBO
glDeleteBUffersARB(1, &vboid);
    • 2.繪製頂點緩衝對象
  • 由於頂點緩衝對象的實現建立在頂點數據的實現之上,繪製頂點緩衝對象幾乎與繪製頂點數組相同。唯一的區別在於,頂點數據的指針在繪製頂點緩衝對象時作爲當前綁定的緩衝對象的偏移量的使用。
//將頂點緩衝對象綁定到頂點數組和索引數組
glBindBuffersARB(GL_ARRAY_BUFFER_ARB, vboid1);//頂點座標
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, vboid2);//索引值
//和頂點數據繪製相同,除了指針
glEnableClientState(GL_VERTEX_ARRAY);//激活座標數組
//最後一個參數是偏移量,而不是指針
glVertexPointer(3, GL_FLOAT, 0, 0);
//用索引畫留個四邊形
glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, 0);
glDisableClientState(GL_VERTEX_ARRAY);//關閉頂點數組
//綁定爲0,切換到正常的指針操作
glBindBuffersARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
//將緩衝對象綁定爲0,則關閉了頂點緩衝對象操作。在使用完頂點緩衝對象後最好將它關閉,這樣原來的頂點數組只恨可以被重新激活。
    • 3.更新頂點緩衝對象
      一種方法,使用glBufferDataARB()呵呵glBufferSubDataARB(),重新賦值新的數據到綁定的頂點緩衝對象。應用程序應該始終有一個合法的頂點數組,即開發者始終維護兩份頂點數據:一個正在使用,另一個在頂點緩衝對象。
      另一種方法,將緩衝對象映射到客戶端內存進行修改。客戶端使用映射到的緩衝更新數據。
void* glMapBufferARB(GLenum target, GLenum access);用於將緩衝對象映射到客戶端內存。
返回一個指向該緩衝的指針,否則返回NULL.
TARGET:
ACCESS:指明對映射的數據做如何操作:讀、寫、讀和寫(GL_READ_ONLY_ARB,GL_WRITE_ONLY_ARB,GL_READ_WRITE_ARB).
GLboolean glUnmapBufferARB(GLenum target):
對VBO的數據修改完畢後,必須撤銷在客戶端程序建立的映射。
如果映射撤銷成功,返回TRUE; 返回FALSE時,標明在建立映射時該VBO的內容已損壞
//綁定,然後映射該VBO
glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboid);
float* ptr = (float*)glMapBufferARB(GL_ARRAY_BUFFER_ARB,GL_WRITE_ONLY_ARB);
//如果指針合法,修改VBO內容
if(ptr) {
updateMyVBO(ptr, ...); //修改VBO中的數據
glUmmapBufferARB(GL_ARRAY_BUFFER_ARB);//使用完畢後撤銷映射
}
//可以繪製修改後的VBO了
...

1.10 文字輸出

OpenGL中,沒有內置文字輸出函數。

  • 位圖文字輸出
    取一位圖,256x256像素;每個字符佔用16x16個像素,共16x16=256個字符。
基於文字位圖的文字繪製由以下三步:
1.將文字位圖裝載爲OpenGL紋理;LoadBitmapFile
2.爲位圖中每個字符的繪製生成顯示列表;
3.在場景繪製時,調用文字的顯示列表顯示文字
爲每個字符生成顯示列表
float cx,cy; //存放當前字符的x,y座標;
base = glGenList(256); //創建256個顯示列表

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章