OpenGL ES學(xué)習(xí)筆記之三(創(chuàng)建立方體)

這次要做的是一個(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è)文件奖唯。

矩陣庫圖片.png

四惨缆、實(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é)果恨溜。


沒有深度測試.gif

你可能已經(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é)果如下:


添加深度測試.gif

實(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下載急侥。

立方體.gif

知識擴(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);
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惜纸,一起剝皮案震驚了整個(gè)濱河市叶撒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌耐版,老刑警劉巖祠够,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異粪牲,居然都是意外死亡古瓤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門腺阳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來落君,“玉大人,你說我怎么就攤上這事亭引∫锼伲” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵焙蚓,是天一觀的道長纹冤。 經(jīng)常有香客問我,道長购公,這世上最難降的妖魔是什么萌京? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮君丁,結(jié)果婚禮上枫夺,老公的妹妹穿的比我還像新娘。我一直安慰自己绘闷,他們只是感情好橡庞,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著印蔗,像睡著了一般扒最。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上华嘹,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天吧趣,我揣著相機(jī)與錄音,去河邊找鬼耙厚。 笑死强挫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的薛躬。 我是一名探鬼主播俯渤,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼型宝!你這毒婦竟也來了八匠?” 一聲冷哼從身側(cè)響起絮爷,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎梨树,沒想到半個(gè)月后坑夯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡抡四,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年柜蜈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片指巡。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡跨释,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厌处,到底是詐尸還是另有隱情,我是刑警寧澤岁疼,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布阔涉,位于F島的核電站,受9級特大地震影響捷绒,放射性物質(zhì)發(fā)生泄漏瑰排。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一暖侨、第九天 我趴在偏房一處隱蔽的房頂上張望椭住。 院中可真熱鬧,春花似錦字逗、人聲如沸京郑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽些举。三九已至,卻和暖如春俭厚,著一層夾襖步出監(jiān)牢的瞬間户魏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工挪挤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叼丑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓扛门,卻偏偏與公主長得像鸠信,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子尖飞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容