OpenGL ES入門第一篇-OpenGL ES初探

OpenGL ES 簡介

      OpenGL ES (OpenGL for Embedded Systems) 是以手持和嵌入式爲目標的⾼級3D圖形應 ⽤程序編程接口(API). OpenGL ES 是⽬前智能手機中佔據統治地位的圖形API.⽀持的平臺: iOS, Andriod , BlackBerry ,bada ,Linux ,Windows.
git

     另外,做爲一名iOS開發者咱們固然要看看Apple 官方文檔關於OpenGL ES 的解釋。編程

The Open Graphics Library (OpenGL) is used for visualizing 2D and 3D data. It is a multipurpose open- standard graphics library that supports applications for 2D and 3D digital content creation, mechanical and architectural design, virtual prototyping, flight simulation, video games, and more. You use OpenGL to configure a 3D graphics pipeline and submit data to it. Vertices are transformed and lit, assembled into primitives, and rasterized to create a 2D image. OpenGL is designed to translate function calls into graphics commands that can be sent to underlying graphics hardware. Because this underlying hardware is dedicated to processing graphics commands, OpenGL drawing is typically very fast. OpenGL for Embedded Systems (OpenGL ES) is a simplified version of OpenGL that eliminates redundant functionality to provide a library that is both easier to learn and easier to implement in mobile graphics hardware
數組

     開放式圖形庫(OpenGL)⽤於可視化的⼆維和三維數據。它是一個多功能開放式標準圖形庫,⽀持2D和3D數字內容建立,機械和建築設計,虛擬原型設計,⻜行模擬,視頻遊戲等應⽤用程序。您可使用OpenGL配置3D圖形管道並向其提交數據。頂點被變換和點亮,組合成圖元,並光柵化以建立2D圖像。OpenGL旨在將函數調⽤轉換爲能夠發送到底層圖形硬件的圖形命令。因爲此底層硬件專用於處理圖形命令,所以OpenGL繪圖一般⾮常快。緩存

     OpenGL for Embedded Systems(OpenGL ES)是OpenGL的簡化版本,它消除了冗餘功能,提供了⼀個既易於學習⼜更易於在移動圖形硬件中實現的庫。
bash

     因爲OpenGL ES 是OpenGL 的簡化版,所以在學習OpenGL ES前有必要去先了解一下基本的OpenGL 的知識,所以推薦你們先去看看前面關於Open GL 的文章服務器

OpenGL ES 渲染流程解析


      如上圖所示,整個渲染流程應該是app

  1. 經過客戶端將圖元數據(頂點座標,紋理座標,變換矩陣等等)和圖片數據(紋理)經過attribute 或者 uniform等通道傳入到頂點着色器
  2. 頂點着色器利用傳入的頂點座標和變換矩陣作各類變換,生成最終的頂點位置,若是有光照還能夠經過光照計算公式生成逐頂點光照顏色
  3. 須要注意的是頂點着色器以後並非立刻就進入了片元着色器,而是先通過了圖元裝配;在這個階段會執行裁剪、透視分割和 Viewport變換等操做。而後再通過光柵化纔到片元着色器
  4.  光柵化就是將圖元轉化成一組二維片斷的過程.而這些轉化的片斷將由片元着⾊器處理.這些⼆維片斷就是屏幕上可繪製的像素. 而片元着色器的任務就是給這些像素填充顏色,能夠是經過attribute通道傳入的顏色,也能夠是經過紋理圖片提取的紋素(某個紋理座標對應的紋理顏色);還能夠是本身計算的顏色(好比混合等等)
  5. 最後到達幀緩衝區,進行透明度、模板、深度測試、混合等等。注意這裏的混合和片元着色器裏的混合有點不同,是指將新生成的⽚段顏⾊與保存在幀緩存區相應位置的顏色值組合起來.


EGL與EAGL

       OpenGL ES 命令須要渲染上下文和繪圖表面才能完成圖形圖像的繪製。其中渲染上下文用來存儲相關OpenGL ES的狀態;繪製表面則是用來繪製圖元的表面,它指定渲染所須要的緩存區類型,好比顏色緩衝區深度緩衝區和模板緩衝區。框架

       然而OpenGL ES 並無提供如何建立渲染上下文或者上下文如何鏈接到原生窗口系統的API。而EGL是Khronos渲染API【如OpenGL ES】和原生窗口系統之間的接口。由於每一個窗⼝系統都有不一樣的定義,因此EGL提供基本的不透明類型—EGLDisplay, 這個類型封裝了全部系統相關性,用於和原生窗口系統的接口.可是咱們iOS平臺是惟一支持OpenGL ES 缺不支持EGL的平臺。Apple提供了本身的EGL API實現,稱爲EAGL。異步

       因爲OpenGL ES 是基於C的API,所以很是方便且受到普遍支持。做爲C API,它與Objective-C Cocoa Touch 應用程序無縫集成。可是,OpenGL ES 規範並無定義窗口層;相反,託管操做系統必須提供函數來建立一個接受命令的OpenGL ES 渲染上下文和一個幀緩衝區,其中寫入任何繪圖命令的結果。所以在iOS上使⽤OpenGL ES須要使用iOS類來設置和呈現繪圖表面,並使⽤平臺中相應的API來呈現其內容。ide

EAGLContext 

     EAGLContext是蘋果本身爲Open GL 提供的渲染上下文,該對象管理着利用OpenGL 渲染圖形所須要的狀態信息、命令、資源等等。在IOS應用程序中,每一個線程都會維護一個當前上下文。當應用程序調用Opengl ES的API時,線程的上下文就會被那個API改變(改變其管理的狀態、命令、資源等等)。

      要設置當前上下文,能夠經過調用EAGLContext類的setCurrentContext:方法來實現,固然也能夠經過EAGLContext類的currentContext方法來獲取一個線程的當前上下文。另外,在建立和初始化EAGLContext對象時,能夠選擇使用哪一個版本的Opengl ES   API。建立Opengl ES 3.0上下文時,以下初始化:

EAGLContext* myContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
複製代碼

      若是設備不支持您鎖設置的OpenGL ES API,那麼 initWithAPI:方法將返回nil。爲了支持OpenGL ES API的多個版本,應該首先嚐試初始化爲最新版本的渲染上下文。若是返回的對象爲nil,則初始化舊版本的上下文。如:

EAGLContext* CreateBestEAGLContext()
{
   EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
   if (context == nil) {
      context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
   }
   return context;
}
複製代碼

     上下文的API屬性指出了上下文支持的OpenGL ES版本。咱們應該首先測試上下文的API屬性而後使用它來選擇正確的渲染路徑。實現此行爲的常見模式是爲每一個渲染路徑建立一個類。應用程序在初始化時測試上下文並建立一次渲染器。

EAGLSharegroup

      儘管上文說到EAGLContext管理着 OpenGL 的狀態,但其實OpenGL的狀態並非直接由EAGLContext來管理的,而是EAGLContext 藉助EAGLContext來管理的。換句話說,OpenGL ES的狀態由EAGLSharegroup對象來建立和維護;每一個EAGLContext都包含一個EAGLSharegroup對象,它將OpenGL的狀態維護委託給EAGLSharegroup。3者的關係以下圖:


       你們都知道在移動端資源是很是匱乏的,在多個上下文中建立一樣內容的備份是很是奢侈的操做;若是可以共享通用的資源則能夠更好的利用設備的圖形資源。這個時候EAGLSharegroup將變得很是有用,當多個上下文關聯到同一個EAGLSharegroup時,被任意一個上下文建立的Opengl ES對象在全部的上下文中都是可用的。注意:共享同一個共享組的全部上下文,都必須使用同一個版本的Opengl ES API來初始化上下文

       當共享組是被多個上下文共享時,咱們就有義務要管理Opengl ES對象狀態的改變。規則以下:

  1. 當應用程序可能要經過多個上下文進入某個對象的同時,要確保對象沒有被同時改變。
  2. 當對象要被髮給上下文的命令改變時,對象此時不能被另外的上下文讀取或者改變。
  3. 在一個對象被改變時,必須是被綁定對象全部的上下文才能看到這些改變。若是一個上下文在綁定以前就引用它,那麼這個對象的內容是沒有被定義的

 GLKit框架概述  

       GLKit 框架的設計目標是爲了簡化基於OpenGL / OpenGL ES的應用開發。它的出現加快了OpenGL / OpenGL ES 應用的開發。GLKit使用數學庫,背景紋理加載,預先建立的着色器效果,以及標準視圖和視圖控制器來實現渲染循環。

     GLKit框架提供了功能和類,能夠減小建立新的基於着⾊器的應⽤程序所需的⼯做量,或者⽀持依賴早期版本的OpenGL ES或OpenGL提供的固定管線功能來處理應用程序。

      GLKView 提供繪製場所(View);GLKViewController(擴展於標準的UIKit. ⽤於繪製視圖內容的管理與呈現.) 

     另外須要注意的是雖然蘋果如今已經棄⽤了OpenGL ES(底層渲染從OpenGL ES改成metal),但因爲OpenGL ES 是基於C的API,做爲C API,它與Objective-C Cocoa Touch 應用程序無縫集成,所以iOS開發者能夠繼續使⽤,所以在使用GLKit相關API時雖然會有相應警告可是並不影響渲染結果。 

GLKit渲染圖形的基本流程

     使用GLKit渲染圖形時首先須要建立一個GLKView做爲繪製表面,同時設置其與OpenGL ES 相關基本參數,好比配置渲染緩存區的格式等等;接着咱們須要按照需求調用GLKit相關API設置相關圖形參數(好比顏色、紋理、頂點座標等等)來完成相關渲染,最後將渲染結果呈如今手機屏幕上。文字描述可能有點抽象,具體以下圖所示:

                     

        上圖中能夠看到一個frameBuffer頻繁出現,那麼什麼是frameBuffer(幀緩衝區)呢,其實幀緩衝區是由像素組成的二維數組,每個存儲單元對應屏幕上的一個像素,整個幀緩衝對應一幀圖像即當前屏幕畫面。幀緩衝一般包括:顏色緩衝,深度緩衝,模板緩衝和累積緩衝。這些緩衝區多是在一塊內存區域,也可能單獨分開,看硬件。而像素數據在進入幀緩衝以前(稱爲片元)必須經過一系列測試才能寫入幀緩衝,若是片元在其中某個測試沒有經過,後面的測試或操做都將再也不進行。這些測試或操做流程是:開始(片元)-裁剪測試-alpha測試-模板測試-深度測試-混合-抖動-邏輯操做-結束(寫入幀緩衝),這一系列操做都是針對片元着色器的輸出(片元的),因此又稱之爲逐片元操做。

     幀緩衝區能夠簡單的理解爲存儲繪製結果的地方;瞭解了什麼是幀緩衝區後,那麼理解上面的圖就不難了, 首先須要設置一下幀緩衝區的格式,包括顏色緩衝、深度緩衝等等的格式,不一樣的格式繪製出的圖形視覺效果可能不同;其次就能夠用代碼來具體實現繪製操做了,繪製的結果會暫時存儲在幀緩衝區;最後等一幅圖像繪製完畢後就能夠交由設備屏幕呈現了。

GLKit相關API介紹

      這裏咱們只介紹一些常見的類和API,基本能知足你們90%以上的開發任務了,對於個別比較冷門用處比較小的API等你們有相關需求的時候再去翻看相關API吧

  1. GLKTextureInfo  OpenGL紋理信息的抽象,裏面封裝了紋理相關的各類信息

            name : OpenGL上下⽂中到的紋理名稱;
            target :紋理綁定的目標(代表紋理是幾維的);
            height : 紋理的高度
            width : 紋理的高度
            textureOrigin: 紋理原點的位置
            alphaState : 紋理中alpha分量狀態
            containsMipmaps: 布爾值,紋理是否包含mip貼圖  

  2. GLKTextureLoader 紋理加載的工具類,可以簡化從各類資源文件中加載紋理 

           - initWithSharegroup: 初始化方法,這裏須要注意sharegroup這個參數是一個                    EAGLSharegroup類型的參數,sharegroup 對象管理與一個或多個EAGLContext對象關聯的      OpenGL ES資源,若不指定或值爲NULL則建立新的對象,當資源須要被共享時再使用它。

           + textureWithContentsOfFile:options:errer: 從文件中加載2D紋理圖像數據,並利用      數據生成新的紋理 

           - textureWithContentsOfFile:options:queue:completionHandler: 從文件中異步
    加載2D紋理圖像數據,並利用這些數據建立新紋理對象 

          - textureWithContentsOfURL:options:error: 從URL中加載2D紋理圖像數據,並利            用數據創 建新的紋理對象 
          - textureWithContentsOfURL:options:queue:completionHandler:  從URL中異步加        載2D紋理圖像數據,並利用數據建立新的紋理對象 
         + textureWithContentsOfData:options:errer: 從內存中加載2D紋理圖像數據,並根
    據數據建立新紋理 
        - textureWithContentsOfData:options:queue:completionHandler:  從內存中異步加        載2D紋理圖像數據,並根 據數據建立新紋理 
       - textureWithCGImage:options:error: 從Quartz圖像加載2D紋理圖像數據並利用數據創
    建新紋理對象
       - textureWithCGImage:options:queue:completionHandler:  從Quartz圖像異步加載2D      紋理圖像數據並利用數據創 建新紋理對象
       + cabeMapWithContentsOfURL:options:errer:  從單個URL加載立方體貼圖紋理
    圖像數據,並根據數據建立新紋理
      - cabeMapWithContentsOfURL:options:queue:completionHandler:  從單個URL異步        加載立方體貼圖紋理 圖像數據,並根據數據建立新紋理
      + cubeMapWithContentsOfFile:options:errer: 從單個文件加載立方體貼圖紋理
    圖像數據,並從數據中建立新紋理 

      - cubeMapWithContentsOfFile:options:queue:completionHandler: 從單個文件異步        加載立方體貼圖紋理圖像數據,並從數據中建立新紋理

      + cubeMapWithContentsOfFiles:options:errer: 從一系列⽂件中加載⽴方體貼圖 紋理圖      像數據,並從數據建立新紋理  

      - cubeMapWithContentsOfFiles:options:options:queue:completionHandler:從一          系列⽂件中異步加載⽴方體貼圖紋理圖像數據,並從數據中建立新紋理 

三、GLKView 使用OpenGL ES 繪製內容的視圖默認實現 

      - initWithFrame:context: 初始化新視圖 
      delegate 視圖代理
      drawableColorFormat 顏⾊渲染緩衝區格式
      drawableDepthFormat 深度渲染緩衝區格式 
      drawableStencilFormat 模板渲染緩衝區的格式
      drawableMultisample 多重採樣緩衝區的格式 
      drawableHeight 底層渲染緩衝區對象的高度(以像素爲單位)
      drawableWidth 底層渲染緩衝區對象的寬度(以像素爲單位) 
      context 繪製視圖內容時使⽤的OpenGL ES上下文
      - bindDrawable 將底層FrameBuffer 對象綁定到OpenGL ES
      enableSetNeedsDisplay 布爾值,控制setNeedsDisplay是否有效
      - display ⽴即重繪視圖內容 

      snapshot 繪製視圖內容並將其做爲新圖像對象(UIImage *)返回,這個方法永遠不要在               draw 方法裏調用,不然會遞歸死循環

     - deleteDrawable 刪除與視圖關聯的可繪製對象  

四、GLKViewDelegate  GLKView對象的回調接口 

     - glkView:drawInRect: 繪製視圖內容 (必須實現代理方法) 

五、GLKViewController 管理OpenGL ES渲染循環的視圖控制器

      paused 布爾值,渲染循環是否已暫停
     pausedOnWillResignActive 布爾值,當前程序即將推出活動狀態時視圖控制器是 否自動暫    停渲染循環
     resumeOnDidBecomeActive 布爾值,當前程序變爲活動狀態時視圖控制是否自動 恢復渲染循環  
     framesDisplayed 視圖控制器自建立以來發送的幀更新數(即繪製了多少幀)
    timeSinceFirstResume 自視圖控制器第⼀次恢復發送更新事件以來通過的時間量
    timeSinceLastResume ⾃上次視圖控制器恢復發送更新事件以來通過的時間量
    timeSinceLastUpdate 自上次視圖控制器調用委託⽅法( glkViewControllerUpdate: )後通過的時間量
     timeSinceLastDraw 自上次視圖控制器調用視圖display方法以來通過的時間量.
    preferredFramesPerSecond 視圖控制器調用視圖以及更新視圖內容的速率(理想狀況下的每秒的更新幀數)  

    framesPerSencond 視圖控制器調用視圖以及更新其內容的實際速率(這是個只讀屬性,由於實際幀率不會徹底由開發者經過preferredFramesPerSecond設置的值來決定,還受到屏幕刷新率等等的影響,所以framesPerSencond只能在不超過屏幕刷新率的基礎上儘可能接近preferredFramesPerSecond) 。

六、GLKViewControllerDelegate 渲染循環回調⽅法

   - glkViewControllerUpdate: 在顯示每一個幀以前調用 
  - glkViewController:willPause: 在渲染循環暫停或恢復以前調用. 

七、GLKBaseEffect  一種簡單光照/着⾊系統,⽤於基於着⾊器的OpenGL渲染 

   label 給Effect(效果)命名
   transform 綁定效果時應⽤於頂點數據的模型視圖,投影和紋理變換  
   lightingType ⽤於計算每一個⽚段的光照策略,值爲GLKLightingType枚舉( GLKLightingTypePerVertex 表示在三⻆形中每一個頂點執行光照計算,而後在三角形⾏插值; GLKLightingTypePerPixel 表示光照計算的輸入在三⻆形內插入,而且在每一個⽚段執行光照計算 ) 簡單點說就是這兩個枚舉值一個表示的是在頂點着色器計算光照,一個在片斷着色器計算光照。
    lightModelTwoSided 布爾值,表示爲圖元的兩側計算光照
    material 計算光照時使用的材質屬性
    lightModelAmbientColor 環境顏色,應⽤在所渲染的全部圖元. 
    l ight0 場景中第⼀個光照屬性 
   light1 場景中第二個光照屬性 
    light2 場景中第三個光照屬性 
   texture2d0 第一個紋理屬性 
   texture2d1 第⼆個紋理屬性 

   textureOrder 紋理應用於渲染圖元的順序
   colorMaterialEnable 布爾值,表示計算光照與材質交互時是否使用頂點顏色屬性 
   useConstantColor 布爾值,指示是否使用常量顏⾊
   constantColor 不提供每一個頂點顏色數據時使用的常量顏⾊
   - prepareToDraw 準備渲染效果 

案例-利用GLKit加載一張圖片

      在OpenGL 中圖片其實就是紋理  所以加載圖片其實就是加載一張紋理,下面咱們就用上文講到的GLKit來加載紋理並繪製到屏幕上。

     由於OpenGL ES 命令須要渲染上下文和繪圖表面才能完成圖形圖像的繪製,所以利用GLKit繪製圖形第一步就是配置好EAGLContext 和 GLKView。具體代碼以下:

- (EAGLContext *)bestCreateEAGLContext
{
    EAGLContext *temContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    if(!temContext) {
        temContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    }    
    return temContext;
}
- (void)setupConfig 
{
    context = [self bestCreateEAGLContext];
    [EAGLContext setCurrentContext:context];
    GLKView *temView = [[GLKView alloc] initWithFrame:self.view.bounds context:context];
    temView.delegate = self;
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    [self.view addSubview:temView];
}複製代碼

     在這裏準備好了渲染上下文和繪製表面,並設置了清屏顏色。在複雜案例中若是須要設置緩存區的格式,也能夠在這裏利用GLKView直接配置,好比加上:

temView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
temView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;複製代碼

     固然在本例中這些配置都是沒有必要的,由於咱們僅僅只是想加載一張圖片,不涉及深度相關的配置,而顏色緩存區格式默認就是GLKViewDrawableColorFormatRGBA8888 因此也能夠不寫。

     設置好了渲染上下文、渲染表面等基本條件後,第二步就能夠設置渲染須要的參數了,好比頂點座標,紋理座標等等,具體代碼以下:

- (void)setupVertexData
{    
    GLfloat vertexData[] = {
        0.5f,-0.5f,0.0f,  1.0f, 0.0f,
        0.5f,0.5f,0.0f,   1.0f, 1.0f,
        -0.5f,0.5f,0.0f,  0.0f, 1.0f, 
        -0.5f,0.5f,0.0f,  0.0f, 1.0f,
        -0.5f,-0.5f,0.0f, 0.0f,0.0f,
         0.5f,-0.5f,0.0f, 1.0f, 0.0f,
    };
    GLuint bufferId;
    glGenBuffers(1, &bufferId);
    glBindBuffer(GL_ARRAY_BUFFER, bufferId);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GL_FLOAT) * 5, (GLfloat *)NULL + 3);
}複製代碼

      這裏頂點座標和紋理座標的設置請參考前一個專題OpenGL相關的文章,須要注意的是vertexData數組是存儲在內存中的,因此首先要將內存數據拷貝到顯存,而在顯存中存儲這些數據須要先生成相應的緩衝區;所以咱們調用了glGenBuffers和glBindBuffer。那麼,這兩個函數有什麼做用呢?

     void glGenBuffers(GLsizei n,GLuint * buffers);官方的解釋爲generate buffer object names;意思是生成緩衝區對象的名字,沒錯僅僅只是個名字,這個函數接收2個參數,第一個表示生成緩衝區名字的個數;第二個參數是用來存儲緩衝對象名稱的數組。

      我的理解爲(若是不對請大神指點)glGenBuffers()函數僅僅是生成一個緩衝對象的名稱,這個緩衝對象並不具有任何意義,它僅僅是個緩衝對象,還不是一個頂點數組緩衝,它相似於C語言中的一個指針變量,咱們能夠分配內存對象而且用它的名稱來引用這個內存對象。OpenGL有不少緩衝對象類型,那麼這個緩衝對象究竟是什麼類型,就要用到下面的glBindBuffer()函數了。

     void glBindBuffer(GLenum target,GLuint buffer);官方解釋:bind a named buffer object。其第一個參數就是緩衝對象的類型,第二個參數爲要綁定的緩衝對象的名稱,也就是咱們在上一個函數裏生成的名稱,使用該函數將緩衝對象綁定到OpenGL上下文環境中以便使用。若是把target綁定到一個已經建立好的緩衝對象,那麼這個緩衝對象將爲當前target的激活對象;可是若是綁定的buffer值爲0,那麼OpenGL將再也不對當前target使用任何緩存對象。 在OpenGL紅寶書中有這樣一個比喻:綁定對象的過程就像設置鐵路的道岔開關,每個緩衝類型中的各個對象就像不一樣的軌道同樣,咱們將開關設置爲其中一個狀態,那麼以後的列車都會駛入這條軌道。 切記:官方文檔指出,GL_INVALID_VALUE is generated if buffer is not a name previously returned from a call to glGenBuffers。換句話說,這個名稱雖然是GLuint類型的,可是你萬萬不能直接指定個常量好比說0, 若是你這樣作,就會出現GL_INVALID_VALUE的錯誤。

      綁定緩衝類型後,全部該緩衝類型的函數調用都是用來配置該目標緩衝類型,好比咱們這裏的頂點緩衝類型GL_ARRAY_BUFFER,glBufferData是經過指定目標緩衝類型來進行數據傳輸的,而每個目標緩衝類型再使用前要提早綁定一個緩衝對象,從而賦予這個緩衝對象一個類型的意義,須要注意的是OpenGL容許咱們同時綁定多個緩衝類型,只要這些緩衝類型是不一樣的,換句話說,同一時間,不能綁定兩個相同類型的緩衝對象也能夠理解爲對於一個類型來講,同一時間只能激活一個類型,不然就會發生錯誤。

      好比綁定了兩個相同類型的目標緩衝,數據的配置確定就會出錯。你能夠想象一下,假如咱們要把數據存入頂點緩衝區,可是頂點緩衝區能夠有不少緩衝對象,我須要傳入哪一個呢,因而我就要提早綁定一個,以後,我只要向頂點緩衝區內傳入數據,這個數據就會自動進入被綁定的那個對象裏面了,在本例中就是將vertexData數據傳入名字爲bufferId的緩衝對象中,完成將內存數據拷貝到顯存的操做。

       前面已經說過glBufferData的做用將數據從內存拷貝到顯存,其函數原型爲:void glBufferData(GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);第一個參數表示緩衝區類型;第二個參數表示數據字節大小;第三個參數表示須要拷貝的數據;第4個參數表示靜態拷貝仍是動態拷貝,這裏的靜態動態之分後面其餘的文章會講到。

       另外,在iOS中默認狀況下出於性能考慮,全部頂點着色器的屬性(Attribute)變量都是關閉的.這意味着頂點數據在着色器端(服務端)是不可用的. 即便你已經使用glBufferData方法,將頂點數據從內存拷貝到頂點緩存區中(GPU顯存中). 因此, 必須由glEnableVertexAttribArray 方法打開通道.指定訪問屬性.才能讓頂點着色器可以訪問到從CPU複製到GPU的數據. 注意: 數據在GPU端是否可見,即着色器可否讀取到數據是由是否啓用了對應的屬性決定,這就是glEnableVertexAttribArray的功能,容許頂點着色器讀取GPU(服務器端)數據。打開屬性通道之後,數據在GPU端可見,可是對於這些數據怎麼讀取呢,怎麼區分哪些是頂點座標?哪些是紋理座標呢?這就要說到glVertexAttribPointer函數了。

      先來看一下函數原型:glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)。該函數的功能爲明確數據的讀取方式。參數index指定要修改的頂點屬性的索引值;參數size表示每次讀取數量;參數type,指定數組中每一個組件的數據類型;參數normalized表示是否須要歸一化操做;參數stride,指定連續頂點屬性之間的偏移量,若是爲0,那麼頂點屬性會被理解爲它們是緊密排列在一塊兒的,好比本例中兩個頂點直接的偏移量是5個浮點數因此爲sizeof(float) *5;參數ptr指定一個指針,指向數組中第一個頂點屬性的第一個組件,好比本例中頂點座標的第一個頂點屬性的第一個組件就是第一個浮點數是因此是(GLfloat *)NULL + 0,又好比本例中紋理座標的第一個頂點屬性的第一個組件就是第4個浮點數是因此是(GLfloat *)NULL + 3。總之利用該函數能夠指定出每次讀幾個數,2個數之間偏移多少 和第一個數的起始位置,這樣天然就能肯定清楚數據的讀取方式。

      好了,上面是參數的配置是本篇的重點因此囉嗦了一點,配置完這些參數後咱們就能夠進入主題開始第三步加載紋理了。直接上代碼:

-(void)setUpTexture

{    //1.獲取紋理圖片路徑
    NSString *filePath = [[NSBundle mainBundle]pathForResource:@"bui" ofType:@"jpg"];
    //2.設置紋理參數    //紋理座標原點是左下角,可是圖片顯示原點應該是左上角. 
     NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];
    //3.使用蘋果GLKit 提供GLKBaseEffect 完成着色器工做(頂點/片元)
    cEffect = [[GLKBaseEffect alloc]init];
    cEffect.texture2d0.enabled = GL_TRUE;
    cEffect.texture2d0.name = textureInfo.name;
}複製代碼

     這一部分的代碼比較簡單 你們直接看上文代碼 和註釋就好了,到了這裏咱們基本的準備工做都作完了能夠開始咱們最後的工做繪製了。實現GLKViewDelegate的代理方法-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect,來完成最後的繪製工做,代碼以下:

-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect 
{
    glClear(GL_COLOR_BUFFER_BIT);
    [mEffect prepareToDraw];
    glDrawArrays(GL_TRIANGLES, 0, 6);
}複製代碼


相關文章
相關標籤/搜索