OpenGL ES 2.0 Making the Hardware Work for You


顯示控制

iOS 系統會通過一個稱之為 Core Animation Compositor (核心動畫合成器[系統組件])去控制最終在屏幕顯示的圖像。

--> 核心動畫層可以同時擁有多個圖層;

--> 圖層保存了所有的繪制結果;

--> Core Animation Compositor 是由 OpenGL ES 來控制圖形處理、圖層的合成、幀緩存數據的快速交換;


Frame Buffers 和 Layers 的關系

--> pixel color render buffer,是 Frame Buffers 與 Layers 交換數據的地方(共享);

--> other render buffers,是可選的,但一個 OpenGL ES 程序至少包含一個;


例子:三角形

--?-> 如果用 UIKit 直接做會怎樣?

----> Try It ...
ViewController.view + UIImageView
前者,設置背景色為黑色;
后者,添加進前者中成為子控件;
1)后者直接設置 .image 為 一張白色的圖片(自己要制作一張圖片);
2)后者不設置圖片,設置顏色為白色,再 .layer 設置貝賽爾曲線進行剪切(要自己計算坐標,并進行繪制);

---->

--?-> 使用 OpenGL ES 直接進行繪制?

首先,分析圖像的組成:

  • 背景色是純黑色的;
  • 圖中有一個白色的直角三角形;
    • 因為 OpenGL ES 實際繪制的圖形是根據坐標點來進行填充的,而且三角形是由三個頂點連線組成的,所以 OpenGL ES 繪制的時候需要 三個坐標點;
      ----> Just Do It ...

類的綁定:


Controller --> OpenGLES_Ch2_1ViewController

view --> GLKView

核心代碼:


OpenGLES_Ch2_1ViewController.h

完整代碼:

//
//  OpenGLES_Ch2_1ViewController.m
//  OpenGLES_Ch2_1
//

#import "OpenGLES_Ch2_1ViewController.h"

@implementation OpenGLES_Ch2_1ViewController

@synthesize baseEffect;

/////////////////////////////////////////////////////////////////
// This data type is used to store information for each vertex
typedef struct {
   GLKVector3  positionCoords;
}
SceneVertex;

/////////////////////////////////////////////////////////////////
// Define vertex data for a triangle to use in example
static const SceneVertex vertices[] = 
{
   {{-0.5f, -0.5f, 0.0}}, // lower left corner
   {{ 0.5f, -0.5f, 0.0}}, // lower right corner
   {{-0.5f,  0.5f, 0.0}}, // upper left corner
};


/////////////////////////////////////////////////////////////////
// Called when the view controller's view is loaded
// Perform initialization before the view is asked to draw
- (void)viewDidLoad
{
   [super viewDidLoad];
   
   // Verify the type of view created automatically by the
   // Interface Builder storyboard
   GLKView *view = (GLKView *)self.view;
   NSAssert([view isKindOfClass:[GLKView class]],
      @"View controller's view is not a GLKView");
   
   // Create an OpenGL ES 2.0 context and provide it to the
   // view
   view.context = [[EAGLContext alloc] 
      initWithAPI:kEAGLRenderingAPIOpenGLES2];
   
   // Make the new context current
   [EAGLContext setCurrentContext:view.context];
   
   // Create a base effect that provides standard OpenGL ES 2.0
   // Shading Language programs and set constants to be used for 
   // all subsequent rendering
   self.baseEffect = [[GLKBaseEffect alloc] init];
   self.baseEffect.useConstantColor = GL_TRUE;
   self.baseEffect.constantColor = GLKVector4Make(
      1.0f, // Red
      1.0f, // Green
      1.0f, // Blue
      1.0f);// Alpha
   
   // Set the background color stored in the current context 
   glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // background color
   
   // Generate, bind, and initialize contents of a buffer to be 
   // stored in GPU memory
   glGenBuffers(1,                // STEP 1
      &vertexBufferID);
   glBindBuffer(GL_ARRAY_BUFFER,  // STEP 2
      vertexBufferID); 
   glBufferData(                  // STEP 3
      GL_ARRAY_BUFFER,  // Initialize buffer contents
      sizeof(vertices), // Number of bytes to copy
      vertices,         // Address of bytes to copy
      GL_STATIC_DRAW);  // Hint: cache in GPU memory
}


/////////////////////////////////////////////////////////////////
// GLKView delegate method: Called by the view controller's view
// whenever Cocoa Touch asks the view controller's view to
// draw itself. (In this case, render into a frame buffer that
// shares memory with a Core Animation Layer)
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
   [self.baseEffect prepareToDraw];
   
   // Clear Frame Buffer (erase previous drawing)
   glClear(GL_COLOR_BUFFER_BIT);
   
   // Enable use of positions from bound vertex buffer
   glEnableVertexAttribArray(      // STEP 4
      GLKVertexAttribPosition);
      
   glVertexAttribPointer(          // STEP 5
      GLKVertexAttribPosition, 
      3,                   // three components per vertex
      GL_FLOAT,            // data is floating point
      GL_FALSE,            // no fixed point scaling
      sizeof(SceneVertex), // no gaps in data
      NULL);               // NULL tells GPU to start at 
                           // beginning of bound buffer
                                   
   // Draw triangles using the first three vertices in the 
   // currently bound vertex buffer
   glDrawArrays(GL_TRIANGLES,      // STEP 6
      0,  // Start with first vertex in currently bound buffer
      3); // Use three vertices from currently bound buffer
}


/////////////////////////////////////////////////////////////////
// Called when the view controller's view has been unloaded
// Perform clean-up that is possible when you know the view 
// controller's view won't be asked to draw again soon.
- (void)viewDidUnload
{
   [super viewDidUnload];
   
   // Make the view's context current
   GLKView *view = (GLKView *)self.view;
   [EAGLContext setCurrentContext:view.context];
    
   // Delete buffers that aren't needed when view is unloaded
   if (0 != vertexBufferID)
   {
      glDeleteBuffers (1,          // STEP 7 
                       &vertexBufferID);  
      vertexBufferID = 0;
   }
   
   // Stop using the context created in -viewDidLoad
   ((GLKView *)self.view).context = nil;
   [EAGLContext setCurrentContext:nil];
}

@end

---->完整分析

繪制的整體過程:
【標記 Buffers --> 綁定 Buffers --> 初始化 Buffers --> 使能 Buffers --> 計算所有點的偏移量 --> 繪制 Buffers --> 刪除 Buffers 】

OpenGLES_Ch2_1ViewController.h 文件


分析:

  • 因為OpenGL ES 2.0 繪制的第一步需要一個標記,所以需要定義一個 GLuint 變量作為標記

GLuint 的定義:typedef uint32_t GLuint; (位于 OpenGLES/gltypes.h)

  • GLKBaseEffect ,基本的效果類
GLKit

OpenGLES_Ch2_1ViewController.m 文件:


分析(viewDidload):


viewDidload

【步驟:判定當前 View 是否是 GLKView --> 設置上下文環境(Context) --> 設置基本渲染效果(baseEffect) --> 準備繪制的數據(標記 Buffers --> 綁定 Buffers --> 初始化 Buffers ) 】

判定 View
Context
  • 1、view.context 的定義: GLKit/GLKView.h -->@property (nonatomic, retain) EAGLContext *context;

  • 2、initWithAPI:定義:OpenGLES/EAGL.h --> - (instancetype) initWithAPI:(EAGLRenderingAPI) api;

  • 3、EAGLRenderingAPI的定義:

typedef NS_ENUM(NSUInteger, EAGLRenderingAPI)
{
    kEAGLRenderingAPIOpenGLES1 = 1,
    kEAGLRenderingAPIOpenGLES2 = 2,
    kEAGLRenderingAPIOpenGLES3 = 3,
};

因為現在 OpenGL ES 已經更新到 3.0了所以有三個選項,因為本文的例子是 基于OpenGL ES 2.0 所以要選擇 kEAGLRenderingAPIOpenGLES2 (注意這個不能選錯);

  • 4、setCurrentContext 的定義: + (BOOL) setCurrentContext:(EAGLContext*) context;,可以監聽返回值,設置是否成功;
設置 BaseEffect
- 1、BaseEffect  的屬性
- 2、`constantColor` 填充色(設置填充色的前提是`self.baseEffect.useConstantColor = GL_TRUE;`,開啟填充色),如果把 Green 置零
準備繪制的數據
- 1、`glClearColor`,設置(view)背景色,定義 -->  `GL_API void           GL_APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);`修改顏色值觀察變化 
填充色
背景色
- 2、`glGenBuffers`,添加標記,定義`GL_API void  GL_APIENTRY glGenBuffers (GLsizei n, GLuint* buffers);`, GLsizei  `typedef int32_t  GLsizei;`

第一個參數是表明,有多少個標記;

第二個參數是表明,標記數是多少;

- 3、`glBindBuffer`,添加綁定,定義`GL_API void  GL_APIENTRY glBindBuffer (GLenum target, GLuint buffer);` GLenum `typedef uint32_t GLenum;`

第一個參數是表明,要綁定的 Buffers 類型(有兩個值:GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER

- 4、`glBufferData`,`定義:GL_API void   GL_APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);`

第一個參數,何種類型的 Buffers ;

第二個參數,GLsizeiptr typedef intptr_t GLsizeiptr; (就是 long), 拷貝多少字節的數據;

第三個參數, 數據的指針;

第四個參數,繪制的類型(STATIC 是表明 Buffers 的內容是靜態的,不再改變;DYNAMIC 表明 Buffers 的內容是頻繁更新的);

- 5、`vertices`,因為我們是要繪制 三角形,所以有三個坐標點(頂點):

坐標值
其中`GLKVector3` 定義 :

(Union,共用體)

--> 因為 OpenGL ES 的坐標范圍為:【-1,1】,三角形在坐標系下的展示為:
坐標系的展示

數據的準備已經做完,那么現在就可以進行圖形繪制了。

繪制的方法是,- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect 這個方法是 GLKView 的代理方法;

Dash 中查看代理方法:


只有一個代理方法,在 Controller 需要重新繪制 View 的時候都會調用這個代理方法,進行繪制。

【繪制步驟:繪制前準備 --> 擦除之前的繪制 --> 繪制最新的】

  • 繪制前準備,[self.baseEffect prepareToDraw];

查看 prepareToDraw 方法:


同步繪制前所有的更改,保證現在要繪制的圖形就是最新的修改;

  • 擦除之前的繪制

// Clear Frame Buffer (erase previous drawing) glClear(GL_COLOR_BUFFER_BIT);

glClear 的定義是:GL_API void GL_APIENTRY glClear (GLbitfield mask);

GLbitfield,定義 :typedef uint32_t GLbitfield;有以下三個值選擇:

因為現在我們繪制的圖形是 2D 的而且只填充了顏色參數,所以直接選擇 GL_COLOR_BUFFER_BIT 選項即可;

  • 繪制最新的


【使能 Buffers --> 計算所有點的偏移量 --> 繪制 Buffers 】

- 使能 Buffers `glEnableVertexAttribArray`,函數的定義是:

GL_API void GL_APIENTRY glEnableVertexAttribArray (GLuint index) __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);

繪制的選項


因為我們是以坐標點進行繪制的,所以選擇 GLKVertexAttribPosition

- 計算所有點的偏移量 `glVertexAttribPointer` , 函數定義為 `GL_API void           GL_APIENTRY glVertexAttribPointer (GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr)  __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_3_0);`

其中,GLint typedef int32_t GLint; ; GLboolean typedef uint8_t GLboolean; ; GLvoid typedef void GLvoid;

參數分析:

第一個參數,表明資源數據的類型;

第二個參數,表明一個坐標點中有多少個元素;

第三個參數,表明元素的類型是什么;

第四個參數,表明有沒有使用縮放;

第五個參數,表明坐標點有多少個字節;

第六個參數,表明從坐標數據緩沖區的起始位開始;

  • 繪制三角形


    glDrawArrays 定義:GL_API void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);第一個參數,表明要求 GPU 繪制一個三角形;第二個參數,表明起始坐標下標;第三個參數,表明有多少個坐標要繪制;

  • 刪除 Buffers

【步驟:保證當前 View.context 是正在使用的 context --> 刪除 Buffers --> 停用 Context】

  • 保證 context
  • 刪除 Buffers


glDeleteBuffers 定義: GL_API void GL_APIENTRY glDeleteBuffers (GLsizei n, const GLuint* buffers); 與 標記的函數是一樣參數,兩者要一一對應起來; 最后,把 vertexBufferID 置零,表明沒有使用這個標記;

  • 停用 context

設置當前繪制的 context 為 nil ,表明不再進行繪制;

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容