一、OpenGL ES 初識
1、OpenGL ES 簡介
OpenGL ES (OpenGL for Embedded Systems) 是以手持和嵌⼊式為⽬標的⾼級3D圖形應用程序編程接⼝口(API)。OpenGL ES 是⽬前智能⼿機中占據統治地位的圖形API。⽀持的平台:iOS、Andriod、 BlackBerry、bada、Linux、Windows。
OpenGL ES 允許應⽤程序利用底層圖形處理器的強⼤功能。iOS 設備上的 GPU 可以執⾏復雜的2D和3D繪圖,以及最終圖像中每個像素的復雜着⾊計算。
蘋果官方文檔 OpenGL ES Design Guidelines 中 OpenGL ES 的構建流程有詳細介紹。
2、主要流程點介紹
2.1)頂點着色器:
1、着⾊器程序:描述頂點上執行操作的頂點着⾊器程序源代碼/可執⾏⽂件
2、頂點着⾊器輸⼊(屬性): ⽤頂點數組提供每個頂點的數據
3、統⼀變量(uniform):頂點/⽚元着⾊器使⽤的不變數據
4、采樣器:代表頂點着⾊器使⽤紋理的特殊統一變量類型
gl_Position:內建變量 --> 頂點數據(每一個),經過處理后得到的值
頂點着⾊器業務:
1、矩陣變換位置
2、計算光照公式⽣成逐頂點顏色
3、⽣成/變換紋理坐標
總結:它可以⽤於執⾏⾃定義計算、實施新的變換、照明或者傳統的固定功能所不允許的基於頂點操作的效果。
2.2)圖元裝配
頂點着⾊器之后的下一個階段是 圖元裝配。
圖元(Primitive):點、線、三⻆形等。
圖元裝配:將頂點數據計算成⼀個個圖元。在這個階段會執⾏裁剪、透視分割和 Viewport 變換操作。
圖元類型和頂點索 確定將被渲染的單獨圖元。對於每個單獨圖元及其對應的頂點,圖元裝配階段執⾏的操作包括:將頂點着⾊器的輸出值執⾏裁剪、透視分割、視⼝變換 后進⼊光柵化階段。
2.3)光柵化
在這個階段,繪制對應的圖元(點/線/三⻆角形)。
光柵化就是將圖元轉化成⼀組⼆維⽚段 的過程。而這些轉化的⽚段將由⽚元着⾊器處理。這些⼆維⽚段就是屏幕上可繪制的像素。
--> 二維:屏幕2維嘛 像素點 --> 每個像素 100個像素點 片元着色器要處理100次 --> 性能?並不會導致性能問題,因為片元着色器是在GPU處理中其實是可以真正的實現高並發,並不會出現 CPU 上出現的性能問題。
2.4)片元着色器(片段着⾊器)
1、着⾊器程序:描述⽚段上執⾏操作的⽚元着⾊器程序源代碼/可執⾏⽂件
2、輸⼊變量:光柵化單元⽤插值為每個⽚段⽣成的頂點着色器輸出
3、統⼀變量(uniform):頂點/⽚元着⾊器使用的不變數據
4、采樣器:代表⽚元着⾊器使⽤紋理的特殊統一變量類型
gl_FragColor:內建變量 --> 每一個像素點,經過片元着色器處理后得到的值
⽚元着⾊器業務:
1、計算顏色
2、獲取紋理值
3、往像素點中填充顏⾊值(紋理值/顏色值)
總結:它可以⽤於圖⽚/視頻/圖形中每個像素的顏色填充
⽐如給視頻添加濾鏡,實際上就是將視頻中 每一幀的 每個圖⽚的像素點顏色填充進⾏修改 --> 每一幀圖片 --> 每一個像素點處理 --> 新值 --> 幀緩沖區 --> 顯示
2.5)逐片段操作
像素歸屬測試:確定幀緩存區中位置(Xw,Yw)的像素⽬前是不是歸屬於OpenGL ES所 有。例如,一個顯示OpenGL ES 幀緩存區 View 被另外一個View 所遮蔽,則窗⼝系統可以確定被遮蔽的像素不屬於OpenGL ES上下文,從而不全顯示這些像素。⽽像素歸屬測試是 OpenGL ES 的一部分,它不由開發者開⼈為控制,⽽是由 OpenGL ES 內部進行。
裁剪測試:裁剪測試確定(Xw,Yw)是否 位於 作為 OpenGL ES 狀態的一部分裁剪矩形范圍內。如果該⽚段位於裁剪區域之外,則被拋棄。
深度測試:輸⼊⽚段的深度值進行比較,確定⽚段是否拒絕測試。
混合:將新生成的⽚段顏色與保存在幀緩存的位置的顏色值組合起來。
抖動:可⽤於 最小化 因為使用有限精度在幀緩存區中保存顏色值⽽產生的 偽像。
2.6)EGL (Embedded Graphics Library )
OpenGL ES 命令需要渲染上下⽂和繪制表面才能完成圖形、圖像的繪制。
渲染上下文:存儲相關OpenGL ES 狀態。
繪制表⾯:⽤於繪制圖元的表面,它指定渲染所需要的緩存區類型,例如顏⾊緩存區,深度緩沖區和模板緩存區。
OpenGL ES API 並沒有提供如何創建渲染上下文或者上下⽂如何連接到原生窗⼝系統。EGL 是 Khronos 渲染API(如OpenGL ES) 和原⽣窗口系統之間的接口。唯⼀⽀持 OpenGL ES 卻不⽀持 EGL 的平台是 iOS。Apple 提供了⾃己的 EGL API 用來 iOS 實現,稱為 EAGL。
因每個窗口系統都有不同的定義,所以 EGL 提供基本的不透明類型 --> EGLDisplay,這個類型封裝了所有系統相關性⽤於和原生窗⼝系統接口。
由於 OpenGL ES 是基於 C 的 API,因此它⾮常便攜且受到⼴泛⽀持。作為 C API,它與 Objective-C Cocoa Touch 應⽤程序⽆縫集成。OpenGL ES 規范沒有定義窗⼝層,相 反,托管操作系統必須提供函數來創建一個接受命令的 OpenGL ES 渲染上下⽂和一個幀 緩沖區,其中寫⼊任何繪圖命令的結果。在iOS上使⽤ OpenGL ES 需要使⽤ iOS 類來設置 和呈現繪圖表面,並使用平台中⽴的 API 來呈現其內容。
2.7)計算機對於動畫的實現
應用程序使用 OpenGL ES 執行動畫顯示的流程:
二、GLKit 框架
GLKit 框架的設計⽬標是為了簡化基於 OpenGL / OpenGL ES 的應⽤開發。它的出現加快了 OpenGL ES 或OpenGL 應⽤程序開發。使⽤數學庫、背景紋理理加載、預先創建的着色器效果、以及標准視圖GLKView和視圖控制器GLKViewController來實現渲染循環。 GLKit 框架提供了功能和類,可以減少創建新的基於着⾊器的應⽤程序所需的⼯作量,或者⽀持依賴早期版本的 OpenGL ES 或 OpenGL 提供的固定函數頂點或⽚段處理的現有應⽤程序。
GLKView:提供繪制場所 --> View
GLKViewController:擴展於標准的 UIKit 設計模式。⽤於繪制視圖內容的管理與呈。
1、GLKit
1)加載紋理
2)提供高性能的數學運算
3)提供常見的着色器
4)提供視圖和視圖控制器
GLKit 代碼:
// 官方文檔中代碼 - (void)viewDidLoad { [super viewDidLoad]; // Create an OpenGL ES context and assign it to the view loaded from storyboard GLKView *view = (GLKView *)self.view; view.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; // Configure renderbuffers created by the view view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; view.drawableDepthFormat = GLKViewDrawableDepthFormat24; view.drawableStencilFormat = GLKViewDrawableStencilFormat8; // Enable multisampling view.drawableMultisample = GLKViewDrawableMultisample4X; } // 必須實現的 - (void)drawRect:(CGRect)rect { // Clear the framebuffer glClearColor(0.0f, 0.0f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Draw using previously configured texture, shader, uniforms, and vertex array glBindTexture(GL_TEXTURE_2D, _planetTexture); glUseProgram(_diffuseShading); glUniformMatrix4fv(_uniformModelViewProjectionMatrix, 1, 0, _modelViewProjectionMatrix.m); glBindVertexArrayOES(_planetMesh); glDrawElements(GL_TRIANGLE_STRIP, 256, GL_UNSIGNED_SHORT); }
2、案例
案例一效果(背景色+紋理):
案例二效果:
1)案例一 .m 代碼
1 // 2 // ViewController.m 3 // GLKit_Demo001 4 // 5 // Created by Domy on 2020/7/26. 6 // Copyright © 2020 Domy. All rights reserved. 7 // 8 9 #import "ViewController.h" 10 11 // 導入 OpenGL ES 12 #import <OpenGLES/ES3/gl.h> 13 #import <OpenGLES/ES3/glext.h> 14 15 @interface ViewController () { 16 // 創建上下文 17 EAGLContext *context; 18 // 着色器 19 GLKBaseEffect *myEffect; 20 } 21 22 @end 23 24 @implementation ViewController 25 26 - (void)viewDidLoad { 27 [super viewDidLoad]; 28 // Do any additional setup after loading the view. 29 30 // 初始化 31 [self setUpConfig]; 32 33 // 設置 頂點/紋理 坐標數據 34 [self configVertex]; 35 36 // 設置紋理 37 [self configTexture]; 38 } 39 40 - (void)configTexture { 41 42 // 1. 圖片路徑 43 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"cat" ofType:@"jpg"]; 44 45 // 2. 設置紋理參數 46 // 紋理坐標原點是左下角,圖片顯示原點應該是左上角 47 NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];// @(1):true 48 // 紋理數據 49 GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil]; 50 51 // 3. 使用 蘋果封好的 着色器 GLKBaseEffect 包含了 頂點和片元着色器 52 myEffect = [[GLKBaseEffect alloc] init]; 53 myEffect.texture2d0.enabled = GL_TRUE; 54 myEffect.texture2d0.name = textureInfo.name; 55 56 } 57 58 - (void)configVertex { 59 60 // 1. 頂點數組 數據 61 // 頂點坐標 前3(x,yxz) 紋理坐標 后2(s,t) 62 GLfloat vertexDataArr[] = { 63 1.0, -0.5, 0.0f, 1.0f, 0.0f, //右下 64 1.0, 0.5, 0.0f, 1.0f, 1.0f, //右上 65 -1.0, 0.5, 0.0f, 0.0f, 1.0f, //左上 66 67 1.0, -0.5, 0.0f, 1.0f, 0.0f, //右下 68 -1.0, 0.5, 0.0f, 0.0f, 1.0f, //左上 69 -1.0, -0.5, 0.0f, 0.0f, 0.0f, //左下 70 };// --> 在內存中 71 72 73 /* 74 頂點數組: 開發者可以選擇設定函數指針,在調用繪制方法的時候,直接由內存傳入頂點數據,也就是說這部分數據之前是存儲在內存當中的,被稱為頂點數組 75 76 頂點緩存區: 性能更高的做法是,提前分配一塊顯存,將頂點數據預先傳入到顯存當中。這部分的顯存,就被稱為頂點緩沖區 77 */ 78 79 80 // 2. 頂點數據copy到頂點緩沖區 81 // 2.1 創建頂點緩沖區 82 GLuint bufferID; 83 glGenBuffers(1, &bufferID);// 1個緩沖區 &bufferID:ID --> 頂點緩沖區存在GPU中 84 // 2.2 綁定頂點緩沖區 85 // 類型 GL_ARRAY_BUFFER: 存數組所以 array_buffer 86 glBindBuffer(GL_ARRAY_BUFFER, bufferID); 87 // 2.3 頂點數據copy到頂點緩沖區 88 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexDataArr), vertexDataArr, GL_STATIC_DRAW); 89 90 91 // 3. 開啟讀取通道 -- 不開啟即使數據copy到了緩沖區也不能用 92 /* 93 (1)在iOS中, 默認情況下,出於性能考慮,所有頂點着色器的屬性(Attribute)變量都是關閉的. 94 意味着,頂點數據在着色器端(服務端)是不可用的. 即使你已經使用glBufferData方法,將頂點數據從內存拷貝到頂點緩存區中(GPU顯存中). 95 所以, 必須由glEnableVertexAttribArray 方法打開通道.指定訪問屬性.才能讓頂點着色器能夠訪問到從CPU復制到GPU的數據. 96 注意: 數據在GPU端是否可見,即,着色器能否讀取到數據,由是否啟用了對應的屬性決定,這就是glEnableVertexAttribArray的功能,允許頂點着色器讀取GPU(服務器端)數據。 97 98 (2)方法簡介 99 /// 功能: 上傳頂點數據到顯存的方法(設置合適的方式從buffer里面讀取數據) 100 // 參數列表: 101 index, 指定要修改的頂點屬性的索引值,例如: GLKVertexAttribPosition 、 GLKVertexAttribTexCoord0 102 size, 每次讀取數量。(如position是由3個(x,y,z)組成,而顏色是4個(r,g,b,a),紋理則是2個.) 103 type, 指定數組中每個組件的數據類型。可用的符號常量有GL_BYTE, GL_UNSIGNED_BYTE, GL_SHORT,GL_UNSIGNED_SHORT, GL_FIXED, 和 GL_FLOAT,初始值為GL_FLOAT。 104 normalized, 指定當被訪問時,固定點 數據值是否應該被歸一化(GL_TRUE)或者直接轉換為固定點值(GL_FALSE) 105 stride, 指定連續頂點屬性之間的偏移量。如果為0,那么頂點屬性會被理解為:它們是緊密排列在一起的。初始值為0 106 ptr指定一個指針,指向數組中第一個頂點屬性的第一個組件。初始值為0 107 glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid* ptr); 108 109 */ 110 // vertexDataArr 數組中存了 頂點坐標和紋理坐標,要分別開啟頂點和紋理通道 111 // 3.1 頂點數據 112 glEnableVertexAttribArray(GLKVertexAttribPosition); 113 // 從數組中讀取數據:數據傳給誰?頂點 GLKVertexAttribPosition -> 每次讀取 3 個元素 -> 元素類型是 float,不歸一化處理 -> 讀取下一個數據的間隔 5 -> 從第 0 位開始讀 114 glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0); 115 // 3.2 紋理數據 116 // GLKVertexAttribTexCoord0 GLKVertexAttribTexCoord1 GLKit只支持2個紋理 117 glEnableVertexAttribArray(GLKVertexAttribTexCoord0); 118 // 從數組中讀取數據:數據傳給誰?紋理 GLKVertexAttribTexCoord0 -> 每次讀取 2 個元素 -> 元素類型是 float,不歸一化處理 -> 讀取下一個數據的間隔 5 -> 從第 3 位開始讀 119 glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3); 120 121 } 122 123 - (void)setUpConfig { 124 // 1.初始化上下文 125 context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; 126 if (!context) { 127 NSLog(@"create content failed"); 128 } 129 130 // 2. 設置當前上下文 131 [EAGLContext setCurrentContext:context]; 132 133 // 3. 創建 GLKView 134 GLKView *view = (GLKView *)self.view; 135 view.context = context; 136 137 // 顏色緩沖區 深度緩沖區 138 view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; 139 view.drawableDepthFormat = GLKViewDrawableDepthFormat16; 140 141 // 4. 設置背景色 142 glClearColor(0, 0, 1, 1); 143 } 144 145 // 5. 調用 drawInRect 方法 146 - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { 147 148 glClear(GL_COLOR_BUFFER_BIT);// 清空背景顏色 149 150 // 准備繪制 151 [myEffect prepareToDraw]; 152 // 開始繪制 153 // mode:GL_TRIANGLES -- 圖元鏈接方式 154 // 0: 從第 0 個頂點開始 155 // 6: 共6個頂點 156 glDrawArrays(GL_TRIANGLES, 0, 6); 157 } 158 159 160 @end
2)案例二 .m 代碼
1 // 2 // ViewController.m 3 // GLKit_Demo001 4 // 5 // Created by Domy on 2020/7/26. 6 // Copyright © 2020 Domy. All rights reserved. 7 // 8 9 #import "ViewController.h" 10 11 // 導入 OpenGL ES 12 #import <OpenGLES/ES3/gl.h> 13 #import <OpenGLES/ES3/glext.h> 14 15 @interface ViewController () { 16 // 創建上下文 17 EAGLContext *context; 18 // 着色器 19 GLKBaseEffect *myEffect; 20 21 // 立方體旋轉 22 int rotation; 23 GLvoid *vertexData; 24 } 25 26 @end 27 28 @implementation ViewController 29 30 - (void)viewDidLoad { 31 [super viewDidLoad]; 32 // Do any additional setup after loading the view. 33 34 // 初始化 35 [self setUpConfig]; 36 // 頂點/紋理 坐標 37 [self configVertex]; 38 // 設置紋理 39 [self configTexture]; 40 } 41 42 // 紋理 43 - (void)configTexture { 44 45 // 1. 圖片路徑 46 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"cat" ofType:@"jpg"]; 47 48 // 2. 設置紋理參數 49 NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(YES),GLKTextureLoaderOriginBottomLeft, nil]; 50 // 紋理數據 51 GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil]; 52 53 // 3. 着色器 GLKBaseEffect 包含了 頂點和片元着色器 54 myEffect = [[GLKBaseEffect alloc] init]; 55 myEffect.texture2d0.enabled = GL_TRUE; 56 myEffect.texture2d0.name = textureInfo.name; 57 58 59 // 4. 投影矩陣 60 CGFloat aspect = fabs(self.view.bounds.size.width / self.view.bounds.size.height); 61 GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90.0), aspect, 0.01, 100.0); 62 myEffect.transform.projectionMatrix = projectionMatrix;// 設置投影矩陣 63 } 64 65 // xy 平面上的2個面 66 - (void)configVertex { 67 68 // 1. 69 GLfloat vertexDataArr[] = { 70 // 前 71 0.25, -0.25, 0.0f, 1.0f, 0.0f, //右下 72 0.25, 0.25, 0.0f, 1.0f, 1.0f, //右上 73 -0.25, 0.25, 0.0f, 0.0f, 1.0f, //左上 74 75 0.25, -0.25, 0.0f, 1.0f, 0.0f, //右下 76 -0.25, 0.25, 0.0f, 0.0f, 1.0f, //左上 77 -0.25, -0.25, 0.0f, 0.0f, 0.0f, //左下 78 79 // 后 80 0.25, -0.25, 0.5f, 1.0f, 0.0f, //右下 81 0.25, 0.25, 0.5f, 1.0f, 1.0f, //右上 82 -0.25, 0.25, 0.5f, 0.0f, 1.0f, //左上 83 84 0.25, -0.25, 0.5f, 1.0f, 0.0f, //右下 85 -0.25, 0.25, 0.5f, 0.0f, 1.0f, //左上 86 -0.25, -0.25, 0.5f, 0.0f, 0.0f, //左下 87 88 // 右 89 0.25, -0.25, 0.5f, 1.0f, 0.0f, //右下 90 0.25, 0.25, 0.5f, 1.0f, 1.0f, //右上 91 0.25, 0.25, 0.0f, 0.0f, 1.0f, //左上 92 93 0.25, -0.25, 0.5f, 1.0f, 0.0f, //右下 94 0.25, 0.25, 0.0f, 0.0f, 1.0f, //左上 95 0.25, -0.25, 0.0f, 0.0f, 0.0f, //左下 96 97 // 左 98 -0.25, -0.25, 0.5f, 1.0f, 0.0f, //右下 99 -0.25, 0.25, 0.5f, 1.0f, 1.0f, //右上 100 -0.25, 0.25, 0.0f, 0.0f, 1.0f, //左上 101 102 -0.25, -0.25, 0.5f, 1.0f, 0.0f, //右下 103 -0.25, 0.25, 0.0f, 0.0f, 1.0f, //左上 104 -0.25, -0.25, 0.0f, 0.0f, 0.0f, //左下 105 106 // 上 107 0.25, 0.25, 0.0f, 1.0f, 0.0f, //右下 108 0.25, 0.25, 0.5f, 1.0f, 1.0f, //右上 109 -0.25, 0.25, 0.5f, 0.0f, 1.0f, //左上 110 111 0.25, 0.25, 0.0f, 1.0f, 0.0f, //右下 112 -0.25, 0.25, 0.5f, 0.0f, 1.0f, //左上 113 -0.25, 0.25, 0.0f, 0.0f, 0.0f, //左下 114 115 // 下 116 0.25, -0.25, 0.0f, 1.0f, 0.0f, //右下 117 0.25, -0.25, 0.5f, 1.0f, 1.0f, //右上 118 -0.25, -0.25, 0.5f, 0.0f, 1.0f, //左上 119 120 0.25, -0.25, 0.0f, 1.0f, 0.0f, //右下 121 -0.25, -0.25, 0.5f, 0.0f, 1.0f, //左上 122 -0.25, -0.25, 0.0f, 0.0f, 0.0f, //左下 123 124 }; 125 126 // 2. 頂點數據copy到頂點緩沖區 127 // 2.1 創建頂點緩沖區 128 GLuint bufferID; 129 glGenBuffers(1, &bufferID);// 1個緩沖區 &bufferID:ID --> 頂點緩沖區存在GPU中 130 // 2.2 綁定頂點緩沖區 131 glBindBuffer(GL_ARRAY_BUFFER, bufferID); 132 // 2.3 頂點數據copy到頂點緩沖區 133 glBufferData(GL_ARRAY_BUFFER, sizeof(vertexDataArr), vertexDataArr, GL_STATIC_DRAW); 134 // 3.1 頂點數據 135 glEnableVertexAttribArray(GLKVertexAttribPosition); 136 // 從數組中讀取數據:數據傳給誰?頂點 GLKVertexAttribPosition -> 每次讀取 3 個元素 -> 元素類型是 float,不歸一化處理 -> 讀取下一個數據的間隔 5 -> 從第 0 位開始讀 137 glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0); 138 // 3.2 紋理數據 139 glEnableVertexAttribArray(GLKVertexAttribTexCoord0); 140 glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3); 141 142 } 143 144 - (void)setUpConfig { 145 146 // 147 // 148 /// 149 150 // 1.初始化上下文 151 context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; 152 if (!context) { 153 NSLog(@"create context failed"); 154 } 155 156 // 2. 設置當前上下文 157 [EAGLContext setCurrentContext:context]; 158 159 // 3. 創建 GLKView 160 GLKView *view = (GLKView *)self.view; 161 view.context = context; 162 163 // 顏色緩沖區 深度緩沖區 164 view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888; 165 view.drawableDepthFormat = GLKViewDrawableDepthFormat24; 166 167 // 4. 設置背景色 168 glClearColor(0.7, 0.7, 0.7, 1); 169 } 170 171 // 旋轉 172 - (void)update { 173 // Set up transform matrices for the rotating planet 174 175 // Z軸上向里移動 3 176 GLKMatrix4 modelviewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -3.0); 177 178 // 旋轉度數 179 rotation = (rotation + 2) % 360; 180 // 參數: 矩陣 弧度 x y z 181 modelviewMatrix = GLKMatrix4Rotate(modelviewMatrix, GLKMathDegreesToRadians(rotation), 0.5, 0.5, 0.5); 182 183 // set 模型視圖矩陣 184 myEffect.transform.modelviewMatrix = modelviewMatrix; 185 } 186 187 - (void)drawCovers { 188 189 [self update]; 190 // 准備繪制 191 [myEffect prepareToDraw]; 192 // 開始繪制 193 glDrawArrays(GL_TRIANGLES, 0, 36); 194 } 195 196 // 5. 調用 drawInRect 方法 197 - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect { 198 199 // 開啟深度測試 200 glEnable(GL_DEPTH_TEST); 201 202 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 清空 顏色 深度緩沖 203 204 [self drawCovers];// 繪制面 205 } 206 207 208 @end