目錄
一辛蚊、目標(biāo)
1. 基礎(chǔ)知識(shí)準(zhǔn)備
2. 圖形分析
二、編寫程序
0. 工程結(jié)構(gòu)與整體渲染管線
1. Depth Render Buffer
2. 數(shù)據(jù)源的編寫與綁定
3. 深度測(cè)試與繪制
4. 讓正方體動(dòng)起來(lái)
三真仲、參考書籍袋马、文章
一、目標(biāo)
1. 基礎(chǔ)知識(shí)準(zhǔn)備
a. 渲染管線的基礎(chǔ)知識(shí)
《OpenGL ES 2.0 (iOS)[01]: 一步從一個(gè)小三角開(kāi)始》
b. 3D 變換
《OpenGL ES 2.0 (iOS)[04]:坐標(biāo)空間 與 OpenGL ES 2 3D空間》
2. 圖形分析
a. 它是一個(gè)正方體秸应,由六個(gè)正方形面組成虑凛,有 8 個(gè)頂點(diǎn);
b. 正方體并不是二維圖形软啼,而是三維圖形桑谍,即頂點(diǎn)坐標(biāo)應(yīng)為{x, y, z},而且 z 不可能一直為 0祸挪;
c. 若由 OpenGL ES 繪制霉囚,z 坐標(biāo)表示深度(depth)信息;
d. 六個(gè)面均有不一樣的顏色,即 8 個(gè)頂點(diǎn)都帶有顏色信息盈罐,即渲染的頂點(diǎn)要提供相應(yīng)的顏色信息榜跌;
e. 六個(gè)正方形面,若由 OpenGL ES 繪制盅粪,需要由兩個(gè)三角面組合而成钓葫,即繪制模式為 GL_TRIANGLE*;
f. 正方體的每一個(gè)頂點(diǎn)都包含在三個(gè)面中票顾,即一個(gè)頂點(diǎn)都會(huì)被使用多次础浮,即繪制的時(shí)候應(yīng)該使用 glDrawElements 方法而不是 glDrawArrays 方法,所以除 8 個(gè)頂點(diǎn)的數(shù)據(jù)外還需增加下標(biāo)數(shù)據(jù)才有可能高效地繪制出正方體奠骄;
g. 正方體在不斷地旋轉(zhuǎn)運(yùn)動(dòng)豆同,即可能要實(shí)時(shí)改變頂點(diǎn)的信息并進(jìn)行重新繪制以達(dá)到運(yùn)動(dòng)的效果(思路:動(dòng)圖就是靜態(tài)圖的快速連續(xù)變化,只要變化的速度大于人眼可以辨別的速度含鳞,就會(huì)產(chǎn)生自然流暢的動(dòng)圖)
分析可程序化:
- 結(jié)合 a影锈、b、c蝉绷、d 四點(diǎn)可以知道鸭廷,頂點(diǎn)的數(shù)據(jù)格式可以為:
#define PositionCoordinateCount (3)
#define ColorCoordinateCount (4)
typedef struct {
GLfloat position[PositionCoordinateCount];
GLfloat color[ColorCoordinateCount];
} VFVertex;
static const VFVertex vertices[] = {
{{...}, {...}}
......
};
當(dāng)然你也可以把 position 和 color 分開(kāi)來(lái),只不過(guò)我認(rèn)為放在一起更好管理罷了熔吗。
- 從 e辆床、f 兩點(diǎn)可以知道,增加的數(shù)據(jù)及繪制的方式:
因?yàn)槭褂?element 方式桅狠,所以增加下標(biāo)信息讼载;
static const GLubyte indices[] = {
......
};
glDrawElements(GL_TRIANGLES,
sizeof(indices) / sizeof(indices[0]),
GL_UNSIGNED_BYTE,
indices);
- 從 g 點(diǎn)可以知道:
圖形的運(yùn)動(dòng),表明圖形在一定時(shí)間內(nèi)不斷地進(jìn)行更新(重新繪制并渲染)中跌,即只要使用具有定時(shí)功能的方法即可處理圖形的運(yùn)動(dòng)维雇,NSTimer 就可以勝任這個(gè)工作,不過(guò) iOS 提供了一個(gè) CADisplayLink 類來(lái)專門做定時(shí)更新的工作晒他,所以可以選用它進(jìn)行運(yùn)動(dòng)更新;
二逸贾、編寫程序
0. 工程結(jié)構(gòu)與整體渲染管線
結(jié)構(gòu)目錄簡(jiǎn)述
藍(lán)框是包含 CADisplayLink 子類的類陨仅,用于更新渲染,就是讓圖形動(dòng)起來(lái)铝侵;
紅框就是整體的渲染管線灼伤,所有的繪制渲染工作均在此處;
渲染管線 + Depth
Render Buffer 有三種緩存咪鲜,Color 狐赡、Depth 、Stencil 三種疟丙;而單純繪制 2D 圖形的時(shí)候因?yàn)闆](méi)有引入 z 坐標(biāo)(z != 0)而只使用了 Render Buffer 的 Color Render Buffer ;
而如今要進(jìn)行渲染的正方體颖侄,是帶有 z 坐標(biāo)鸟雏,即深度信息,所以自然要引入 Depth Render Buffer 了览祖;
引入 Depth Render Buffer 并使其工作的步驟:
ViewController 的程序調(diào)度
#import "ViewController.h"
#import "VFGLCubeView.h"
@interface ViewController ()
@property (strong, nonatomic) VFGLCubeView *cubeView;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGRect rect = CGRectOffset(self.view.frame, 0, 0);
self.cubeView = [[VFGLCubeView alloc] initWithFrame:rect];
[_cubeView prepareDisplay];
[_cubeView drawAndRender];
[self.view addSubview:_cubeView];
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.cubeView update];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.cubeView pauseUpdate];
}
@end
內(nèi)容并不復(fù)雜孝鹊,所以此處不進(jìn)行贅述;
渲染管線
prepareDisplay + drawAndRender
prepareDisplay 渲染管線的準(zhǔn)備部分
- (void)prepareDisplay {
// 1. Context
[self settingContext];
// 2 要在 Render Context setCurrent 后, 再進(jìn)行 OpenGL ES 的操作
// [UIColor colorWithRed:0.423 green:0.046 blue:0.875 alpha:1.000]
// [UIColor colorWithRed:0.423 green:0.431 blue:0.875 alpha:1.000]
[self setRenderBackgroundColor:RGBAColorMake(0.423, 0.431, 0.875, 1.000)];
// 2.? Vertex Buffer Object
self.vboBufferID = [self createVBO];
[self bindVertexDatasWithVertexBufferID:_vboBufferID
bufferTarget:GL_ARRAY_BUFFER
dataSize:sizeof(vertices)
data:vertices
elements:NO];
[self bindVertexDatasWithVertexBufferID:kInvaildBufferID
bufferTarget:GL_ELEMENT_ARRAY_BUFFER
dataSize:sizeof(indices)
data:indices
elements:YES];
// 3. Shader
GLuint vertexShaderID = [self createShaderWithType:GL_VERTEX_SHADER];
[self compileVertexShaderWithShaderID:vertexShaderID type:GL_VERTEX_SHADER];
GLuint fragmentShaderID = [self createShaderWithType:GL_FRAGMENT_SHADER];
[self compileVertexShaderWithShaderID:fragmentShaderID type:GL_FRAGMENT_SHADER];
self.programID = [self createShaderProgram];
[self attachShaderToProgram:_programID
vertextShader:vertexShaderID
fragmentShader:fragmentShaderID];
[self linkProgramWithProgramID:_programID];
[self updateUniformsLocationsWithProgramID:_programID];
// 4. Attach VBOs
[self attachCubeVertexArrays];
}
基于這部分展蒂,本文的工作在以下兩處進(jìn)行:
// 1. Context
[self settingContext];
它負(fù)責(zé)確定渲染上下文又活,以及 Render Buffer 與 Frame Buffer 的資源綁定處理;
[self settingContext];
詳見(jiàn) 本章 1.Depth Render Buffer 一節(jié)
// 2.? Vertex Buffer Object
self.vboBufferID = [self createVBO];
[self bindVertexDatasWithVertexBufferID:_vboBufferID
bufferTarget:GL_ARRAY_BUFFER
dataSize:sizeof(vertices)
data:vertices
elements:NO];
[self bindVertexDatasWithVertexBufferID:kInvaildBufferID
bufferTarget:GL_ELEMENT_ARRAY_BUFFER
dataSize:sizeof(indices)
data:indices
elements:YES];
它是處理頂點(diǎn)緩存數(shù)據(jù)的锰悼;
VBO 與 數(shù)據(jù)源
詳見(jiàn) 本章 2. 數(shù)據(jù)源的編寫與綁定
drawAndRender 渲染管線的余下部分
- (void)drawAndRender {
// 5. Draw Cube
// 5.0 使用 Shader
[self userShaderWithProgramID:_programID];
// 5.1 應(yīng)用 3D 變換
self.modelPosition = GLKVector3Make(0, -0.5, -5);
[self transforms];
// 5.2 清除舊渲染緩存
[self clearColorRenderBuffer:YES depth:YES stencil:NO];
// 5.3 開(kāi)啟深度測(cè)試
[self enableDepthTesting];
// 5.4 繪制圖形
[self drawCube];
// 5.5 渲染圖形
[self render];
}
基于這部分柳骄,本文的工作在此處進(jìn)行:
// 5.2 清除舊渲染緩存
[self clearColorRenderBuffer:YES depth:YES stencil:NO];
// 5.3 開(kāi)啟深度測(cè)試
[self enableDepthTesting];
// 5.4 繪制圖形
[self drawCube];
詳見(jiàn) 本章 3. 深度測(cè)試與繪制 一節(jié)
關(guān)于實(shí)時(shí)更新的內(nèi)容
[self.cubeView update];
[self.cubeView pauseUpdate];
詳見(jiàn) 本章 4. 讓正方體動(dòng)起來(lái)
1. Depth Render Buffer
[self settingContext];
它的內(nèi)容為:
- (void)setContext:(EAGLContext *)context {
if (_context != context) {
[EAGLContext setCurrentContext:_context];
[self deleteFrameBuffer:@[@(self.frameBufferID)]];
self.frameBufferID = kInvaildBufferID;
[self deleteRenderBuffer:@[@(self.colorRenderBufferID), @(self.depthRenderBufferID)]];
self.colorRenderBufferID = self.depthRenderBufferID = kInvaildBufferID;
_context = context;
if (context != nil) {
_context = context;
[EAGLContext setCurrentContext:_context];
// 2. Render / Frame Buffer
// 2.0 創(chuàng)建 Frame Buffer
[self deleteFrameBuffer:@[@(self.frameBufferID)]];
self.frameBufferID = [self createFrameBuffer];
// 2.1 Color & Depth Render Buffer
[self deleteRenderBuffer:@[@(self.colorRenderBufferID)]];
self.colorRenderBufferID = [self createRenderBuffer];
[self renderBufferStrogeWithRenderID:self.colorRenderBufferID];
[self attachRenderBufferToFrameBufferWithRenderBufferID:self.colorRenderBufferID
attachment:GL_COLOR_ATTACHMENT0];
// 2.2 檢查 Frame 裝載 Render Buffer 的問(wèn)題
[self checkFrameBufferStatus];
// 2.3 Add Depth Render Buffer
[self enableDepthRenderBuffer];
[self deleteRenderBuffer:@[@(self.depthRenderBufferID)]];
if ( ! CGSizeEqualToSize(self.renderBufferSize, CGSizeZero) &&
self.depthMode != VFDrawableDepthMode_None) {
self.depthRenderBufferID = [self createRenderBuffer];
if (self.depthRenderBufferID == kInvaildBufferID) {
return;
}
[self renderBufferStrogeWithRenderID:self.depthRenderBufferID];
[self attachRenderBufferToFrameBufferWithRenderBufferID:self.depthRenderBufferID
attachment:GL_DEPTH_ATTACHMENT];
}
// 2.4 檢查 Frame 裝載 Render Buffer 的問(wèn)題
[self checkFrameBufferStatus];
}
}
}
- (void)settingContext {
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
}
這里重寫了 setContext: 方法,核心內(nèi)容是
// 2.3 Add Depth Render Buffer
// 2.3 Add Depth Render Buffer
[self enableDepthRenderBuffer];
[self deleteRenderBuffer:@[@(self.depthRenderBufferID)]];
if ( ! CGSizeEqualToSize(self.renderBufferSize, CGSizeZero) &&
self.depthMode != VFDrawableDepthMode_None) {
self.depthRenderBufferID = [self createRenderBuffer];
if (self.depthRenderBufferID == kInvaildBufferID) {
return;
}
[self renderBufferStrogeWithRenderID:self.depthRenderBufferID];
[self attachRenderBufferToFrameBufferWithRenderBufferID:self.depthRenderBufferID
attachment:GL_DEPTH_ATTACHMENT];
}
步驟分解:
第一步箕般,創(chuàng)建并綁定深度渲染緩存耐薯,對(duì)應(yīng)程序代碼為:
self.depthRenderBufferID = [self createRenderBuffer];
- (GLuint)createRenderBuffer {
GLuint ID = kInvaildBufferID;
glGenRenderbuffers(RenderMemoryBlock, &ID); // 申請(qǐng) Render Buffer
glBindRenderbuffer(GL_RENDERBUFFER, ID); // 創(chuàng)建 Render Buffer
return ID;
}
第二步哮塞,存儲(chǔ)新創(chuàng)建的渲染緩存碌上,對(duì)應(yīng)程序代碼為:
[self renderBufferStrogeWithRenderID:self.depthRenderBufferID];
- (void)renderBufferStrogeWithRenderID:(GLuint)renderBufferID {
if (renderBufferID == self.colorRenderBufferID) {
// 必須要在 glbindRenderBuffer 之后 (就是使用 Render Buffer 之后), 再綁定渲染的圖層
[self bindDrawableObjectToRenderBuffer];
self.renderBufferSize = [self getRenderBufferSize];
}
if (renderBufferID == self.depthRenderBufferID) {
glRenderbufferStorage(GL_RENDERBUFFER,
GL_DEPTH_COMPONENT16,
self.renderBufferSize.width,
self.renderBufferSize.height);
}
}
核心函數(shù):存儲(chǔ)渲染信息
glRenderbufferStorage | |
---|---|
void glRenderbufferStorage(GLenum target,GLenum internalformat,GLsizei width, GLsizei height) | |
target 只能是 GL_RENDERBUFFER | |
internalformat 可用選項(xiàng)見(jiàn)下表 | |
width 渲染緩存的寬度(像素單位) | |
height 渲染緩存的高度(像素單位) |
internalformat | 存儲(chǔ)格式(位 = bit) |
---|---|
顏色方面 | GL_RGB565(5 + 6 + 5 = 16位)、GL_RGBA4(4 x 4 = 16)绊寻、GL_RGB5_A1(5 + 5 + 5 + 1 = 16)丙者、GL_RGB8_OES(3 x 8 = 24 )复斥、GL_RGBA8_OES(4 x 8 = 32) |
深度方面 | GL_DEPTH_COMPONENT16(16位)、GL_DEPTH_COMPONENT24_OES(24位)械媒、GL_DEPTH_COMPONENT32_OES(32位) |
模板方面 | GL_STENCIL_INDEX8目锭、GL_STENCIL_INDEX1_OES、GL_STENCIL_INDEX4_OES |
深度與模板 | GL_DEPTH24_STENCIL8_OES |
第三步纷捞,裝載渲染緩存到幀緩存中痢虹,對(duì)應(yīng)程序代碼為:
[self attachRenderBufferToFrameBufferWithRenderBufferID:self.depthRenderBufferID
attachment:GL_DEPTH_ATTACHMENT];
- (void)attachRenderBufferToFrameBufferWithRenderBufferID:(GLuint)renderBufferID attachment:(GLenum)attachment {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, renderBufferID);
}
2. 數(shù)據(jù)源的編寫與綁定
數(shù)據(jù)源的書寫
從 2D 到 3D :
右下方,線框正方體的 8 個(gè)頂點(diǎn)坐標(biāo)分布主儡,其實(shí) 0~7 的編號(hào)是你決定的奖唯,也就是說(shuō) 0 放在那里開(kāi)始都是可以的,只要是 8 個(gè)點(diǎn)即可糜值;
static const VFVertex vertices[] = {
// Front
// 0 [UIColor colorWithRed:0.438 green:0.786 blue:1.000 alpha:1.000]
{{ 1.0, -1.0, 1.0}, {0.438, 0.786, 1.000, 1.000}}, // 淡(藍(lán)) -- 0
// 1 [UIColor colorWithRed:1.000 green:0.557 blue:0.246 alpha:1.000]
{{ 1.0, 1.0, 1.0}, {1.000, 0.557, 0.246, 1.000}}, // 淡(橙) -- 1
// 2 [UIColor colorWithRed:0.357 green:0.927 blue:0.690 alpha:1.000]
{{-1.0, 1.0, 1.0}, {0.357, 0.927, 0.690, 1.000}}, // 藍(lán)(綠) -- 2
// 3 [UIColor colorWithRed:0.860 green:0.890 blue:0.897 alpha:1.000]
{{-1.0, -1.0, 1.0}, {0.860, 0.890, 0.897, 1.000}}, // 超淡藍(lán) 偏(白) -- 3
// Back
// 4 [UIColor colorWithRed:0.860 green:0.890 blue:0.897 alpha:1.000]
{{-1.0, -1.0, -1.0}, {0.860, 0.890, 0.897, 1.000}}, // 超淡藍(lán) 偏(白) -- 4
// 5 [UIColor colorWithRed:0.357 green:0.927 blue:0.690 alpha:1.000]
{{-1.0, 1.0, -1.0}, {0.357, 0.927, 0.690, 1.000}}, // 藍(lán)(綠) -- 5
// 6 [UIColor colorWithRed:1.000 green:0.557 blue:0.246 alpha:1.000]
{{ 1.0, 1.0, -1.0}, {1.000, 0.557, 0.246, 1.000}}, // 淡(橙) -- 6
// 7 [UIColor colorWithRed:0.438 green:0.786 blue:1.000 alpha:1.000]
{{ 1.0, -1.0, -1.0}, {0.438, 0.786, 1.000, 1.000}}, // 淡(藍(lán)) -- 7
};
只要你空間想像不是特別差丰捷,估計(jì)能看出每個(gè)點(diǎn)的坐標(biāo)吧!你可以把這樣的點(diǎn) { 1.0, -1.0, -1.0} 改成你喜歡的數(shù)值亦可寂汇,只要最終是正方體即可病往;
真正重要的數(shù)據(jù)其實(shí)是下標(biāo)數(shù)據(jù):
static const GLubyte indices[] = {
// Front ------------- 藍(lán)橙綠白 中間線(藍(lán)綠)
0, 1, 2, // 藍(lán)橙綠
2, 3, 0, // 綠白藍(lán)
// Back ------------- 藍(lán)橙綠白 中間線(白橙)
4, 5, 6, // 白綠橙
6, 7, 4, // 橙藍(lán)白
// Left ------------- 白綠
3, 2, 5, // 白綠綠
5, 4, 3, // 綠白白
// Right ------------- 藍(lán)橙
7, 6, 1, // 藍(lán)橙橙
1, 0, 7, // 橙藍(lán)藍(lán)
// Top ------------- 橙綠
1, 6, 5, // 橙橙綠
5, 2, 1, // 綠綠橙
// Bottom ------------- 白藍(lán)
3, 4, 7, // 白白藍(lán)
7, 0, 3 // 藍(lán)藍(lán)白
};
這些下標(biāo)的值由兩個(gè)因素決定,第一個(gè)因素是上面 8 個(gè)頂點(diǎn)數(shù)據(jù)的下標(biāo)骄瓣;第二個(gè)因素是時(shí)鐘方向停巷;
現(xiàn)在看看時(shí)鐘方向:
有沒(méi)有發(fā)現(xiàn),每一個(gè)正方形的兩個(gè)小三角,都是逆時(shí)針?lè)较虻呐锨冢划?dāng)然你也可以換成順時(shí)針?lè)较蚶俑鳎鄳?yīng)的下標(biāo)數(shù)據(jù)就要發(fā)生改變;
EP: 如 Front 這個(gè)面硼被,如果使用順時(shí)針來(lái)寫數(shù)據(jù)為:
// Front ------------- 白綠橙藍(lán) 中間線(白橙)
3, 2, 1, // 白綠橙
1, 0, 2, // 橙藍(lán)綠
你也可以從 2 或 1 開(kāi)始示损,看你的喜好咯;
方向只有兩個(gè):
資源綁定
這里主要是 VBO 的數(shù)據(jù)綁定嚷硫,增加 Element 的支持而已检访;
[self bindVertexDatasWithVertexBufferID:kInvaildBufferID
bufferTarget:GL_ELEMENT_ARRAY_BUFFER
dataSize:sizeof(indices)
data:indices
elements:YES];
- (void)bindVertexDatasWithVertexBufferID:(GLuint)vertexBufferID bufferTarget:(GLenum)target dataSize:(GLsizeiptr)size data:(const GLvoid *)data elements:(BOOL)isElement {
if ( ! isElement) {
glBindBuffer(target, vertexBufferID);
}
// 創(chuàng)建 資源 ( context )
glBufferData(target, // 緩存塊 類型
size, // 創(chuàng)建的 緩存塊 尺寸
data, // 要綁定的頂點(diǎn)數(shù)據(jù)
GL_STATIC_DRAW); // 緩存塊 用途
}
此處不再贅述;
如果實(shí)在不懂仔掸,請(qǐng)移步至
《OpenGL ES 2.0 (iOS)[03]:熟練圖元繪制脆贵,玩轉(zhuǎn)二維圖形》練習(xí)練習(xí);
3. 深度測(cè)試與繪制
清除舊的深度緩存信息
[self clearColorRenderBuffer:YES depth:YES stencil:NO];
- (void)clearColorRenderBuffer:(BOOL)color depth:(BOOL)depth stencil:(BOOL)stencil {
GLbitfield colorBit = 0;
GLbitfield depthBit = 0;
GLbitfield stencilBit = 0;
if (color) { colorBit = GL_COLOR_BUFFER_BIT; }
if (depth) { depthBit = GL_DEPTH_BUFFER_BIT; }
if (stencil) { stencilBit = GL_STENCIL_BUFFER_BIT; }
glClear(colorBit | depthBit | stencilBit);
}
啟用深度測(cè)試
[self enableDepthTesting];
- (void)enableDepthTesting {
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
}
這里多了一個(gè) GL_CULL_FACE 的啟用起暮,它的意思就是卖氨,把看不見(jiàn)的像素信息剔除掉,只保留能看見(jiàn)的信息(留前去后)负懦;
如果沒(méi)有啟用 GL_DEPTH_TEST 程序運(yùn)行后是這樣的:
很明顯圖形是有穿透性的筒捺,如果去掉 GL_DEPTH_TEST 就不是實(shí)體的正方體了;當(dāng)然如果你喜歡這種效果纸厉,也可以關(guān)掉 GL_DEPTH_TEST (反正我個(gè)人覺(jué)得關(guān)掉也蠻好看的)系吭;
重新綁定 Color Render Buffer
原因,因?yàn)楫?dāng)綁定 Depth Render Buffer 之后颗品,渲染管線從原來(lái)的綁定(激活)的 Color Render Buffer 切換成了肯尺,綁定(激活)Depth Render Buffer ,從而導(dǎo)致渲染出來(lái)的結(jié)果躯枢,不是期望中的那樣则吟;所以在繪制前要重新綁定(激活)Color Render Buffer .
- (void)drawCube {
// 失敗的核心原因
// 因?yàn)?depth buffer 是最后一個(gè)綁定的,所以當(dāng)前渲染的 buffer 變成了 depth 而不是 color
// 所以 渲染的圖形沒(méi)有任何變化锄蹂,無(wú)法產(chǎn)生深度效果
// Make the Color Render Buffer the current buffer for display
[self rebindRenderBuffer:@[@(self.colorRenderBufferID)]];
[self rebindVertexBuffer:@[@(self.vboBufferID)]];
glDrawElements(GL_TRIANGLES,
sizeof(indices) / sizeof(indices[0]),
GL_UNSIGNED_BYTE,
indices);
}
這是注釋了代碼中氓仲,[self rebindRenderBuffer:@[@(self.colorRenderBufferID)]];
的運(yùn)行結(jié)果;
4. 讓正方體動(dòng)起來(lái)
ViewController 的調(diào)度
其實(shí)就是得糜,view 顯示的時(shí)候更新敬扛,不顯示的時(shí)候停止更新;
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.cubeView update];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.cubeView pauseUpdate];
}
CubeView 的應(yīng)用
#pragma mark - DisplayLink Update
- (void)preferTransformsWithTimes:(NSTimeInterval)time {
GLfloat rotateX = self.modelRotate.x;
// rotateX += M_PI_4 * time;
GLfloat rotateY = self.modelRotate.y;
rotateY += M_PI_2 * time;
GLfloat rotateZ = self.modelRotate.z;
rotateZ += M_PI * time;
self.modelRotate = GLKVector3Make(rotateX, rotateY, rotateZ);
}
本類提供的改變參數(shù)有:
@property (assign, nonatomic) GLKVector3 modelPosition, modelRotate, modelScale;
@property (assign, nonatomic) GLKVector3 viewPosition , viewRotate , viewScale ;
@property (assign, nonatomic) GLfloat projectionFov, projectionScaleFix, projectionNearZ, projectionFarZ;
已經(jīng)包含了所有的變換操作掀亩;
以下的幾個(gè)方法均是處理 VFRedisplay 類的實(shí)時(shí)更新問(wèn)題;
// <VFRedisplayDelegate>
- (void)updateContentsWithTimes:(NSTimeInterval)times {
[self preferTransformsWithTimes:times];
[self drawAndRender];
}
#pragma mark - Update
- (void)update {
self.displayUpdate = [[VFRedisplay alloc] init];
self.displayUpdate.delegate = self;
self.displayUpdate.preferredFramesPerSecond = 25;
self.displayUpdate.updateContentTimes = arc4random_uniform(650) / 10000.0;
[self.displayUpdate startUpdate];
}
- (void)pauseUpdate {
[self.displayUpdate pauseUpdate];
}
#pragma mark - Dealloc
- (void)dealloc {
[self.displayUpdate endUpdate];
}
self.displayUpdate.preferredFramesPerSecond = 25; //更新頻率
self.displayUpdate.updateContentTimes = arc4random_uniform(650) / 10000.0; // 控制變化率(快慢)
核心是 - (void)updateContentsWithTimes:(NSTimeInterval)times
方法欢顷,這個(gè)方法是用于更新時(shí)槽棍,實(shí)時(shí)調(diào)用的方法;由VFRedisplay
類提供的協(xié)議 @interface VFGLCubeView ()<VFRedisplayDelegate>
方法;
VFRedisplay.h 主要內(nèi)容
@protocol VFRedisplayDelegate <NSObject>
- (void)updateContentsWithTimes:(NSTimeInterval)times;
@end
......
- (void)startUpdate;
- (void)pauseUpdate;
- (void)endUpdate;
VFRedisplay.m 主要內(nèi)容
開(kāi)始更新的方法:
- (void)startUpdate {
if ( ! self.delegate ) {
return;
}
self.displayLink = [CADisplayLink displayLinkWithTarget:self
selector:@selector(displayContents:)];
self.displayLink.frameInterval = (NSUInteger)MAX(kLeastSeconds,
(kTotalSeconds / self.preferredFramesPerSecond));
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
self.displayPause = kDefaultDisplayPause;
}
- (void)displayContents:(CADisplayLink *)sender {
if ([self.delegate respondsToSelector:@selector(updateContentsWithTimes:)]) {
[self.delegate updateContentsWithTimes:self.updateContentTimes];
}
}
四步走:
第一步,創(chuàng)建相應(yīng)的更新調(diào)度方法- (void)displayContents:(CADisplayLink *)sender
炼七,這個(gè)方法必須是- (void)selector:(CADisplayLink *)sender
這種類型的缆巧;
第二步,指定一個(gè)更新頻率(就是一秒更新多少次)frameInterval
一般是 24豌拙、25陕悬、30,默認(rèn)是 30 的按傅;
第三步捉超,把 CADisplayLink 的子類添加到當(dāng)前的 RunLoop [NSRunLoop currentRunLoop]
上,不然程序是無(wú)法調(diào)度指定的方法的唯绍;
第四步拼岳,啟動(dòng)更新 static const BOOL kDefaultDisplayPause = NO;
;
displayPause 屬性
@property (assign, nonatomic) BOOL displayPause; @dynamic displayPause; - (void)setDisplayPause:(BOOL)displayPause { self.displayLink.paused = displayPause; } - (BOOL)displayPause { return self.displayLink.paused; }
停止更新的方法:
- (void)pauseUpdate {
self.displayPause = YES;
}
結(jié)束更新的方法:
- (void)endUpdate {
self.displayPause = YES;
[self.displayLink invalidate];
[self.displayLink removeFromRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
}
不用的時(shí)候况芒,當(dāng)然要先停止更新惜纸,再關(guān)掉時(shí)鐘(CADisplayLink 就是一個(gè)時(shí)鐘類),最后要從當(dāng)前 RunLoop 中移除绝骚;
5. 工程文件
Github: DrawCube
Github:DrawCube_Onestep
增加魔方色開(kāi)關(guān)耐版,RubikCubeColor 宏定義;
三压汪、參考書籍粪牲、文章
《OpenGL ES 2 Programming Guide》
《OpenGL Programming Guide》8th
《Learning OpenGL ES For iOS》
RW.OpenGLES2.0