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

學(xué)習(xí)目標:

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

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

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


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

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

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

    • 透視投影


      透視投影.png

      透視投影會進行透視除法對距離觀察者很遠的對象進行縮短和收縮。在投影到屏幕之后,視景體背?與視景體正面的寬度測量標準不同。

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

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

          GLShaderManager 屬性.png

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

        • GLShanderManageruniform

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

              GLShaderManager::UserStockShader(GLT_ATTRIBUTE_VERTEX,GLfloat vColor[4]);
          
          • 單位著??:只是簡單地使用默認笛卡爾坐標系(坐標范圍(-1.0, 1.0))。
          • 所有的?段都應(yīng)用同?種顏?,?何圖形為實?和未渲染的。
          • 需要設(shè)置存儲著色器?個屬性: GLT_ATTRIBUTE_VERTEX(頂點分量)
          • 參數(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)一著?色器?進行了拓展。允許為?何圖形變換指定一個 4 * 4 變換矩陣。經(jīng)常被稱為模型視圖投影矩陣
        • 上色著色器

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

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

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

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

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

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

        • 點和線

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

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

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


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


            image.png
        • 環(huán)繞

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


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

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

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

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


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


                image.png
        • 簡單批次容器

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

源碼實例

/*
 實現(xià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ù)允許指定堆棧的最大深度、默認的堆棧深度為64.這個矩陣堆在初始化時已經(jīng)在堆棧中包含了單位矩陣。
 GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
 
 //通過調(diào)用頂部載入這個單位矩陣
 void GLMatrixStack::LoadIndentiy(void);
 
 //在堆棧頂部載入任何矩陣
 void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
 */
// 各種需要的類
GLShaderManager     shaderManager;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLFrame             cameraFrame;//觀察者位置
GLFrame             objectFrame;//被觀察者位置

//投影矩陣
GLFrustum           viewFrustum;

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



void DrawWireFramedBatch(GLBatch* pBatch)
{
    /*------------畫綠色部分----------------*/
    /* GLShaderManager 中的Uniform 值——平面著色器
     參數(shù)1:平面著色器
     參數(shù)2:運行為幾何圖形變換指定一個 4 * 4變換矩陣
     --transformPipeline 變換管線(指定了2個矩陣堆棧)
     參數(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í)行后,過慮線點的鋸齒
     GL_BLEND                啟用顏色混合。例如實現(xiàn)半透明效果
     GL_DEPTH_TEST           啟用深度測試 根據(jù)坐標的遠近自動隱藏被遮住的圖形(材料
     
     
     glDisable(GLenum mode); 用于關(guān)閉指定的功能 功能由參數(shù)決定
     
     */
      //畫黑色邊框
    glPolygonOffset(-1.0f, -1.0f); // 偏移深度,在同一位置要繪制填充和邊線,會產(chǎn)生z沖突,所以要偏移
    glEnable(GL_POLYGON_OFFSET_LINE);
    
    // 畫反鋸齒。讓黑邊好看些
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    //繪制線框幾何黑色版 三種模式,實心,邊框,點,可以作用在正面,背面,或者兩面
    //通過調(diào)用glPolygonMode將多邊形正面或者背面設(shè)為線框模式,實現(xiàn)線框渲染
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    
    //設(shè)置線條寬度
    glLineWidth(2.5f);
    
    /* GLShaderManager 中的Uniform 值——平面著色器
     參數(shù)1:平面著色器
     參數(shù)2:運行為幾何圖形變換指定一個 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);
}



// 召喚場景
void RenderScene(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    //壓棧
    modelViewMatrix.PushMatrix();
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    
    //矩陣乘以矩陣堆棧的頂部矩陣,相乘的結(jié)果隨后簡存儲在堆棧的頂部
    modelViewMatrix.MultMatrix(mCamera);
    
    M3DMatrix44f mObjectFrame;
    //只要使用 GetMatrix 函數(shù)就可以獲取矩陣堆棧頂部的值,這個函數(shù)可以進行2次重載。用來使用GLShaderManager 的使用。或者是獲取頂部矩陣的頂點副本數(shù)據(jù)
    objectFrame.GetMatrix(mObjectFrame);
    
    //矩陣乘以矩陣堆棧的頂部矩陣,相乘的結(jié)果隨后簡儲存在堆棧的頂部
    modelViewMatrix.MultMatrix(mObjectFrame);
    
    /* GLShaderManager 中的Uniform 值——平面著色器
     參數(shù)1:平面著色器
     參數(shù)2:運行為幾何圖形變換指定一個 4 * 4變換矩陣
     --transformPipeline.GetModelViewProjectionMatrix() 獲取的
     GetMatrix函數(shù)就可以獲得矩陣堆棧頂部的值
     參數(shù)3:顏色值(黑色)
     */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    
    //根據(jù)nStep值來表示
    switch (nStep) {
        case 0:
            //設(shè)置點的大小
            glPointSize(4.0f);
            pointBatch.Draw();
            //畫完之后,避免影響到其他的設(shè)置,所以要設(shè)置恢復(fù)默認值
            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();
    
    //進行緩沖區(qū)交換
    glutSwapBuffers();
}


//特殊鍵位處理(上、下、左、右移動)
void SpecialKeys(int key, int x, int y)
{
    // 圍繞一個指定的X,Y,Z軸旋轉(zhuǎn)
    /**
     RotateWorld(float fAngle, float x, float y, float z)
     x,y,z中的值,1表示YES,0表示NO來判斷指定哪個軸旋轉(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)用頂部載入單元矩陣:因為不用做什么模型變化,所以用單元矩陣就行了
    modelViewMatrix.LoadIdentity();
}


int main(int argc, char* argv[])
{
    
    ////設(shè)置工作路徑
    gltSetWorkingDirectory(argv[0]);
    //初始化
    glutInit(&argc, argv);
    //申請一個顏色緩存區(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");
    //注冊回調(diào)函數(shù)(改變尺寸)
    glutReshapeFunc(ChangeSize);
    //點擊空格時,調(diào)用的函數(shù)
    glutKeyboardFunc(KeyPressFunc);
    //特殊鍵位函數(shù)(上下左右)
    glutSpecialFunc(SpecialKeys);
    //顯示函數(shù)
    glutDisplayFunc(RenderScene);
    //判斷一下是否能初始化glew庫,確保項目能正常使用OpenGL 框架
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    //繪制
    SetupRC();
    //runloop運行循環(huán)
    glutMainLoop();
    return 0;
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內(nèi)容

  • 圖元處理(Primitive Processing) 如何在場景中使用曲面細分來添加幾何細節(jié) 如何使用幾何著色器處...
    RichardJieChen閱讀 6,952評論 2 4
  • 本文首發(fā)于個人博客:Lam's Blog - 【OpenGL-ES】二維紋理,文章由MarkDown語法編寫,可能...
    格子林ll閱讀 3,805評論 0 9
  • opengl opengl渲染管線基礎(chǔ)架構(gòu) 3.1 基礎(chǔ)圖形管線 3.1.1 客戶機-服務(wù)器 就opengl而言,...
    plsour閱讀 886評論 0 0
  • 渲染管線 蘋果提供了兩種OpenGL ES的可視化模型,一種是客戶端—服務(wù)端的架構(gòu)模型,另一種就是管線的模型。 客...
    sellse閱讀 12,170評論 1 10
  • 1.foldr foldr::(a->b->b)->b->[a]->b foldr函數(shù)應(yīng)用在list上,它的功能就...
    b7c9833ccf70閱讀 448評論 0 0