OpenGL學(xué)習(xí)之路(2.0):OpenGL 基礎(chǔ)渲染

學(xué)習(xí)目標(biāo):

  • OpenGL 渲染結(jié)構(gòu)
  • 如何使用7種OpenGL基礎(chǔ)圖元
  • 如何使用儲(chǔ)存著色器
  • 如何使用Uniform屬性
  • 如何使用GLBatch 幫助類傳遞幾何圖形

OpenGL 渲染結(jié)構(gòu)

  • 基礎(chǔ)圖形管線
    • OpenGL 中的圖元是頂點(diǎn)的集合以預(yù)定義的?式結(jié)合?起。
      • 例如:?個(gè)單獨(dú)的點(diǎn)就是?個(gè)圖元。它只需要一個(gè)頂點(diǎn)
  • 渲染管線簡(jiǎn)化流程
    流程圖.png
    • 客戶端和服務(wù)端
      • 管線分為上下兩部分,上部分是客戶端,而下部分則是服務(wù)端。
      • 客戶端是存儲(chǔ)在CPU儲(chǔ)存器中的,并且在應(yīng)用程序中執(zhí)行或者在主系統(tǒng)內(nèi)存中驅(qū)動(dòng)程序中執(zhí)行。驅(qū)動(dòng)程序回將渲染命令和數(shù)組組合起來,發(fā)送給服務(wù)器執(zhí)行?。ㄔ?臺(tái)典型的個(gè)人計(jì)算機(jī)上,服務(wù)器實(shí)際上就是圖形加速卡上的硬件和內(nèi)存)
      • 服務(wù)?和客戶機(jī)在功能上也是異步的。它們是各?獨(dú)立的軟件塊或硬件塊。我們是希望它們2個(gè)端都盡量在不停的工作。客戶端不斷的把數(shù)據(jù)塊和命令塊組合在?起輸送到緩沖區(qū),然后緩沖區(qū)就會(huì)發(fā)送到服務(wù)器執(zhí)行。
      • 如果服務(wù)器?停?工作等待客戶機(jī),或者客戶機(jī)停?工作來等待服務(wù)?做好接受更多的命令和準(zhǔn)備,我們把這種情況成為管線停滯
    • 著色器
      • 上圖的Vertex Shader(頂點(diǎn)著??) 和 Fragment Shader(?段著??)。
        • 著??是使用GLSL編寫的程序,看起來與C語?非常類似。 著??必須從源代碼中編譯和鏈接在一起。最終準(zhǔn)備就緒的著??程序
        • 頂點(diǎn)著??-->處理從客戶機(jī)輸?的數(shù)據(jù)、應(yīng)?變換、進(jìn)?其他的類型的數(shù)學(xué)運(yùn)算來計(jì)算關(guān)照效果、位移、顏?值等等。(**為了渲染共有3個(gè)頂點(diǎn)的三?形,頂點(diǎn)著??將執(zhí)行3次,也就是為了每個(gè)頂點(diǎn)執(zhí)??次)在?前的硬件上有多個(gè)執(zhí)?單元同時(shí)運(yùn)?,就意味著所有的3個(gè)頂點(diǎn)可以同時(shí)進(jìn)?處理!
    • primitive Assembly(圖元裝配)
      • 說明的是:3個(gè)頂點(diǎn)已經(jīng)組合在一起,而三角形已經(jīng)逐個(gè)片段地進(jìn)行了光柵化。每個(gè)片段通過執(zhí)行片元著色器
    • 注意要點(diǎn)
      • 必須要為著色器提供數(shù)據(jù),否則無法實(shí)現(xiàn)!
      • 為著色器傳遞渲染數(shù)據(jù)的方法有三種
        • 屬性(Attributes)
        • uniform值
        • 紋理Texture Data
  • 屬性、uniform值、紋理、輸出
    • 屬性
      • 對(duì)每一個(gè)頂點(diǎn)都要作改變的數(shù)據(jù)元素。實(shí)際上,頂點(diǎn)位置本身就是一個(gè)屬性。屬性值可以是浮點(diǎn)數(shù)、整數(shù)、布爾數(shù)據(jù)。
      • 屬性還可以是:紋理坐標(biāo)、顏色值、關(guān)照計(jì)算表面法線
      • 在頂點(diǎn)程序(shader渲染)可以代表你想要的任何意義。因?yàn)槎际悄阍O(shè)定的。
      • 屬性會(huì)從本地客戶機(jī)內(nèi)存中復(fù)制存儲(chǔ)在圖形硬件中的一個(gè)緩沖區(qū)上。這些屬性只提供給頂點(diǎn)著??使用,對(duì)于?元著??沒有太?意義。
      • 聲明: 這些屬性對(duì)每個(gè)頂點(diǎn)都要做改變,但并不意味著它們的值不能重復(fù)。通常情況下,它們都是不一樣的,但有可能整個(gè)數(shù)組都是同一值的情況。
    • Uniform值
      • 屬性是?種對(duì)整個(gè)批次屬性都取統(tǒng)一值的單一值。它是不變的。通過設(shè)置uniform變量就緊接著發(fā)送?個(gè)圖元批次命令,而Uniform變量實(shí)際上可以?數(shù)次限制地使?,設(shè)置一個(gè)應(yīng)用于整個(gè)表面的單個(gè)顏色值,還可以設(shè)置一個(gè)時(shí)間值。在每次渲染某種類型的頂點(diǎn)動(dòng)畫時(shí)修改它。
      • 注意: 這?的uniform 變量每個(gè)批次改變一次,?不是每個(gè)頂點(diǎn)改變?次。
      • 與屬性相同點(diǎn):可以是浮點(diǎn)值、整數(shù)、布爾值
      • 與屬性不同:頂點(diǎn)著色器和片元著色器都可以使用uniform變量。uniform變量還可以是標(biāo)量類型、矢量類型、uniform矩陣。
    • 紋理
      • 傳遞給著??的第三種數(shù)據(jù)類型:紋理數(shù)據(jù)
        • 在頂點(diǎn)著??、?段著色?中都可以對(duì)紋理數(shù)據(jù)進(jìn)行采樣和篩選。
        • 典型的應(yīng)用場(chǎng)景: 片段著??對(duì)?個(gè)紋理值進(jìn)行采樣,然后在一個(gè)三?形表面應(yīng)用渲染紋理數(shù)據(jù)。
        • 紋理數(shù)據(jù),不僅表現(xiàn)在圖形,很多圖形文件格式都是以無符號(hào)字節(jié) (每個(gè)顏?色通道8位)形式對(duì)顏色分量進(jìn)行存儲(chǔ)的。
    • 輸出(outs)
      • 在圖表中第四種數(shù)據(jù)類型是輸出(out);輸出數(shù)據(jù)是作為?個(gè)階段著色器的輸出定義的,而后續(xù)階段的著??則作為輸?定義。
      • 輸出數(shù)據(jù)可以簡(jiǎn)單的從?個(gè)階段傳遞到下一個(gè)階段,也可以?不同的方式插入。
      • 客戶端的代碼接觸不到這些內(nèi)部變量我們的OpenGL開發(fā)暫時(shí)接觸不到。
  • 創(chuàng)建坐標(biāo)系
    • 正投影圖


      正投影圖.png
    • 這就是一個(gè)正投影的例子,在3個(gè)軸(X,Y,Z)中,它們的范圍都是-100到+100.這個(gè)視景體將包括所有的幾何圖形。

    • 如果你指定了視景體外的幾何圖形,就會(huì)被裁減掉!(它將沿著視景體的邊界進(jìn)行剪切)

    • 在正投影中,所有在這個(gè)空間范圍內(nèi)的所有東西都將被呈現(xiàn)在屏幕上。?不存在照相機(jī)或視點(diǎn)坐標(biāo)系的概念。

    • 透視投影


      透視投影.png

      透視投影會(huì)進(jìn)行透視除法對(duì)距離觀察者很遠(yuǎn)的對(duì)象進(jìn)行縮短和收縮。在投影到屏幕之后,視景體背?與視景體正面的寬度測(cè)量標(biāo)準(zhǔn)不同。

      • 上圖所示:平截頭體(frustum)的幾何體,它的觀察方向是從金字塔的尖端到寬闊端。觀察著的視點(diǎn)與金字塔的尖端拉開一定距離。
           GLFrustum類通過setPerspective ?方法為我們構(gòu)建?個(gè)平截頭體。 
           CLFrustum::SetPerspective(float fFov,float fAspect,float fNear ,float fFar);
           參數(shù):
               fFov:垂直?向上的視場(chǎng)?度 
               fAspect:窗口的寬度與高度的縱橫比     
               fNear:近裁剪?距離 (視角到近裁剪面距離為fNear)
               fFar:遠(yuǎn)裁剪面距離(視角到遠(yuǎn)裁剪面距離為fFar)
               縱橫比 = 寬(w)/?(h)
        
  • 使用儲(chǔ)存著色器
    • 使用背景
      • 在OpenGL 核?框架中,并沒有提供任何內(nèi)建渲染管線,在提交?個(gè)?何圖形進(jìn)行渲染之前,必須實(shí)現(xiàn)?個(gè)著色?。 在前面的課程可以使用存儲(chǔ)著??。這些存儲(chǔ)著??由GLTools的C++類 GLShaderManager管理。它們能夠滿足進(jìn)行基本渲染的基本要求。要求不高的程序員,這些存儲(chǔ)著??已經(jīng)?以滿?他們的需求。但是,隨著時(shí)間 和經(jīng)驗(yàn)的提升,?部分開發(fā)者可能不滿?于此。 會(huì)開始?己著手去寫著色?。
      • 儲(chǔ)存著色器的使用
        • GLShaderManager 的初始化

              // GLShaderManager 的初始化
              GLShaderManager shaderManager; 
              shaderManager.InitializeStockShaders();
          
        • GLShaderManager 屬性

          GLShaderManager 屬性.png

          存儲(chǔ)著??為每?個(gè)變量都使?一致的內(nèi)部變量命名規(guī)和相同的屬性槽。以上就是存儲(chǔ)著??的屬性列表

        • GLShanderManageruniform

          • ?般情況,要對(duì)?何圖形進(jìn)?渲染,我們需要給對(duì)象遞交屬性矩陣,?先要綁定我們想要使?的著?程序上,并提供程序的uniform值。但是GLShanderManager 類可以暫時(shí)為我們完成工作。
          • useStockShader 函數(shù)會(huì)選擇?個(gè)存儲(chǔ)著?器并提供這個(gè)著??的uniform值。
              GLShaderManager::UserStockShader(GLeunm shader...);
          
        • 單位(Identity 著??)

              GLShaderManager::UserStockShader(GLT_ATTRIBUTE_VERTEX,GLfloat vColor[4]);
          
          • 單位著??:只是簡(jiǎn)單地使用默認(rèn)笛卡爾坐標(biāo)系(坐標(biāo)范圍(-1.0, 1.0))。
          • 所有的?段都應(yīng)用同?種顏?,?何圖形為實(shí)?和未渲染的。
          • 需要設(shè)置存儲(chǔ)著色器?個(gè)屬性: GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量)
          • 參數(shù)2:vColor[4],你需要的顏色
        • 平面著色器

              GLShaderManager::UserStockShader(GLT_SHADER_FLAT,GLfloat mvp[16],GLfloat vColor[4]);
          
          • GLT_SHADER_FLAT:平面著色器
          • mvp[16]:允許變化的4*4矩陣
          • 顏色:vColor[4]
          • 它將統(tǒng)一著?色器?進(jìn)行了拓展。允許為?何圖形變換指定一個(gè) 4 * 4 變換矩陣。經(jīng)常被稱為模型視圖投影矩陣
        • 上色著色器

          • 在?何圖形中應(yīng)用的變換矩陣。需要設(shè)置存儲(chǔ)著??的 GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量) 和GLT_ATTRIBUTE_COLOR(顏?分量) 2個(gè)屬性。顏色值將被平滑地插?頂點(diǎn)之間(平滑著?)
          GLShaderManager::UserStockShader(GLT_SHADER_SHADED,GLfloat mvp
          [16]);
          
          
        • 默認(rèn)光源著色器

          GLShaderManager::UserStockShader(GLT_SHADER_DEFAULT_LIGHT,GLfl
          oat mvMatrix[16],GLfloat pMatrix[16],GLfloat vColor[4]);
          
          
          • GLT_SHADER_DEFAULT_LIGHT: 默認(rèn)光源著??
          • mvMatrix[16]: 模型視圖矩陣
          • pMatrix[16]: 投影矩陣
          • vColor[4]: 顏?值
        • 點(diǎn)光源著??

          LShaderManager::UserStockShader(GLT_SHADER_DEFAULT_LIGHT_DIEF,GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vLightPos[3],GLfloat vColor[4]);
          
          
          • 參數(shù)1:點(diǎn)光源著??
          • 參數(shù)2:模型視圖矩陣
          • 參數(shù)3:投影矩陣
          • 參數(shù)4:視點(diǎn)坐標(biāo)光源位置
          • 參數(shù)5:顏?值
          • 區(qū)別:點(diǎn)光源著??和默認(rèn)光源著??很相似,區(qū)別在于:光源位置是特定的。 同樣需要設(shè)置存儲(chǔ)著??的 GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量) 和
            GLT_ATTRIBUTE_NORMAL(表?法線)
        • 紋理理替換矩陣

          • 著色器通過給定的模型視圖投影矩陣,使?綁定到 nTextureUnit (紋理單元) 指定紋理單元的紋理對(duì)?何圖形進(jìn)行變化。片段顏色:是直接從紋理樣本中直接獲取的。
            需要設(shè)置存儲(chǔ)著??的 GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量) 和
            GLT_ATTRIBUTE_NORMAL(表?法線)
            GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_REPLACE,GLfloat mvMatrix[16],GLint nTextureUnit);
            
            
        • 紋理調(diào)整著??

          • 將?個(gè)基本色乘以一個(gè)取自紋理單元 nTextureUnit 的紋理。需要設(shè)置存儲(chǔ)著??的 GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量)
            GLT_ATTRIBUTE_TEXTURE0(紋理坐標(biāo)).
                GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_MODULATE,GLfloat mvMatrix[16],GLfloat vColor[4],GLint nTextureUnit);
                
            
        • 紋理光源著??

          • 將?個(gè)紋理通過漫反射照明計(jì)算機(jī)進(jìn)?調(diào)整(相乘)。光線在視覺空間中 的位置是給定的。需要設(shè)置存儲(chǔ)著色?的 GLT_ATTRIBUTE_VERTEX(頂點(diǎn)分量)
            GLT_ATTRIBUTE_TEXTURE0(紋理坐標(biāo))、GLT_ATTRIBUTE_NORMAL(表面法線)
                GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIEF,GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vLightPos[3],GLfloat vBaseColor[4],GLint nTextureUnit);
            
            
          • 參數(shù)1:紋理理光源著??
          • 參數(shù)2:投影矩陣
          • 參數(shù)3:視覺空間中的光源位置
          • 參數(shù)4:?何圖形的基本色
          • 參數(shù)5:將要使?的紋理單元
      • 點(diǎn)鏈接
        • 第一次學(xué)習(xí)任何計(jì)算機(jī)系統(tǒng)中繪制任何類型的 2 D 圖形時(shí),大多數(shù)可能從繪制像素開始。像素是計(jì)算機(jī)屏幕上顯示的最小元素。以下是最簡(jiǎn)單額計(jì)算機(jī)圖形:在計(jì)算機(jī)屏幕繪制一個(gè)點(diǎn),并將它設(shè)置一個(gè)特定的顏色。在這個(gè)簡(jiǎn)單的基礎(chǔ)。上慢慢學(xué)會(huì)創(chuàng)建線、多邊形、圓和其他性質(zhì)和圖形。但是!使用 OpenGL 在計(jì)算機(jī)屏幕上進(jìn)行繪圖則完全不同。我們不關(guān)物理屏幕坐標(biāo)和像素,關(guān)注的是視景體中的位置坐標(biāo)。將這些點(diǎn)、線和三角形從創(chuàng)建 3 D 空間投影到計(jì)算機(jī)屏幕上的 2 D 圖形則是著色器程序和光柵化硬件所要完成的工作。

        • 點(diǎn)和線

          • 今天我們將從更底層更基礎(chǔ)的角度來詳細(xì)學(xué)習(xí)OpenGL圖元渲染。點(diǎn),是最簡(jiǎn)單的圖像。每個(gè)特定的頂點(diǎn)在屏幕上都僅僅是一個(gè)單獨(dú)的點(diǎn)。默認(rèn)的情況下,點(diǎn)的大小是一個(gè)像素的大小。
          • 修改點(diǎn)大小的方法:
             //1.最簡(jiǎn)單也是最常用的4.0f ,表示點(diǎn)的大小
             glPointSize(4.0f) ;
             //2.設(shè)置點(diǎn)的大小范圍和點(diǎn)與點(diǎn)之間的間隔
             GLfloat sizes[2] = {2.0f,4.0f};
             GLfloat step = 1.0f;
             //獲取點(diǎn)大小范圍和最小步長(zhǎng)
             glGetFloatv(GL_POINT_SIZE_RANGE,sizes);
             glGetFloatv(GL_ POINT_GRAULARITY ,&step);
             //3.通過使用程序點(diǎn)大小模式來設(shè)置點(diǎn)大小
             glEnable (GL_ PROGRAM_ POINT_SIZE) ;
             //這種模式下允許我們]通過編程在頂點(diǎn)著器或幾何著色器中設(shè)置點(diǎn)大小。著色器內(nèi)建變量: gl_ PointSize,并且可以在器源碼直接寫
             gl_PointSize = 5.0;
            
            
          • 比點(diǎn)更進(jìn)一步的就是獨(dú)立線段了。一個(gè)線段就是2個(gè)頂點(diǎn)之間繪制的。
            默認(rèn)情況下,線段的寬度是一個(gè)像素。改變線段唯一的方式通過:
              //1.設(shè)置獨(dú)立線段寬度為2.5f;
              glLineWidth(2.5f);
          
        • 線環(huán)

          • 線環(huán)是線帶的一種簡(jiǎn)單拓展。在線帶的基礎(chǔ)上額外增加一條線帶閉合的。
        • 繪制三角形

          • 最簡(jiǎn)單的實(shí)體多邊形就是三角形,它只有3個(gè)邊。光柵化硬件最歡迎三角形。并且現(xiàn)在OpenGL已經(jīng)是OpenGL中支持的唯一種多邊形。每3個(gè)頂點(diǎn)定義一個(gè)新的三角形。


            三角形.png
          • 在本次的學(xué)習(xí)中,我們不僅繪制一個(gè)三角形,更繪制4個(gè)三角形組成金字塔形的三角形。我們可以使用方向鍵來旋轉(zhuǎn)金字塔,從不同角度進(jìn)行觀察。但是這個(gè)金字塔沒有底面,所以我們可以看到它的底部。


            image.png
        • 環(huán)繞

          • 將順時(shí)針方向繪制的三角形用逆時(shí)針的方式繪制。在繪制第一個(gè)三角形時(shí),線條是按照從VO-V1,再到V2。最后再回到VO的個(gè)閉合三角形。這個(gè)是沿著頂點(diǎn)順時(shí)針方向。這種順序與方向結(jié)合來指定頂點(diǎn)的方式稱為環(huán)繞。


            image.png
          • 上圖的2個(gè)三角形的纏繞方向完全相反。

          • 在默認(rèn)的情況下,OpenGL認(rèn)為具有逆時(shí)針方向環(huán)繞的多邊形是正面的。而右側(cè)的順時(shí)針方向三角形是三角形的背面。

          • 為什么會(huì)認(rèn)為這個(gè)問題會(huì)很重要了?因?yàn)槲覀兂3OM麨橐粋€(gè)多邊形的正面和背面分別設(shè)置不同的物理特征。我們可以完全隱藏一個(gè)多邊形的背面,或者給它設(shè)置一種不同的顏色和反射屬性。紋理圖像在背面三角形中也是相反的。在一個(gè)場(chǎng)景中,使所有的多邊形保持環(huán)繞方向的一致,并使用正面多邊形來繪制所有實(shí)心物體的表面是非常重要的。

            //定義前向和背向的多變形: 
            glF rontFace (mode)
            參數(shù): GL_CW | GL_CCW
            GL_CCW: 表示傳入的mode會(huì)選擇逆時(shí)針為前向
            GL_CW: 表示順時(shí)針為前向。
            默認(rèn): GL_CCW。逆向時(shí)針為前向。
            
            
          • 三角地帶


            image.png
            • 對(duì)于很多表面和形狀來說,我們可能需要繪制幾個(gè)相連的三角形。我們可以使用GL_TRIANGLE_STRIP圖元繪制一串相連的三角形。從而節(jié)省大量的時(shí)間。使用三角帶而不是分別指定每個(gè)三角形,這樣做的優(yōu)點(diǎn):
              • 1.用前3個(gè)頂點(diǎn)指定第1個(gè)三角形之后,對(duì)于接下來的每一個(gè)三角形,只需要再指定1個(gè)頂點(diǎn)。需要繪制大量的三角形時(shí),采用這種方法可以節(jié)省大量的程序代碼和數(shù)據(jù)存儲(chǔ)空間。
              • 2.提供運(yùn)算性能和節(jié)省帶寬。更少的頂點(diǎn)意味著數(shù)據(jù)從內(nèi)存?zhèn)鬏數(shù)綀D形卡的速度更快,并且頂點(diǎn)著色器需要處理的次數(shù)也更少了。


                image.png
        • 簡(jiǎn)單批次容器

          • GLTools庫中包含額一個(gè)簡(jiǎn)單的容器類,叫做GBatch。這個(gè)類可以作為7種圖元的簡(jiǎn)單批次容器使用。而且它知道在使用GL_ ShaderManager 支持的任意存儲(chǔ)著色器時(shí)如何對(duì)圖元進(jìn)行渲染。
            void GLBatch: :Begain(GLeunm primitive,GLuint nVerts,GLuint nTe
            xttureUnints = 0) ;
            參數(shù)1:圖元
            參數(shù)2:頂點(diǎn)數(shù)
            參數(shù)3:一組或者2組紋理坐標(biāo)(可選)
            // 復(fù)制表面法線
            void GLBatch:: CopyNormalDataf(GLfloat *vNorms) ;
            //復(fù)制顏色
            void GLBatch:: CopyColorData4f(GLfloat *vColors) ;
            //復(fù)制紋理坐標(biāo)
            void GLBatch::CopyTexCoordData2f(GLFloat *vTextCoords , GLuint uiTextureLayer);
            // 結(jié)束繪制
            void GLBatch::End(void);
            

源碼實(shí)例

/*
 實(shí)現(xiàn)功能:
    點(diǎn)擊屏幕,將固定位置上的頂點(diǎn)數(shù)據(jù)以6種不同形態(tài)展示!
 */

#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"

#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

/*
 GLMatrixStack 變化管線使用矩陣堆棧
 
 GLMatrixStack 構(gòu)造函數(shù)允許指定堆棧的最大深度、默認(rèn)的堆棧深度為64.這個(gè)矩陣堆在初始化時(shí)已經(jīng)在堆棧中包含了單位矩陣。
 GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
 
 //通過調(diào)用頂部載入這個(gè)單位矩陣
 void GLMatrixStack::LoadIndentiy(void);
 
 //在堆棧頂部載入任何矩陣
 void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
 */
// 各種需要的類
GLShaderManager     shaderManager;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLFrame             cameraFrame;//觀察者位置
GLFrame             objectFrame;//被觀察者位置

//投影矩陣
GLFrustum           viewFrustum;

//容器類(7種不同的圖元對(duì)應(yīng)7種容器對(duì)象)
GLBatch             pointBatch; // 點(diǎn)
GLBatch             lineBatch; // 線
GLBatch             lineStripBatch; // 線段
GLBatch             lineLoopBatch;//線環(huán)
GLBatch             triangleBatch;
GLBatch             triangleStripBatch;
GLBatch             triangleFanBatch;

//幾何變換的管道
GLGeometryTransform transformPipeline;
M3DMatrix44f        shadowMatrix;


GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };


// 跟蹤效果步驟
int nStep = 0;



// 此函數(shù)在呈現(xiàn)上下文中進(jìn)行任何必要的初始化。.
// 這是第一次做任何與opengl相關(guān)的任務(wù)。
void SetupRC()
{
    //灰色背景
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
    //初始化
    shaderManager.InitializeStockShaders();
    // 開啟深度測(cè)試
    glEnable(GL_DEPTH_TEST);
    
    //設(shè)置變換管線以使用兩個(gè)矩陣堆棧
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    
    //觀察者移動(dòng)位置
    cameraFrame.MoveForward(-20.0f);
    
    /*
     常見函數(shù):
     void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0);
     參數(shù)1:表示使用的圖元
     參數(shù)2:頂點(diǎn)數(shù)
     參數(shù)3:紋理坐標(biāo)(可選)
     
     //負(fù)責(zé)頂點(diǎn)坐標(biāo)
     void GLBatch::CopyVertexData3f(GLFloat *vNorms);
     
     //結(jié)束,表示已經(jīng)完成數(shù)據(jù)復(fù)制工作
     void GLBatch::End(void);
     
     
     */
   //定義一些點(diǎn),類似佛羅里達(dá)州的形狀。
    GLfloat vCoast[24][3] = {
        {2.80, 1.20, 0.0 }, {2.0,  1.20, 0.0 },
        {2.0,  1.08, 0.0 },  {2.0,  1.08, 0.0 },
        {0.0,  0.80, 0.0 },  {-.32, 0.40, 0.0 },
        {-.48, 0.2, 0.0 },   {-.40, 0.0, 0.0 },
        {-.60, -.40, 0.0 },  {-.80, -.80, 0.0 },
        {-.80, -1.4, 0.0 },  {-.40, -1.60, 0.0 },
        {0.0, -1.20, 0.0 },  { .2, -.80, 0.0 },
        {.48, -.40, 0.0 },   {.52, -.20, 0.0 },
        {.48,  .20, 0.0 },   {.80,  .40, 0.0 },
        {1.20, .80, 0.0 },   {1.60, .60, 0.0 },
        {2.0, .60, 0.0 },    {2.2, .80, 0.0 },
        {2.40, 1.0, 0.0 },   {2.80, 1.0, 0.0 }
    };
    // 用點(diǎn)的形式--表示佛羅里達(dá)州的形狀
    pointBatch.Begin(GL_POINTS, 24);
    pointBatch.CopyVertexData3f(vCoast);
    pointBatch.End();
    
    //通過線的形式--表示佛羅里達(dá)州的形狀
    lineBatch.Begin(GL_LINES, 24);
    lineBatch.CopyVertexData3f(vCoast);
    lineBatch.End();
    
    //通過線段的形式表示表示佛羅里達(dá)州的形狀
    lineStripBatch.Begin(GL_LINE_STRIP, 24);
    lineStripBatch.CopyVertexData3f(vCoast);
    lineStripBatch.End();
    
    //通過線環(huán)的形式表示佛羅里達(dá)州的形狀
    lineLoopBatch.Begin(GL_LINE_LOOP, 24);
    lineLoopBatch.CopyVertexData3f(vCoast);
    lineLoopBatch.End();
    
    //通過三角形逆時(shí)針創(chuàng)建金字塔
    GLfloat vPyramid[12][3] = {
        -2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f,
        
        2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,
        
        2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,
        
        -2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f,
    };
    
    // GL_TRIANGLES 每3個(gè)頂點(diǎn)定義一個(gè)新的三角形
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
    
    //三角形扇形--六邊形
    GLfloat vPoints[100][3]; // 超過我們需要的數(shù)組
    int nVerts = 0;
    //半徑
    GLfloat r = 3.0f;
    
    //原點(diǎn)(x,y,z); = (0, 0, 0)
    vPoints[nVerts][0] = 0.0f;
    vPoints[nVerts][1] = 0.0f;
    vPoints[nVerts][2] = 0.0f;
    
    //M3D_2PI 就是2Pi 的意思,就一個(gè)圓的意思。 繪制圓形
    
    for (GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0) {
        
        //數(shù)組下標(biāo)自增(每自增1次就表示一個(gè)頂點(diǎn))
        nVerts++;
        /*
         弧長(zhǎng)=半徑*角度,這里的角度是弧度制,不是平時(shí)的角度制
         既然知道了cos值,那么角度=arccos,求一個(gè)反三角函數(shù)就行了
         */
        //x點(diǎn)坐標(biāo) = cos(angle) * 半徑
        vPoints[nVerts][0] = float(cos(angle)) * r;
        //y點(diǎn)坐標(biāo) = sin(angle) * 半徑
        vPoints[nVerts][1] = float(sin(angle)) * r;
        //z 點(diǎn)的坐標(biāo)
        vPoints[nVerts][2] = -0.5f;
        
    }
    
    //結(jié)束扇形 前面一共繪制7個(gè)頂點(diǎn)(包括圓心)
    printf("三角形扇形頂點(diǎn)數(shù):%d\n", nVerts);
    //添加閉合的終點(diǎn)
    nVerts++;
    vPoints[nVerts][0] = r;
    vPoints[nVerts][1] = 0.0f;
    vPoints[nVerts][2] = 0.0f;
   
    //加載!
    //GL_TRIANGLE_FAN 以一個(gè)圓心為中心呈扇形排列,共用相鄰頂點(diǎn)的一組三角形
    triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
    triangleFanBatch.CopyVertexData3f(vPoints);
    triangleFanBatch.End();
    
    // 三角形條帶,一個(gè)小環(huán)或圓柱段
    // 頂點(diǎn)下標(biāo)
    int iCounter = 0;
    //半徑
    GLfloat radius = 3.0f;
    //從0度~360度,以0.3弧度為步長(zhǎng)
    for (GLfloat angle = 0.0f; angle <= (2.0*M3D_PI); angle += 0.3f) {
        
        //獲取圓形的頂點(diǎn)x,y
        GLfloat x = radius * sin(angle);
        GLfloat y = radius * cos(angle);
        
        // 繪制2個(gè)三角形(他們的x,y頂點(diǎn)一樣,只是z點(diǎn)不一樣)
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = -0.5;
        iCounter++;
        
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = 0.5;
        iCounter++;
    }
    
    //關(guān)閉循環(huán)
    printf("三角形帶的頂點(diǎn)數(shù):%d\n", iCounter);
    
    //結(jié)束循環(huán),在循環(huán)位置生成2個(gè)三角形
    vPoints[iCounter][0] = vPoints[0][0];
    vPoints[iCounter][1] = vPoints[0][1];
    vPoints[iCounter][2] = -0.5;
    iCounter++;
    
    vPoints[iCounter][0] = vPoints[1][0];
    vPoints[iCounter][1] = vPoints[1][1];
    vPoints[iCounter][2] = 0.5;
    iCounter++;
    
    // GL_TRIANGLE_STRIP 共用一個(gè)條帶(strip)上的頂點(diǎn)的一組三角形
    triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
    triangleStripBatch.CopyVertexData3f(vPoints);
    triangleStripBatch.End();
}



void DrawWireFramedBatch(GLBatch* pBatch)
{
    /*------------畫綠色部分----------------*/
    /* GLShaderManager 中的Uniform 值——平面著色器
     參數(shù)1:平面著色器
     參數(shù)2:運(yùn)行為幾何圖形變換指定一個(gè) 4 * 4變換矩陣
     --transformPipeline 變換管線(指定了2個(gè)矩陣堆棧)
     參數(shù)3:顏色值
     */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();
 
    /*-----------邊框部分-------------------*/
    /*
     glEnable(GLenum mode); 用于啟用各種功能。功能由參數(shù)決定
     參數(shù)列表:http://blog.csdn.net/augusdi/article/details/23747081
     注意:glEnable() 不能寫在glBegin() 和 glEnd()中間
     GL_POLYGON_OFFSET_LINE  根據(jù)函數(shù)glPolygonOffset的設(shè)置,啟用線的深度偏移
     GL_LINE_SMOOTH          執(zhí)行后,過慮線點(diǎn)的鋸齒
     GL_BLEND                啟用顏色混合。例如實(shí)現(xiàn)半透明效果
     GL_DEPTH_TEST           啟用深度測(cè)試 根據(jù)坐標(biāo)的遠(yuǎn)近自動(dòng)隱藏被遮住的圖形(材料
     
     
     glDisable(GLenum mode); 用于關(guān)閉指定的功能 功能由參數(shù)決定
     
     */
      //畫黑色邊框
    glPolygonOffset(-1.0f, -1.0f); // 偏移深度,在同一位置要繪制填充和邊線,會(huì)產(chǎn)生z沖突,所以要偏移
    glEnable(GL_POLYGON_OFFSET_LINE);
    
    // 畫反鋸齒。讓黑邊好看些
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    //繪制線框幾何黑色版 三種模式,實(shí)心,邊框,點(diǎn),可以作用在正面,背面,或者兩面
    //通過調(diào)用glPolygonMode將多邊形正面或者背面設(shè)為線框模式,實(shí)現(xiàn)線框渲染
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    
    //設(shè)置線條寬度
    glLineWidth(2.5f);
    
    /* GLShaderManager 中的Uniform 值——平面著色器
     參數(shù)1:平面著色器
     參數(shù)2:運(yùn)行為幾何圖形變換指定一個(gè) 4 * 4變換矩陣
     --transformPipeline.GetModelViewProjectionMatrix() 獲取的
     GetMatrix函數(shù)就可以獲得矩陣堆棧頂部的值
     參數(shù)3:顏色值(黑色)
     */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();
    
    // 復(fù)原原本的設(shè)置
    ////通過調(diào)用glPolygonMode將多邊形正面或者背面設(shè)為全部填充模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
}



// 召喚場(chǎng)景
void RenderScene(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //壓棧
    modelViewMatrix.PushMatrix();
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    
    //矩陣乘以矩陣堆棧的頂部矩陣,相乘的結(jié)果隨后簡(jiǎn)存儲(chǔ)在堆棧的頂部
    modelViewMatrix.MultMatrix(mCamera);
    
    M3DMatrix44f mObjectFrame;
    //只要使用 GetMatrix 函數(shù)就可以獲取矩陣堆棧頂部的值,這個(gè)函數(shù)可以進(jìn)行2次重載。用來使用GLShaderManager 的使用?;蛘呤谦@取頂部矩陣的頂點(diǎn)副本數(shù)據(jù)
    objectFrame.GetMatrix(mObjectFrame);
    
    //矩陣乘以矩陣堆棧的頂部矩陣,相乘的結(jié)果隨后簡(jiǎn)儲(chǔ)存在堆棧的頂部
    modelViewMatrix.MultMatrix(mObjectFrame);
    
    /* GLShaderManager 中的Uniform 值——平面著色器
     參數(shù)1:平面著色器
     參數(shù)2:運(yùn)行為幾何圖形變換指定一個(gè) 4 * 4變換矩陣
     --transformPipeline.GetModelViewProjectionMatrix() 獲取的
     GetMatrix函數(shù)就可以獲得矩陣堆棧頂部的值
     參數(shù)3:顏色值(黑色)
     */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    
    //根據(jù)nStep值來表示
    switch (nStep) {
        case 0:
            //設(shè)置點(diǎn)的大小
            glPointSize(4.0f);
            pointBatch.Draw();
            //畫完之后,避免影響到其他的設(shè)置,所以要設(shè)置恢復(fù)默認(rèn)值
            glPointSize(1.0f);
            break;
        case 1:
            //設(shè)置線的寬度
            glLineWidth(2.0f);
            lineBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 2: //線段
            glLineWidth(2.0f);
            lineStripBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 3: //線環(huán)
            glLineWidth(2.0f);
            lineLoopBatch.Draw();
            glLineWidth(1.0f);
            break;
        case 4:
            DrawWireFramedBatch(&triangleBatch);
            break;
        case 5:
            DrawWireFramedBatch(&triangleStripBatch);
            break;
        case 6:
            DrawWireFramedBatch(&triangleFanBatch);
            break;
    }
    
    //還原到以前的模型視圖矩陣(單位矩陣)
    modelViewMatrix.PopMatrix();
    
    //進(jìn)行緩沖區(qū)交換
    glutSwapBuffers();
}


//特殊鍵位處理(上、下、左、右移動(dòng))
void SpecialKeys(int key, int x, int y)
{
    // 圍繞一個(gè)指定的X,Y,Z軸旋轉(zhuǎn)
    /**
     RotateWorld(float fAngle, float x, float y, float z)
     x,y,z中的值,1表示YES,0表示NO來判斷指定哪個(gè)軸旋轉(zhuǎn)
     */
    if (key == GLUT_KEY_UP) {

        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
    }
    
    if (key == GLUT_KEY_DOWN) {
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
    }
    
    if (key == GLUT_KEY_LEFT) {
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
    }
    
    if (key == GLUT_KEY_RIGHT) {
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
    }
    
    glutPostRedisplay();
}




//根據(jù)空格次數(shù)。切換不同的“窗口名稱”
void KeyPressFunc(unsigned char key, int x, int y)
{
    //判斷key的次數(shù)
    if (key == 32) {
        
        nStep++;
        if (nStep > 6) {
            nStep = 0;
        }
    }

    //switch 判斷顯示具體操作
    switch (nStep) {
        case 0:
            glutSetWindowTitle("GL_POINTS");
            break;
        case 1:
            glutSetWindowTitle("GL_LINES");
            break;
        case 2:
            glutSetWindowTitle("GL_LINE_STRIP");
            break;
        case 3:
            glutSetWindowTitle("GL_LINE_LOOP");
            break;
        case 4:
            glutSetWindowTitle("GL_TRIANGLES");
            break;
        case 5:
            glutSetWindowTitle("GL_TRIANGLE_STRIP");
            break;
        case 6:
            glutSetWindowTitle("GL_TRIANGLE_FAN");
            break;
    }
    
    
    //重新繪制
    glutPostRedisplay();

}


// 窗口已更改大小,或剛剛創(chuàng)建。無論哪種情況,我們都需要
// 使用窗口維度設(shè)置視口和投影矩陣.
void ChangeSize(int w, int h)
{
    // 窗口大小
    glViewport(0, 0, w, h);
    //創(chuàng)建投影矩陣,并將它載入投影矩陣堆棧中
    //創(chuàng)建投影矩陣,并將它載入投影矩陣堆棧中
    /**
     @params    fFov        角度
     @params    fAspect    縱橫比(寬高比)
     @params    fNear
     @params    fFar
     */
    viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0, 500.0f);
    //投影矩陣
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //調(diào)用頂部載入單元矩陣:因?yàn)椴挥米鍪裁茨P妥兓?,所以用單元矩陣就行?    modelViewMatrix.LoadIdentity();
}


int main(int argc, char* argv[])
{
    
    ////設(shè)置工作路徑
    gltSetWorkingDirectory(argv[0]);
    //初始化
    glutInit(&argc, argv);
    //申請(qǐng)一個(gè)顏色緩存區(qū)、深度緩存區(qū)、雙緩存區(qū)、模板緩存區(qū)
    /**
     GLUT_DOUBLE    雙緩存區(qū)
     GLUT_RGBA      顏色緩存區(qū)
     GLUT_DEPTH     深度緩存區(qū)
     GLUT_STENCIL   模板緩存區(qū)
     */
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //設(shè)置window 的尺寸
    glutInitWindowSize(800, 600);
    //創(chuàng)建window的名稱
    glutCreateWindow("GL_Point");
    //注冊(cè)回調(diào)函數(shù)(改變尺寸)
    glutReshapeFunc(ChangeSize);
    //點(diǎn)擊空格時(shí),調(diào)用的函數(shù)
    glutKeyboardFunc(KeyPressFunc);
    //特殊鍵位函數(shù)(上下左右)
    glutSpecialFunc(SpecialKeys);
    //顯示函數(shù)
    glutDisplayFunc(RenderScene);
    //判斷一下是否能初始化glew庫,確保項(xiàng)目能正常使用OpenGL 框架
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    //繪制
    SetupRC();
    //runloop運(yùn)行循環(huán)
    glutMainLoop();
    return 0;
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 本文首發(fā)于個(gè)人博客:Lam's Blog - 【OpenGL-ES】二維紋理,文章由MarkDown語法編寫,可能...
    格子林ll閱讀 3,958評(píng)論 0 9
  • opengl opengl渲染管線基礎(chǔ)架構(gòu) 3.1 基礎(chǔ)圖形管線 3.1.1 客戶機(jī)-服務(wù)器 就opengl而言,...
    plsour閱讀 926評(píng)論 0 0
  • 渲染管線 蘋果提供了兩種OpenGL ES的可視化模型,一種是客戶端—服務(wù)端的架構(gòu)模型,另一種就是管線的模型。 客...
    sellse閱讀 12,407評(píng)論 1 10
  • 1.foldr foldr::(a->b->b)->b->[a]->b foldr函數(shù)應(yīng)用在list上,它的功能就...
    b7c9833ccf70閱讀 479評(píng)論 0 0

友情鏈接更多精彩內(nèi)容