這次要做的是一個(gè)立方體乎莉,在學(xué)完紋理之后其實(shí)大家應(yīng)該能自己實(shí)現(xiàn)一個(gè)立方體,也就是把Z軸利用上就可以實(shí)現(xiàn)了。唯一的難點(diǎn)是這里涉及一個(gè)深度測試讼载,如果不加深度測試渲染出來的立方體會是一個(gè)有缺失面的立方體。現(xiàn)在我們進(jìn)入正題中跌,和以前一樣我們用兩套代碼來實(shí)現(xiàn)咨堤。
學(xué)習(xí)的代碼都在我的github倉庫歡迎大家學(xué)習(xí)指教!
不使用GLKit的方式
一漩符、設(shè)置頂點(diǎn)和紋理數(shù)據(jù)
為了避免重復(fù)同樣的話一喘,前面講過的知識點(diǎn)將不再說明,我們是在上一篇代碼的基礎(chǔ)之上進(jìn)行修改的嗜暴。前面我們已經(jīng)做出了一個(gè)紋理凸克,只要用六個(gè)面就可以拼一個(gè)立方體,和單個(gè)紋理面的區(qū)別是空間坐標(biāo)不一樣而已∶屏ぃ現(xiàn)將頂點(diǎn)坐標(biāo)數(shù)組寫成以下方式:
// 頂點(diǎn)結(jié)構(gòu)體
typedef struct{
GLfloat position[3];
GLfloat texturePosion[2];
} Vertex;
const Vertex vertexes[] = {
// 頂點(diǎn) 紋理
// 前面
{{-0.5f, 0.5f, 0.5f}, {0.0f, 0.0f}}, // 前左上 0
{{-0.5f, -0.5f, 0.5f}, {0.0f, 1.0f}}, // 前左下 1
{{0.5f, -0.5f, 0.5f}, {1.0f, 1.0f}}, // 前右下 2
{{0.5f, 0.5f, 0.5f}, {1.0f, 0.0f}}, // 前右上 3
// 后面
{{-0.5f, 0.5f, -0.5f}, {1.0f, 0.0f}}, // 后左上 4
{{-0.5f, -0.5f, -0.5f}, {1.0f, 1.0f}}, // 后左下 5
{{0.5f, -0.5f, -0.5f}, {0.0f, 1.0f}}, // 后右下 6
{{0.5f, 0.5f, -0.5f}, {0.0f, 0.0f}}, // 后右上 7
// 左面
{{-0.5f, 0.5f, -0.5f}, {0.0f, 0.0f}}, // 后左上 8
{{-0.5f, -0.5f, -0.5f}, {0.0f, 1.0f}}, // 后左下 9
{{-0.5f, 0.5f, 0.5f}, {1.0f, 0.0f}}, // 前左上 10
{{-0.5f, -0.5f, 0.5f}, {1.0f, 1.0f}}, // 前左下 11
// 右面
{{0.5f, 0.5f, 0.5f}, {0.0f, 0.0f}}, // 前右上 12
{{0.5f, -0.5f, 0.5f}, {0.0f, 1.0f}}, // 前右下 13
{{0.5f, -0.5f, -0.5f}, {1.0f, 1.0f}}, // 后右下 14
{{0.5f, 0.5f, -0.5f}, {1.0f, 0.0f}}, // 后右上 15
// 上面
{{-0.5f, 0.5f, -0.5f}, {0.0f, 0.0f}}, // 后左上 16
{{-0.5f, 0.5f, 0.5f}, {0.0f, 1.0f}}, // 前左上 17
{{0.5f, 0.5f, 0.5f}, {1.0f, 1.0f}}, // 前右上 18
{{0.5f, 0.5f, -0.5f}, {1.0f, 0.0f}}, // 后右上 19
// 下面
{{-0.5f, -0.5f, 0.5f}, {0.0f, 0.0f}}, // 前左下 20
{{0.5f, -0.5f, 0.5f}, {1.0f, 0.0f}}, // 前右下 21
{{-0.5f, -0.5f, -0.5f}, {0.0f, 1.0f}}, // 后左下 22
{{0.5f, -0.5f, -0.5f}, {1.0f, 1.0f}}, // 后右下 23
};
// 頂點(diǎn)索引
const GLbyte indexes[] = {
// 前面
0, 1, 2,
0, 2, 3,
// 后面
4, 5, 6,
4, 6, 7,
// 左面
8, 9, 11,
8, 11, 10,
// 右面
12, 13, 14,
12, 14, 15,
// 上面
16, 17, 18,
16, 18, 19,
// 下面
20, 22, 23,
20, 23, 21,
};
你們可能已經(jīng)看到了萎战,這次的頂點(diǎn)坐標(biāo)和上一篇筆記的不一樣,這里是把頂點(diǎn)對應(yīng)的紋理坐標(biāo)也寫在一起了舆逃。這樣寫我們也可以學(xué)習(xí)另一種方式設(shè)置頂點(diǎn)和紋理坐標(biāo)蚂维,這里我們還用到了索引,這樣有可以實(shí)現(xiàn)頂點(diǎn)數(shù)據(jù)的重復(fù)利用颖侄。
二鸟雏、設(shè)置VBO(頂點(diǎn)緩存)
這次繪制立方體的時(shí)候有沒有會發(fā)現(xiàn)頂點(diǎn)數(shù)據(jù)比原來多很多!其實(shí)這還算是少的览祖。簡單的圖形我們可以通過手寫頂點(diǎn)數(shù)據(jù)來實(shí)現(xiàn)孝鹊,但是如果是一個(gè)復(fù)雜的圖形就不行了,我們在玩大型游戲的時(shí)候都看過人物的結(jié)構(gòu)很復(fù)雜展蒂,這個(gè)用我們手寫頂點(diǎn)數(shù)據(jù)是不可能實(shí)現(xiàn)的又活!一般是由設(shè)計(jì)師用建模工具來實(shí)現(xiàn)的,最終的文件還是頂點(diǎn)數(shù)據(jù)锰悼。這些數(shù)據(jù)是在內(nèi)存中的柳骄,只有在繪制的時(shí)候才會從CPU傳給GPU,這種數(shù)據(jù)傳遞本身的性能也不是很高箕般,當(dāng)頂點(diǎn)數(shù)據(jù)很大的時(shí)候?qū)π阅艿挠绊懜黠@耐薯。一般的優(yōu)化方案就是將頂點(diǎn)數(shù)據(jù)緩存的GPU的顯存里,這樣不用每次從CPU傳遞頂點(diǎn)數(shù)據(jù)到GPU從而提高圖形的性能。下面我們就用代碼實(shí)現(xiàn)VBO:
/**
設(shè)置頂點(diǎn)緩存
*/
- (void)setupVBO{
// 設(shè)置VBO
// 設(shè)置頂點(diǎn)緩存
GLuint bufferVBO;
glGenBuffers(1, &bufferVBO);
glBindBuffer(GL_ARRAY_BUFFER, bufferVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexes), vertexes, GL_STATIC_DRAW);
// 設(shè)置索引緩存
GLuint bufferIndex;
glGenBuffers(1, &bufferIndex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIndex);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexes), indexes, GL_STATIC_DRAW);
// 設(shè)置頂點(diǎn)
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
glEnableVertexAttribArray(GLKVertexAttribPosition);
// 設(shè)置紋理坐標(biāo)
glVertexAttribPointer(GLKVertexAttribTexCoord0 , 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
}
三曲初、引入矩陣變換的庫
在圖像的世界里圖像的放大体谒、縮小、位移都是通過圖片乘以一個(gè)矩陣來實(shí)現(xiàn)的(通過矩陣相乘效率更高)你在iOS開發(fā)中是否遇到過圖形的transform屬性臼婆,你打印一下看看里面的結(jié)構(gòu)你就會明白抒痒,這也是一個(gè)矩陣數(shù)據(jù)。這個(gè)涉及線性代數(shù)的東西颁褂,你大學(xué)里學(xué)的線性代數(shù)的東西是不是已經(jīng)忘記了故响?不過不用怕網(wǎng)上已經(jīng)有很多矩陣計(jì)算相關(guān)的庫,我們直接用就可以了颁独。其實(shí)GLKit里也有矩陣計(jì)算的庫,因?yàn)槲覀冞@里不打算用GLKit所以我們用一個(gè)別人寫好的庫彩届。可以從這里下載這個(gè)庫。下載圖片中的這幾個(gè)文件奖唯。
四惨缆、實(shí)現(xiàn)觸摸來滾動立方體
如果我們不添加讓立方體滾動的功能就只能看到一個(gè)面,看不出立方體的效果丰捷,而且深度測試的效果也看不出來坯墨。滾動的實(shí)現(xiàn)方式也很簡單讓其乘以一個(gè)矩陣即可,我們用兩個(gè)變量來記錄手指在屏幕上劃動的量變病往。代碼如下:
// 限制上下轉(zhuǎn)動的角度
#define kLimitDegreeUpDown 40.0
@interface ViewController ()
// 記錄x軸方向上的變量
@property(nonatomic,assign)GLfloat degreeX;
// 記錄y軸方向上的變量
@property(nonatomic,assign)GLfloat degreeY;
@end
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
UITouch * touch = touches.anyObject;
CGPoint currentPoint = [touch locationInView:self.view];
CGPoint previousPoint = [touch previousLocationInView:self.view];
self.degreeX += previousPoint.y - currentPoint.y;
// 限制上下轉(zhuǎn)動的角度
if (self.degreeX > kLimitDegreeUpDown) {
self.degreeX = kLimitDegreeUpDown;
}
if (self.degreeX < -kLimitDegreeUpDown) {
self.degreeX = -kLimitDegreeUpDown;
}
self.degreeY += previousPoint.x - currentPoint.x;
}
五捣染、設(shè)置視角
可能你會在上一個(gè)筆記中看到我們創(chuàng)建的紋理是一個(gè)長方形的,但我們設(shè)置的坐標(biāo)應(yīng)該出現(xiàn)的是一個(gè)正方形才對停巷。其實(shí)我以前也糾結(jié)這個(gè)問題耍攘,查了很多資料都沒找到相關(guān)說明,后來在我無意間設(shè)置完視角發(fā)現(xiàn)一切都正常了畔勤,所以我們要對代碼進(jìn)行以下更改:
- 將頂點(diǎn)著色器文件修改如下
attribute vec4 myPosition;
// 添加物體運(yùn)動的變換矩陣
uniform mat4 modelView;
// 添加視角的變換矩陣
uniform mat4 projection;
attribute vec2 textureCoordsIn;
varying vec2 textureCoordsOut;
void main()
{
// 將原來的頂點(diǎn)乘以矩陣數(shù)據(jù)蕾各,注意乘的前后順序
gl_Position = projection * modelView * myPosition;
textureCoordsOut = textureCoordsIn;
}
- 添加相應(yīng)的槽
// 添加以下成員變量
GLuint _modelViewSlot; // 物體變換的槽
GLuint _projectionSlot; // 攝像機(jī)的槽
// 在原來設(shè)置槽的位置添加以下代碼
_modelViewSlot = glGetUniformLocation(_program, "modelView");
_projectionSlot = glGetUniformLocation(_program, "projection");
- 用一個(gè)方法來傳遞視角數(shù)據(jù)和用一個(gè)方法來傳遞圖形的變換矩陣,該方法在render方法里調(diào)用
- (void)setupPerspactive{
GLfloat aspect = self.view.frame.size.width / self.view.frame.size.height;
ksMatrix4 tempMatrix;
ksMatrixLoadIdentity(&tempMatrix);
// 設(shè)置視角矩陣
ksPerspective(&tempMatrix, 60, aspect, 0.1f, 10.0f);
// 傳遞視角矩陣數(shù)據(jù)
glUniformMatrix4fv(_projectionSlot, 1 , GL_FALSE, (GLfloat *)&tempMatrix.m[0][0]);
}
/**
設(shè)置圖形的變換矩陣
*/
- (void)setupModelViewMatrix{
// 設(shè)置物體的變換
ksMatrixLoadIdentity(&_matrix4);
// 遠(yuǎn)離視野庆揪,不然是在視角會在立方體的中心
ksMatrixTranslate(&_matrix4, 0, 0, -3);
// x方向旋轉(zhuǎn)
ksMatrixRotate(&_matrix4, self.degreeX, 1, 0, 0);
// y方向旋轉(zhuǎn)
ksMatrixRotate(&_matrix4, self.degreeY, 0, 1, 0);
glUniformMatrix4fv(_modelViewSlot, 1, GL_FALSE, (GLfloat *)&_matrix4.m[0][0]);
}
六式曲、實(shí)現(xiàn)視圖的刷新
前面我們的代碼都是只渲染一次,在修改了數(shù)據(jù)后視圖是不會改變的缸榛。為了能及時(shí)渲染每次修改的數(shù)據(jù)我們要用CADisplayLink來不停渲染數(shù)據(jù)吝羞。實(shí)現(xiàn)代碼正如下:
@interface ViewController ()
@property (nonatomic,strong)CADisplayLink * link;
@end
- (void)viewDidLoad {
[super viewDidLoad];
// 設(shè)置視圖刷新
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(render)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
修改渲染方法,因?yàn)槲覀兏某闪耸褂盟饕齺砝L制圖形内颗,所以繪制方法也需要修改钧排,修改后的渲染方法如下:
/**
渲染
*/
- (void)render {
[self setupPerspactive];
[self setupModelViewMatrix];
glViewport(0, 0, self.view.frame.size.width, self.view.frame.size.height);
glClearColor(1.0, 1.0, 1.0, 1.0);
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 用索引要設(shè)置要繪制的方法
glDrawElements(GL_TRIANGLES, sizeof(indexes)/sizeof(indexes[0]), GL_UNSIGNED_BYTE, 0);
[_context presentRenderbuffer:GL_RENDERBUFFER];
}
七、實(shí)現(xiàn)深度測試
通過以上步驟我們已經(jīng)可實(shí)現(xiàn)一個(gè)半成品了均澳,我們先運(yùn)行看一下結(jié)果恨溜。
你可能已經(jīng)看到了問題:有的面有缺失符衔。對于該問題是因?yàn)楹箐秩镜拿娓采w了先前渲染的面所造成的,解決方法是設(shè)置深度測試即可糟袁。這樣在渲染的時(shí)候會檢測各個(gè)面的前后關(guān)系柏腻,在后面被擋住的面將不會渲染,從而來解決此問題系吭。深度測試的代碼如下:
- 在原來設(shè)置渲染緩存的方法里添加深度測試緩存代碼
- (void)setupFrameAndRenderBuffer {
// 申請渲染緩存
glGenRenderbuffers(1, &_renderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
// 該方法最好在綁定渲染后立即設(shè)置,不然后面會被綁定為深度渲染緩存
[_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_glLayer];
// 設(shè)置深度調(diào)試
GLint width;
GLint height;
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &width);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &height);
// 申請深度渲染緩存
glGenRenderbuffers(1, &_depthRenderBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthRenderBuffer);
// 設(shè)置深度測試的存儲信息
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
// 申請幀緩存
glGenFramebuffers(1, &_frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _renderBuffer);
// 將渲染緩存掛載到GL_DEPTH_ATTACHMENT這個(gè)掛載點(diǎn)上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, _depthRenderBuffer);
// GL_RENDERBUFFER綁定的是深度測試渲染緩存颗品,所以要綁定回色彩渲染緩存
glBindRenderbuffer(GL_RENDERBUFFER, _renderBuffer);
// 檢查幀緩存狀態(tài)
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
NSLog(@"Error: Frame buffer is not completed.");
exit(1);
}
}
- 在渲染方法里將原來的清空緩存區(qū)數(shù)據(jù)的方法改成以下方法
glEnable(GL_DEPTH_TEST);
// 清空色彩數(shù)據(jù)的同時(shí)清空深度數(shù)據(jù)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
運(yùn)行結(jié)果如下:
實(shí)現(xiàn)只有線連成的方法體
我在評論區(qū)看到有人想問的問題可能是在問怎么實(shí)現(xiàn)用線繪制立方體肯尺,就在這里補(bǔ)充一下評論區(qū)里的問題的答案。我們目前都是通過將頂點(diǎn)繪制成三角形的方式來實(shí)現(xiàn)一些圖形效果躯枢,除此之外我們還可以將頂點(diǎn)數(shù)據(jù)繪制成點(diǎn)(也可以說成點(diǎn)精靈)或者直線则吟。這次我們用直線實(shí)現(xiàn),首先將頂點(diǎn)和索引數(shù)據(jù)要修改如下:
const GLfloat vertexes[] = {
-0.5f, 0.5f, 0.5f, // 前左上 0
-0.5f, -0.5f, 0.5f, // 前左下 1
0.5f, -0.5f, 0.5f, // 前右下 2
0.5f, 0.5f, 0.5f, // 前右上 3
// 后面
-0.5f, 0.5f, -0.5f, // 后左上 4
-0.5f, -0.5f, -0.5f, // 后左下 5
0.5f, -0.5f, -0.5f, // 后右下 6
0.5f, 0.5f, -0.5f // 后右上 7
};
const GLbyte indexes[] = {
0, 1,
1, 2,
2, 3,
3, 0,
4, 5,
5, 6,
6, 7,
7, 4,
0, 4,
1, 5,
2, 6,
3, 7
};
由于我們的頂點(diǎn)數(shù)據(jù)沒有了紋理坐標(biāo)锄蹂,指針的跨度由5變成了3所以我們要將設(shè)置頂點(diǎn)參數(shù)的代碼修改如下:
glVertexAttribPointer(_vertexSlot, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 3, NULL);
我們的片元著色器也得修改氓仲,因?yàn)樵瓉硎菑募y理中獲取顏色數(shù)據(jù)而現(xiàn)在我們直接設(shè)置顏色數(shù)據(jù),將著色器修改如下:
precision mediump float;
void main()
{
// 我們在這里將顏色設(shè)成了紅色得糜,下面4個(gè)數(shù)分別對應(yīng)RGBA
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
上面說過我們這次要繪制成直線敬扛,所以在繪制的方法里將GL_TRIANGLES修改成GL_LINES,表示用線繪制朝抖。
glDrawElements(GL_LINES, sizeof(indexes) / sizeof(indexes[0]), GL_UNSIGNED_BYTE, 0);
好了啥箭,現(xiàn)在運(yùn)行一下代碼看看效果吧。代碼也同步更新了治宣,可以在我的github下載急侥。
知識擴(kuò)展
后面會講到天空盒子,其實(shí)天空盒子就是一個(gè)立方體只是視角在立方體的中心而已侮邀,還記得我們設(shè)置圖形變換矩陣的時(shí)候有一行這樣的代碼:
// 遠(yuǎn)離視野坏怪,不然是在視角會在立方體的中心
ksMatrixTranslate(&_matrix4, 0, 0, -3);
這是為了看到圖形的全貌才設(shè)置的讓其在Z軸上遠(yuǎn)離我們,如果將這行代碼注釋了你就會看到我們的視角在立方體的中心绊茧,平面是雙面顯示的所以在立方體中心也能看到美女的圖片(后面我們會講到面剔除讓其只在一面顯示從而提高性能)铝宵,你可以設(shè)置每個(gè)面都不同就可以實(shí)現(xiàn)天空盒子了,這個(gè)先留給大家嘗試一下吧按傅!
使用GLKit的方式
使用GLKit的方法要簡單很多捉超,簡單到不用再寫shader了。上面的原理能講的都已經(jīng)講完了唯绍,這里就只提供相關(guān)代碼和注釋拼岳。需要說明的會在代碼注釋里。
// 限制上下轉(zhuǎn)動的角度
#define kLimitDegreeUpDown 40.0
// 頂點(diǎn)結(jié)構(gòu)體
typedef struct{
GLfloat position[3];
GLfloat texturePosion[2];
} Vertex;
const Vertex vertexes[] = {
// 頂點(diǎn) 紋理
// 前面
{{-0.5f, 0.5f, 0.5f}, {0.0f, 1.0f}}, // 前左上 0
{{-0.5f, -0.5f, 0.5f}, {0.0f, 0.0f}}, // 前左下 1
{{0.5f, -0.5f, 0.5f}, {1.0f, 0.0f}}, // 前右下 2
{{0.5f, 0.5f, 0.5f}, {1.0f, 1.0f}}, // 前右上 3
// 后面
{{-0.5f, 0.5f, -0.5f}, {1.0f, 1.0f}}, // 后左上 4
{{-0.5f, -0.5f, -0.5f}, {1.0f, 0.0f}}, // 后左下 5
{{0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}}, // 后右下 6
{{0.5f, 0.5f, -0.5f}, {0.0f, 1.0f}}, // 后右上 7
// 左面
{{-0.5f, 0.5f, -0.5f}, {0.0f, 1.0f}}, // 后左上 8
{{-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}}, // 后左下 9
{{-0.5f, 0.5f, 0.5f}, {1.0f, 1.0f}}, // 前左上 10
{{-0.5f, -0.5f, 0.5f}, {1.0f, 0.0f}}, // 前左下 11
// 右面
{{0.5f, 0.5f, 0.5f}, {0.0f, 1.0f}}, // 前右上 12
{{0.5f, -0.5f, 0.5f}, {0.0f, 0.0f}}, // 前右下 13
{{0.5f, -0.5f, -0.5f}, {1.0f, 0.0f}}, // 后右下 14
{{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f}}, // 后右上 15
// 上面
{{-0.5f, 0.5f, -0.5f}, {0.0f, 1.0f}}, // 后左上 16
{{-0.5f, 0.5f, 0.5f}, {0.0f, 0.0f}}, // 前左上 17
{{0.5f, 0.5f, 0.5f}, {1.0f, 0.0f}}, // 前右上 18
{{0.5f, 0.5f, -0.5f}, {1.0f, 1.0f}}, // 后右上 19
// 下面
{{-0.5f, -0.5f, 0.5f}, {0.0f, 1.0f}}, // 前左下 20
{{0.5f, -0.5f, 0.5f}, {1.0f, 1.0f}}, // 前右下 21
{{-0.5f, -0.5f, -0.5f}, {0.0f, 0.0f}}, // 后左下 22
{{0.5f, -0.5f, -0.5f}, {1.0f, 0.0f}}, // 后右下 23
};
const GLbyte indexes[] = {
// 前面
0, 1, 2,
0, 2, 3,
// 后面
4, 5, 6,
4, 6, 7,
// 左面
8, 9, 11,
8, 11, 10,
// 右面
12, 13, 14,
12, 14, 15,
// 上面
16, 17, 18,
16, 18, 19,
// 下面
20, 22, 23,
20, 23, 21,
};
@interface ViewController ()
@property (nonatomic,strong)GLKBaseEffect * effect;
@property(nonatomic,assign)GLfloat degreeX;
@property(nonatomic,assign)GLfloat degreeY;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
GLKView * glView = (GLKView *)self.view;
EAGLContext * contex = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!contex) {
NSLog(@"context創(chuàng)建失敗");
}
if (![EAGLContext setCurrentContext:contex]) {
NSLog(@"設(shè)置當(dāng)前context失敗");
}
glView.context = contex;
glView.drawableDepthFormat = GLKViewDrawableDepthFormat16;
self.effect = [[GLKBaseEffect alloc] init];
// 加載紋理圖片
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:@(1), GLKTextureLoaderOriginBottomLeft, nil];
NSError * error;
CGImageRef image = [UIImage imageNamed:@"girl"].CGImage;
GLKTextureInfo * textureInfo = [GLKTextureLoader textureWithCGImage:image options:options error:&error];
if (error) {
NSLog(@"%@", error);
}
// 設(shè)置紋理可用
self.effect.texture2d0.enabled = GL_TRUE;
// 傳遞紋理信息
self.effect.texture2d0.name = textureInfo.name;
[self setupVBO];
[self setupPerspective];
glClearColor(1.0, 1.0, 1.0, 1.0);
glEnable(GL_DEPTH_TEST);
}
/**
設(shè)置頂點(diǎn)緩存
*/
- (void)setupVBO{
// 設(shè)置VBO
GLuint bufferVBO;
glGenBuffers(1, &bufferVBO);
glBindBuffer(GL_ARRAY_BUFFER, bufferVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexes), vertexes, GL_STATIC_DRAW);
GLuint bufferIndex;
glGenBuffers(1, &bufferIndex);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bufferIndex);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexes), indexes, GL_STATIC_DRAW);
// 設(shè)置頂點(diǎn)
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
glEnableVertexAttribArray(GLKVertexAttribPosition);
// 設(shè)置紋理坐標(biāo)
glVertexAttribPointer(GLKVertexAttribTexCoord0 , 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
}
- (void)setupPerspective{
GLfloat aspect = self.view.frame.size.width / self.view.frame.size.height;
GLKMatrix4 perspective = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(60), aspect, 0.1, 200);
self.effect.transform.projectionMatrix = perspective;
}
- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
UITouch * touch = touches.anyObject;
CGPoint currentPoint = [touch locationInView:self.view];
CGPoint previousPoint = [touch previousLocationInView:self.view];
self.degreeX += currentPoint.y - previousPoint.y;
// 限制上下轉(zhuǎn)動的角度
if (self.degreeX > kLimitDegreeUpDown) {
self.degreeX = kLimitDegreeUpDown;
}
if (self.degreeX < -kLimitDegreeUpDown) {
self.degreeX = -kLimitDegreeUpDown;
}
self.degreeY += currentPoint.x - previousPoint.x;
}
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 遠(yuǎn)離視野况芒,不然是在正方體內(nèi)部
GLKMatrix4 modelMat = GLKMatrix4Translate(GLKMatrix4Identity, 0, 0, -3);
modelMat = GLKMatrix4RotateX(modelMat, GLKMathDegreesToRadians(self.degreeX));
modelMat = GLKMatrix4RotateY(modelMat, GLKMathDegreesToRadians(self.degreeY));
self.effect.transform.modelviewMatrix = modelMat;
[self.effect prepareToDraw];
glDrawElements(GL_TRIANGLES, sizeof(indexes)/sizeof(indexes[0]), GL_UNSIGNED_BYTE, 0);
}