2013年4月13日 星期六

OpenGL基本瞭解(七) (FBO and VBO)

FBO




默認情況下,我們渲染的目標幀緩衝區是由窗口系統提供的,顯示在屏幕上。要實現離屏渲染,我們可以通過程序創建一個新的幀緩衝區替代默認的幀緩衝區,把我們的渲染結果輸入到新建的緩衝區中,而不在窗口中顯示。這就是離屏渲染的基本原理。
要實現離屏渲染,首先先熟悉下下面的概念:

FBO(Frame Buffer Object)

幀緩衝對象:Opengl擴展提供了創建額外幀緩衝對象的接口,我們可以利用opengl創建的幀緩衝對象來替代窗口系統提供的幀緩衝區。這樣就允許我們把該新建的緩衝區作為我們渲染的目標。
窗口系統提供的幀緩衝區和我們創建的幀緩衝區的不同之處在于,當窗口創建時,窗口系統管理的幀緩衝區會自動分配其顏色、深度、模版等緩衝區。而opengl創建的幀緩衝區對象,需要手動創建與該幀緩衝區對象相關的其他渲染緩衝區。






fbo


從上圖我們可以看到,FBO并無實際存儲數據的地方,需要創建相應的存儲數據的緩衝區與之綁定(掛載)。
因此,當我們創建了一個FBO,需要提供相應的緩衝區作為數據的存儲區。這些緩衝區稱為幀緩衝區附加(framebuffer attachment)。幀緩衝區附加分為兩類:紋理緩衝區和渲染緩衝區。如果將渲染緩衝區關聯到幀緩衝對象,即是離屏渲染。
簡單的介紹完原理后,我們來看看如何實現,代碼其實很簡單。

第一步:創建FBO

glGenFramebuffers(GLsize n,GLuint* ids);
分配n個未使用的幀緩衝對象的名字,并在ids中返回這些名字。

第二步:綁定FBO

glBindFramebuffer(GLenum target,GLuint framebuffer);
指定一個幀緩衝區對象用於讀取或寫入,當target是GL_DRAW_FRAMEBUFFER時,framebuffer作為渲染的目標幀緩衝區。當target是GL_READ_FRAMEBUFFER時,framebuffer作為讀取數據的幀緩衝區。

第三步:創建渲染緩衝區

glGenRenderbuffers 的原型为:
void glGenRenderbuffers (GLsizei n, GLuint* renderbuffers)
它是为 renderbuffer 申請一個 id(或曰名字)。参數 n 表示申請生成 renderbuffer 的個數,而 renderbuffers 返回分配给 renderbuffer 的 id,注意:返回的 id 不會为0,id 0 是OpenGL ES 保留的,我們也不能使用 id 为0的 renderbuf

RenderBuffer(OpenGL ES 總共有三大不同用途的color buffer,depth buffer 和 stencil buffer,這裏是最基本的 color buffer)。
 

第四步:綁定渲染緩衝區

glBindRenderbuffer 的原型为:
void glBindRenderbuffer (GLenum target, GLuint renderbuffer) 
這個函數將指定 id 的 renderbuffer 設置为當前 renderbuffer。参數 target 必須为 GL_RENDERBUFFER,参數 renderbuffer 是就是使用 glGenRenderbuffers 生成的 id。


第五步:創建渲染緩衝區存儲

glRenderbufferStorage(GLenum target,GLenum internalformat,GLsize width,GLsize height);
為綁定的渲染緩衝區分配存儲空間用來存儲圖像數據。target必須是GL_RENDERBUFFER。internalformat是我們指定的圖像格式。例如:GL_RGB,GL_RED,GL_R8等等

當指定 id 的 renderbuffer 第一次被設置为當前 renderbuffer 時,會初始化該 renderbuffer 對象,其初始值为:
width 和 height:像素單位的寬和高,默認值为0;
internal format:內部格式,三大 buffer 格式之一 -- color,depth or stencil;
Color bit-depth:僅當內部格式为 color 時,設置顏色的 bit-depth,默認值为0;
Depth bit-depth:僅當內部格式为 depth時,默認值为0;
Stencil bit-depth: 僅當內部格式为 stencil,默認值为0;
在內部使用 drawable(在這裏是 EAGLLayer)的相關信息作为参數調用了 glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); 後者 glRenderbufferStorage 指定存儲在 renderbuffer 中圖像的寬高以及顏色格式,並按照此規格为之分配存儲空間。在這裏,將使用我們在前面設置 eaglLayer 的顏色格式 RGBA8, 以及 eaglLayer 的寬高作为参數調用 glRenderbufferStorage。


第六步:幀緩衝區的附加

把renderbuffer和當前綁定的幀緩衝區對象的附加關聯起來。
setupFrameBuffer 大體與前面的 setupRenderBuffer 相同,由 glGenFramebuffers分配的 id也不可能是 0,id 为 0 的 framebuffer 是OpenGL ES 保留的,它指向窗口系統提供的 framebuffer,我們同样不能使用 id 为 0 的framebuffer,否則系統會出錯。glFramebufferRenderbuffer的函數原型为:
void glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
該函數是將相關 buffer(三大buffer之一)attach到framebuffer上(如果 renderbuffer不为 0,知道前面为什麼說glGenRenderbuffers 返回的id 不會为 0 吧)或從 framebuffer上detach(如果 renderbuffer为 0)。参數 attachment 是指定 renderbuffer 被裝配到那個裝配點上,其值是GL_COLOR_ATTACHMENT0, GL_DEPTH_ATTACHMENT, GL_STENCIL_ATTACHMENT中的一個,分別對應 color,depth和 stencil三大buffer。
target必須是GL_DRAW_FRAMEBUFFER或 GL_READ_FRAMEBUFFER。attachment必須是如下之一:GL_COLOR_ATTACHMENTi、 GL_DEPTH_ATTACHMENT、GL_STENCIL_ATTACHMENT或GL_DEPTH_STENCIL_ATTACHMENT。 renderbuffertarget必須是GL_RENDERBUFFER。




下面為項目中使用的部份源碼:
 


- (void)setupRenderBuffer {
    glGenRenderbuffers(1, &_colorRenderBuffer);
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
    // 为 color renderbuffer 分配存儲空間
    [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];  


}

- (void)setupFrameBuffer {    
    glGenFramebuffers(1, &_frameBuffer);
    // 設置为當前 framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    // 將 _colorRenderBuffer 裝配到 GL_COLOR_ATTACHMENT0 這個裝配點上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, 
                              GL_RENDERBUFFER, _colorRenderBuffer);
}
 



void initFrameBuffer()
{
glGenRenderbuffers(1,&renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER,renderbuffer); glRenderbufferStorage(GL_RENDERBUFFER,GL_RGB,windowWidth,
windowHeight);

glGenFramebuffersEXT(1,&framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER,framebuffer);   /// 順序可以不一樣
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,GL_REN DERBUFFER,renderbuffer);
}


參考網頁
http://www.geekermx.com/opengl-offscreen-rendering/
http://rritw.com/a/bianchengyuyan/JS-HTML-WEB/20121125/260149.html

VBO


VBO在此先參考 wiki的說明

http://en.wikipedia.org/wiki/Vertex_Buffer_Object

A Vertex Buffer Object (VBO) is an OpenGL feature that provides methods for uploading data (vertex, normal vector, color, etc.) to the video device for non-immediate-mode rendering. VBOs offer substantial performance gains over immediate mode rendering primarily because the data resides in the video device memory rather than the system memory and so it can be rendered directly by the video device.


什么是VBOs呢?Vertex Buffer Object使用高速的显卡内存,而不是普通的系统RAM内存。它不仅仅降低了每帧的内存操作,而且减少了数据在显卡和CPU之间的传输

說明參考網頁
http://www.cnblogs.com/kesalin/archive/2012/12/20/vbo.html

VBO (Vertex Buffer object),即顶点缓存,它直接在 GPU 中开辟一个缓存区域来存储顶点数据,因为它是用来缓存储顶点数据,因此被称之为顶点缓存。我们只会在初始化缓冲区,以及在顶点数据有变化时才需要对该缓冲 区进行写操作。使用顶点缓存能够大大较少了CPU-GPU 之间的数据拷贝开销,因此显著地提升了程序运行的效率。

1. OpenGL ES 中通过如下函数来实现 VBO:
顶点缓存对象 API
glGenBuffers 创建顶点缓存对象
glBindBuffer 将顶点缓存对象设置为当前数组缓存对象(array buffer object)或当前元素缓存对象(element buffer object)
glBufferData 为顶点缓存对象申请内存空间,并进行初始化(视传入的参数而定)
glBufferSubData 初始化或更新顶点缓存对象
glDeleteBuffers 删除顶点缓存对象




2,创建顶点缓存对象
void glGenBuffers (GLsizei n, GLuint* buffers);
参数 n : 表示需要创建顶点缓存对象的个数;
参数 buffers :用于存储创建好的顶点缓存对象句柄;
同第一篇文章《[OpenGL ES 01]OpenGL ES之初体验》中的讲的 render buffer 对象句柄一样,在这里,顶点缓存对象句柄始终是大于 0 的正整数,0 是 OpenGL ES 保留。该函数能够一次产生多个顶点缓存对象。
3,将顶点缓存对象设置为(或曰绑定到)当前数组缓存对象或元素缓存对象
void glBindBuffer (GLenum target, GLuint buffer);
参数 target :指定绑定的目标,取值为 GL_ARRAY_BUFFER(用于顶点数据) 或 GL_ELEMENT_ARRAY_BUFFER(用于索引数据);
参数 buffer :顶点缓存对象句柄;
4,为顶点缓存对象分配空间
void glBufferData (GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);
参数 target:与 glBindBuffer 中的参数 target 相同;
参数 size :指定顶点缓存区的大小,以字节为单位计数;
data :用于初始化顶点缓存区的数据,可以为 NULL,表示只分配空间,之后再由 glBufferSubData 进行初始化;
usage :表示该缓存区域将会被如何使用,它的主要目的是用于提示OpenGL该对该缓存区域做何种程度的优化。其参数为以下三个之一:
GL_STATIC_DRAW:表示该缓存区不会被修改;
GL_DyNAMIC_DRAW:表示该缓存区会被周期性更改;
GL_STREAM_DRAW:表示该缓存区会被频繁更改;
如果顶点数据一经初始化就不会被修改,那么就应该尽量使用 GL_STATIC_DRAW,这样能获得更好的性能。
5,更新顶点缓冲区数据
void glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data);
参数 :offset 表示需要更新的数据的起始偏移量;
参数 :size 表示需要更新的数据的个数,也是以字节为计数单位;
data :用于更新的数据;
6,释放顶点缓存
void glDeleteBuffers (GLsizei n, const GLuint* buffers);
参数与 glGenBuffers 类似,就不再累述,该函数用于删除顶点缓存对象,释放顶点缓存。


另一個英文的說明
http://www.songho.ca/opengl/gl_vbo.html


2013年4月12日 星期五

OpenGL基本實作(四)

沿用前一個專案的架構,然後參考 http://blog.csdn.net/kesalin/article/details/8281313 的設計。

在此例中加入額外的X方向轉動。


其中背面剔除這個做法,幾乎都使用默認值,比較難以理解。


1. StoryBoard修改成一樣的架構,原有的放到旁邊去,這樣一個專案可以給兩個測試並用。

所有Slider的範圍都一樣 -180~180  default = 0


2. 加入一個新的OpenGLView2,作為顯示結果,原有的保留。本來要改成同時執行兩個OPENGL,後來研究發現iOS並不支援,因此將原來的OpenGLView 的instance,給它mark起來。
OpenGLView2.h

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#import "ksMatrix.h"

@interface OpenGLView2 : UIView {
    CAEAGLLayer* _eaglLayer;
    EAGLContext* _context;
    GLuint _colorRenderBuffer;
    GLuint _frameBuffer;
   
    GLuint _programHandle;
    GLuint _positionSlot;
    GLuint _modelViewSlot;
    GLuint _projectionSlot;
    GLuint _colorSlot;
   
    ksMatrix4 _modelViewMatrix;
    ksMatrix4 _projectionMatrix;
   
    float _rotateShoulder;
    float _rotateElbow;
    float _xRotateShoulder;
}

@property (nonatomic, assign) float rotateShoulder;
@property (nonatomic, assign) float rotateElbow;

@property (nonatomic, assign) float xRotateShoulder;
- (void)render;
- (void)cleanup;
- (void)toggleDisplayLink;

@end


OpenGLView2.m

#import "OpenGLView2.h"
#import "GLESUtils.h"


// Declare private members inside anonymous category
@interface OpenGLView2()
{
    ksMatrix4 _shouldModelViewMatrix;
    ksMatrix4 _elbowModelViewMatrix;
   
    float _rotateColorCube;
   
    CADisplayLink * _displayLink;
}

- (void)setupLayer;
- (void)setupContext;
- (void)setupBuffers;
- (void)destoryBuffers;

- (void)setupProgram;
- (void)setupProjection;

- (void)updateShoulderTransform;
- (void)updateElbowTransform;
- (void)resetTransform;

- (void)updateRectangleTransform;
- (void)updateColorCubeTransform;
- (void)drawColorCube;

- (void)drawCube:(ksColor) color;

@end

@implementation OpenGLView2

@synthesize rotateShoulder = _rotateShoulder;
@synthesize rotateElbow = _rotateElbow;
@synthesize xRotateShoulder = _xRotateShoulder;

+ (Class)layerClass {
    // Support for OpenGL ES
    return [CAEAGLLayer class];
}

- (void)setupLayer
{
    _eaglLayer = (CAEAGLLayer*) self.layer;
   
    // Make CALayer visibale
    _eaglLayer.opaque = YES;
   
    // Set drawable properties
    _eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
}


- (void)setupContext {
    // Set OpenGL version, here is OpenGL ES 2.0
    EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
    _context = [[EAGLContext alloc] initWithAPI:api];
    if (!_context) {
        NSLog(@" >> Error: Failed to initialize OpenGLES 2.0 context");
        exit(1);
    }
   
    // Set OpenGL context
    if (![EAGLContext setCurrentContext:_context]) {
        _context = nil;
        NSLog(@" >> Error: Failed to set current OpenGL context");
        exit(1);
    }
}

- (void)setupBuffers {
    glGenRenderbuffers(1, &_colorRenderBuffer);
    // Set as current renderbuffer
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
    // Allocate color renderbuffer
    [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
   
    glGenFramebuffers(1, &_frameBuffer);
    // Set as current framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
   
    // Attach _colorRenderBuffer to _frameBuffer
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                              GL_RENDERBUFFER, _colorRenderBuffer);
}

- (void)destoryBuffers
{
    glDeleteRenderbuffers(1, &_colorRenderBuffer);
    _colorRenderBuffer = 0;
   
    glDeleteFramebuffers(1, &_frameBuffer);
    _frameBuffer = 0;
}

- (void)cleanup
{
    [self destoryBuffers];
   
    if (_programHandle != 0) {
        glDeleteProgram(_programHandle);
        _programHandle = 0;
    }
   
    if (_context && [EAGLContext currentContext] == _context)
        [EAGLContext setCurrentContext:nil];
   
    _context = nil;
}


- (void)setupProgram
{
    // Load shaders
    //
    NSString * vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"VertexShader2"
                                                                  ofType:@"glsl"];
    NSString * fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader2"
                                                                    ofType:@"glsl"];
   
    _programHandle = [GLESUtils loadProgram:vertexShaderPath
                 withFragmentShaderFilepath:fragmentShaderPath];
    if (_programHandle == 0) {
        NSLog(@" >> Error: Failed to setup program.");
        return;
    }
   
    glUseProgram(_programHandle);
   
    // Get the attribute position slot from program
    //
    _positionSlot = glGetAttribLocation(_programHandle, "vPosition");
   
    // Get the attribute color slot from program
    //
    _colorSlot = glGetAttribLocation(_programHandle, "vSourceColor");
   
    // Get the uniform model-view matrix slot from program
    //
    _modelViewSlot = glGetUniformLocation(_programHandle, "modelView");
   
    // Get the uniform projection matrix slot from program
    //
    _projectionSlot = glGetUniformLocation(_programHandle, "projection");
}

-(void)setupProjection
{
    // Generate a perspective matrix with a 60 degree FOV
    //
    float aspect = self.frame.size.width / self.frame.size.height;
    ksMatrixLoadIdentity(&_projectionMatrix);
    ksPerspective(&_projectionMatrix, 60.0, aspect, 1.0f, 20.0f);
   
    // Load projection matrix
    glUniformMatrix4fv(_projectionSlot, 1, GL_FALSE, (GLfloat*)&_projectionMatrix.m[0][0]);
   
    glEnable(GL_CULL_FACE);
}

- (void) updateShoulderTransform
{
    ksMatrixLoadIdentity(&_shouldModelViewMatrix);
   
    ksMatrixTranslate(&_shouldModelViewMatrix, -0.0, 0.0, -5.5);
   
    // Rotate the shoulder
    //
    ksMatrixRotate(&_shouldModelViewMatrix, self.rotateShoulder, 0.0, 0.0, 1.0);
    
    ksMatrixRotate(&_shouldModelViewMatrix, self.xRotateShoulder, 1.0, 0.0, 0.0);
   

    // Scale the cube to be a shoulder
    //
    ksMatrixCopy(&_modelViewMatrix, &_shouldModelViewMatrix);
    ksMatrixScale(&_modelViewMatrix, 1.5, 0.6, 0.6);
   
    // Load the model-view matrix
    glUniformMatrix4fv(_modelViewSlot, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);
}

- (void) updateElbowTransform
{
    // Relative to shoulder
    //
    ksMatrixCopy(&_elbowModelViewMatrix, &_shouldModelViewMatrix);
   
    // Translate away from shoulder
    //
    ksMatrixTranslate(&_elbowModelViewMatrix, 1.5, 0.0, 0.0);
   
    // Rotate the elbow
    //
    ksMatrixRotate(&_elbowModelViewMatrix, self.rotateElbow, 0.0, 0.0, 1.0);
   
    // Scale the cube to be a elbow
    ksMatrixCopy(&_modelViewMatrix, &_elbowModelViewMatrix);
    ksMatrixScale(&_modelViewMatrix, 1.0, 0.4, 0.4);
   
    // Load the model-view matrix
    glUniformMatrix4fv(_modelViewSlot, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);
}

- (void)drawCube:(ksColor) color
{
    GLfloat vertices[] = {
        0.0f, -0.5f, 0.5f,
        0.0f, 0.5f, 0.5f,
        1.0f, 0.5f, 0.5f,
        1.0f, -0.5f, 0.5f,
       
        1.0f, -0.5f, -0.5f,
        1.0f, 0.5f, -0.5f,
        0.0f, 0.5f, -0.5f,
        0.0f, -0.5f, -0.5f,
    };
   
    GLubyte indices[] = {
        0, 1, 1, 2, 2, 3, 3, 0,
        4, 5, 5, 6, 6, 7, 7, 4,
        0, 7, 1, 6, 2, 5, 3, 4
    };
   
    glVertexAttrib4f(_colorSlot, color.r, color.g, color.b, color.a);
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices );
    glEnableVertexAttribArray(_positionSlot);
   
    glDrawElements(GL_LINES, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices);
}

- (void) updateRectangleTransform
{
    ksMatrixLoadIdentity(&_modelViewMatrix);
   
    ksMatrixTranslate(&_modelViewMatrix, 0.0, -2, -5.5);
   
    // Load the model-view matrix
    glUniformMatrix4fv(_modelViewSlot, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);
}

- (void)drawColorRectangle
{
    GLfloat vertices[] = {
        -0.5f, -0.5f, 0.0f, 1.0, 0.0, 0.0, 1.0,
        -0.5f, 0.5f, 0.0f, 0.0, 0.0, 1.0, 1.0,
        0.5f, 0.5f, 0.0f, 1.0, 1.0, 0.0, 1.0,
        0.5f, -0.5f, 0.0f, 1.0, 1.0, 1.0, 1.0
    };
   
    GLubyte indices[] = {
        0, 3, 2,
        0, 2, 1
    };
   
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), vertices);
    glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), vertices + 3);
    glEnableVertexAttribArray(_positionSlot);
    glEnableVertexAttribArray(_colorSlot);
    glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices);
    glDisableVertexAttribArray(_colorSlot);
}

- (void) updateColorCubeTransform
{
    ksMatrixLoadIdentity(&_modelViewMatrix);
   
    ksMatrixTranslate(&_modelViewMatrix, 0.0, -2, -5.5);
   
    ksMatrixRotate(&_modelViewMatrix, _rotateColorCube, 0.0, 1.0, 0.0);
   
    // Load the model-view matrix
    glUniformMatrix4fv(_modelViewSlot, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);
}

- (void) drawColorCube
{
    GLfloat vertices[] = {
        -0.5f, -0.5f, 0.5f, 1.0, 0.0, 0.0, 1.0,     // red
        -0.5f, 0.5f, 0.5f, 1.0, 1.0, 0.0, 1.0,      // yellow
        0.5f, 0.5f, 0.5f, 0.0, 0.0, 1.0, 1.0,       // blue
        0.5f, -0.5f, 0.5f, 1.0, 1.0, 1.0, 1.0,      // white
       
        0.5f, -0.5f, -0.5f, 1.0, 1.0, 0.0, 1.0,     // yellow
        0.5f, 0.5f, -0.5f, 1.0, 0.0, 0.0, 1.0,      // red
        -0.5f, 0.5f, -0.5f, 1.0, 1.0, 1.0, 1.0,     // white
        -0.5f, -0.5f, -0.5f, 0.0, 0.0, 1.0, 1.0,    // blue
    };
   
    GLubyte indices[] = {
        // Front face
        0, 3, 2, 0, 2, 1,
       
        // Back face
        7, 5, 4, 7, 6, 5,
       
        // Left face
        0, 1, 6, 0, 6, 7,
       
        // Right face
        3, 4, 5, 3, 5, 2,
       
        // Up face
        1, 2, 5, 1, 5, 6,
       
        // Down face
        0, 7, 4, 0, 4, 3
    };
   
    // 說明:
    // a. Position 資料是位於 struct 的起始處, 因此偏移量為: 0.
    // b. Color 資料是位於 Position 資料之後, 因此偏移量為: sizeof(float) * 3.
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), vertices);
    glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, 7 * sizeof(float), vertices + 3);
   
   
    glEnableVertexAttribArray(_positionSlot);
    glEnableVertexAttribArray(_colorSlot);
    glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices);
    glDisableVertexAttribArray(_colorSlot);
}

- (void)render
{
    if (_context == nil)
        return;
   
    ksColor colorRed = {1, 0, 0, 1};
    ksColor colorWhite = {1, 1, 1, 1};
   
    glClearColor(0.0, 1.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
   
    // Setup viewport
    //
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
   
    // Draw color rectangle
    //
    //    [self updateRectangleTransform];
    //    [self drawColorRectangle];
   
    // Draw color cube
    //
    [self updateColorCubeTransform];
    [self drawColorCube];
   
   
    // Draw shoulder
    //
    [self updateShoulderTransform];
    [self drawCube:colorRed];
   
    // Draw elbow
    //
    [self updateElbowTransform];
    [self drawCube:colorWhite];
   
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self setupLayer];
        [self setupContext];
        [self setupProgram];
        [self setupProjection];
       
        [self resetTransform];
    }
   
    return self;
}

- (void)layoutSubviews
{
    [EAGLContext setCurrentContext:_context];
    glUseProgram(_programHandle);
   
    [self destoryBuffers];
   
    [self setupBuffers];
   
    [self render];
}

#pragma mark - Transform properties

- (void)resetTransform
{
    self.rotateShoulder = 0.0;
    self.rotateElbow = 0.0;
   
    [self updateShoulderTransform];
    [self updateElbowTransform];
}

- (void)setRotateShoulder:(float)rotateShoulder
{
    _rotateShoulder = rotateShoulder;
   
    [self render];
}

- (float)rotateShoulder
{
    return _rotateShoulder;
}

- (void)setRotateElbow:(float)rotateElbow
{
    _rotateElbow = rotateElbow;
   
    [self render];
}

- (float)rotateElbow
{
    return _rotateElbow;
}

- (void)setXRotateShoulder:(float)xRotateShoulder
{
    _xRotateShoulder = xRotateShoulder;
   
    [self render];
}

- (float)xRotateShoulder
{
    return _xRotateShoulder;
}


- (void)toggleDisplayLink
{
    if (_displayLink == nil) {
        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkCallback:)];
        [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    else {
        [_displayLink invalidate];
        [_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        _displayLink = nil;
    }
}

- (void)displayLinkCallback:(CADisplayLink*)displayLink
{
    _rotateColorCube += displayLink.duration * 90;
   
    [self render];
}

#pragma mark

@end



3. 參考上圖,另外加入VertexShader2.glsl及FragmentShader2.glsl。原有的保留不用。

FragmentShader2.glsl

precision mediump float;

varying vec4 vDestinationColor;

void main()
{
    gl_FragColor = vDestinationColor;  // 設定顏色之處
}


VertexShader2.glsl

uniform mat4 projection;
uniform mat4 modelView;
attribute vec4 vPosition;

attribute vec4 vSourceColor;
varying vec4 vDestinationColor;

void main(void)
{
    gl_Position = projection * modelView * vPosition;
    vDestinationColor = vSourceColor;
}


4. 修改mainViewController.h
#import <UIKit/UIKit.h>
#import "OpenGLView.h"
#import "OpenGLView2.h"

@interface mainViewController : UIViewController

@property (strong, nonatomic) IBOutlet UIView *controlView;

//@property (strong, nonatomic) IBOutlet OpenGLView *openGLView;
@property (strong, nonatomic) IBOutlet OpenGLView2 *openGLView2;

@property (strong, nonatomic) IBOutlet UISlider *posXSlider;

@property (strong, nonatomic) IBOutlet UISlider *posYSlider;

@property (strong, nonatomic) IBOutlet UISlider *posZSlider;

@property (strong, nonatomic) IBOutlet UISlider *scaleZSlider;

@property (strong, nonatomic) IBOutlet UISlider *rotateXSlider;

@property (strong, nonatomic) IBOutlet UISlider *rotateYSlider;

@end



5. 修改 mainViewController.m

//@synthesize openGLView;
@synthesize openGLView2;


- (IBAction)shouderRotate:(id)sender {
    UISlider * slider = (UISlider *)sender;
    float currentValue = [slider value];
   
    NSLog(@" >> current shoulder is %f", currentValue);
   
    self.openGLView2.rotateShoulder = currentValue;
   
}
- (IBAction)elbowRotate:(id)sender {

    UISlider * slider = (UISlider *)sender;
    float currentValue = [slider value];
   
    NSLog(@" >> current elbow is %f", currentValue);
   
    self.openGLView2.rotateElbow = currentValue;

}




- (IBAction)shouderXRotate:(id)sender {
   
    UISlider * slider = (UISlider *)sender;
    float currentValue = [slider value];
   
    NSLog(@" >> current shoulder is %f", currentValue);
   
    self.openGLView2.xRotateShoulder = currentValue;
}


- (IBAction)cubeRotate:(id)sender {
   
    [self.openGLView2 toggleDisplayLink];
   
    UIButton * button = (UIButton *)sender;
    NSString * text = button.titleLabel.text;
    if ([text isEqualToString:@"Rotate"]) {
        [button setTitle: @"Stop" forState: UIControlStateNormal];
    }
    else {
        [button setTitle: @"Rotate" forState: UIControlStateNormal];
    }

}


6. 結果


2013年4月11日 星期四

OpenGL基本實作(三)

一個3D方向的變化例

參考資料
http://blog.csdn.net/kesalin/article/details/7168967
http://blog.csdn.net/kesalin/article/details/8271112

將原本的例子重新整理到新版的Xcode,並將角錐改成長方形以及加上Y方向的旋轉選項。

1. 首先開啓一個新的專案

2. 加入所需的Sliders/Buttons以及UIView到Storyboard,UIView作為OPENGL的演示區。
POSX/POSY的範圍 3 ~ -3  default 0.5
POSZ 的範圍 -10 ~ -1  default -5.5
ScaleZ  範圍  2 ~ 0.5  default 1
RotateX/RotateY 範圍  -180 ~180   default  0


3.加入所需的Framework,新增OpenGLES及QuartzCore這兩個



4. 首先加入utils的class,此處做為載入Shader之用,繼承自NSObject
GLESUtils.h

#import <Foundation/Foundation.h>
#include <OpenGLES/ES2/gl.h>

@interface GLESUtils : NSObject

// Create a shader object, load the shader source string, and compile the shader.
//
+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString;

+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath;

//
///
/// Load a vertex and fragment shader, create a program object, link program.
/// Errors output to log.
/// vertexShaderFilepath Vertex shader source file path.
/// fragmentShaderFilepath Fragment shader source file path
/// return A new program object linked with the vertex/fragment shader pair, 0 on failure
//
+(GLuint)loadProgram:(NSString *)vertexShaderFilepath withFragmentShaderFilepath:(NSString *)fragmentShaderFilepath;

@end


GLESUtils.m

#import "GLESUtils.h"

@implementation GLESUtils

+(GLuint)loadShader:(GLenum)type withFilepath:(NSString *)shaderFilepath
{
    NSError* error;
    NSString* shaderString = [NSString stringWithContentsOfFile:shaderFilepath
                                                       encoding:NSUTF8StringEncoding
                                                          error:&error];
    if (!shaderString) {
        NSLog(@"Error: loading shader file: %@ %@", shaderFilepath, error.localizedDescription);
        return 0;
    }
   
    return [self loadShader:type withString:shaderString];
}

+(GLuint)loadShader:(GLenum)type withString:(NSString *)shaderString
{
    // Create the shader object
    GLuint shader = glCreateShader(type);
    if (shader == 0) {
        NSLog(@"Error: failed to create shader.");
        return 0;
    }
   
    // Load the shader source
    const char * shaderStringUTF8 = [shaderString UTF8String];
    glShaderSource(shader, 1, &shaderStringUTF8, NULL);
   
    // Compile the shader
    glCompileShader(shader);
   
    // Check the compile status
    GLint compiled = 0;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
   
    if (!compiled) {
        GLint infoLen = 0;
        glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );
       
        if (infoLen > 1) {
            char * infoLog = malloc(sizeof(char) * infoLen);
            glGetShaderInfoLog (shader, infoLen, NULL, infoLog);
            NSLog(@"Error compiling shader:\n%s\n", infoLog );
           
            free(infoLog);
        }
       
        glDeleteShader(shader);
        return 0;
    }
   
    return shader;
}

+(GLuint)loadProgram:(NSString *)vertexShaderFilepath withFragmentShaderFilepath:(NSString *)fragmentShaderFilepath
{
    // Load the vertex/fragment shaders
    GLuint vertexShader = [self loadShader:GL_VERTEX_SHADER
                              withFilepath:vertexShaderFilepath];
    if (vertexShader == 0)
        return 0;
   
    GLuint fragmentShader = [self loadShader:GL_FRAGMENT_SHADER
                                withFilepath:fragmentShaderFilepath];
    if (fragmentShader == 0) {
        glDeleteShader(vertexShader);
        return 0;
    }
   
    // Create the program object
    GLuint programHandle = glCreateProgram();
    if (programHandle == 0)
        return 0;
   
    glAttachShader(programHandle, vertexShader);
    glAttachShader(programHandle, fragmentShader);
   
    // Link the program
    glLinkProgram(programHandle);
   
    // Check the link status
    GLint linked;
    glGetProgramiv(programHandle, GL_LINK_STATUS, &linked);
   
    if (!linked) {
        GLint infoLen = 0;
        glGetProgramiv(programHandle, GL_INFO_LOG_LENGTH, &infoLen);
       
        if (infoLen > 1){
            char * infoLog = malloc(sizeof(char) * infoLen);
            glGetProgramInfoLog(programHandle, infoLen, NULL, infoLog);
           
            NSLog(@"Error linking program:\n%s\n", infoLog);
           
            free(infoLog);
        }
       
        glDeleteProgram(programHandle );
        return 0;
    }
   
    // Free up no longer needed shader resources
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
   
    return programHandle;
}

@end


5. 加入矩陣運算的C Code
ksMatrix.c

#include "ksMatrix.h"
#include <stdlib.h>
#include <math.h>

void * memcpy(void *, const void *, size_t);
void * memset(void *, int, size_t);

unsigned int ksNextPot(unsigned int n)
{
    n--;
    n |= n >> 1; n |= n >> 2;
    n |= n >> 4; n |= n >> 8;
    n |= n >> 16;
    n++;
    return n;
}

//
// Matrix math utility
//

void ksMatrixScale(ksMatrix4 * result, float sx, float sy, float sz)
{
    result->m[0][0] *= sx;
    result->m[0][1] *= sx;
    result->m[0][2] *= sx;
    result->m[0][3] *= sx;
   
    result->m[1][0] *= sy;
    result->m[1][1] *= sy;
    result->m[1][2] *= sy;
    result->m[1][3] *= sy;
   
    result->m[2][0] *= sz;
    result->m[2][1] *= sz;
    result->m[2][2] *= sz;
    result->m[2][3] *= sz;
}

void ksMatrixTranslate(ksMatrix4 * result, float tx, float ty, float tz)
{
    result->m[3][0] += (result->m[0][0] * tx + result->m[1][0] * ty + result->m[2][0] * tz);
    result->m[3][1] += (result->m[0][1] * tx + result->m[1][1] * ty + result->m[2][1] * tz);
    result->m[3][2] += (result->m[0][2] * tx + result->m[1][2] * ty + result->m[2][2] * tz);
    result->m[3][3] += (result->m[0][3] * tx + result->m[1][3] * ty + result->m[2][3] * tz);
}

void ksMatrixRotate(ksMatrix4 * result, float angle, float x, float y, float z)
{
    float sinAngle, cosAngle;
    float mag = sqrtf(x * x + y * y + z * z);
   
    sinAngle = sinf ( angle * M_PI / 180.0f );
    cosAngle = cosf ( angle * M_PI / 180.0f );
    if ( mag > 0.0f )
    {
        float xx, yy, zz, xy, yz, zx, xs, ys, zs;
        float oneMinusCos;
        ksMatrix4 rotMat;
       
        x /= mag;
        y /= mag;
        z /= mag;
       
        xx = x * x;
        yy = y * y;
        zz = z * z;
        xy = x * y;
        yz = y * z;
        zx = z * x;
        xs = x * sinAngle;
        ys = y * sinAngle;
        zs = z * sinAngle;
        oneMinusCos = 1.0f - cosAngle;
       
        rotMat.m[0][0] = (oneMinusCos * xx) + cosAngle;
        rotMat.m[0][1] = (oneMinusCos * xy) - zs;
        rotMat.m[0][2] = (oneMinusCos * zx) + ys;
        rotMat.m[0][3] = 0.0F;
       
        rotMat.m[1][0] = (oneMinusCos * xy) + zs;
        rotMat.m[1][1] = (oneMinusCos * yy) + cosAngle;
        rotMat.m[1][2] = (oneMinusCos * yz) - xs;
        rotMat.m[1][3] = 0.0F;
       
        rotMat.m[2][0] = (oneMinusCos * zx) - ys;
        rotMat.m[2][1] = (oneMinusCos * yz) + xs;
        rotMat.m[2][2] = (oneMinusCos * zz) + cosAngle;
        rotMat.m[2][3] = 0.0F;
       
        rotMat.m[3][0] = 0.0F;
        rotMat.m[3][1] = 0.0F;
        rotMat.m[3][2] = 0.0F;
        rotMat.m[3][3] = 1.0F;
       
        ksMatrixMultiply( result, &rotMat, result );
    }
}

// result[x][y] = a[x][0]*b[0][y]+a[x][1]*b[1][y]+a[x][2]*b[2][y]+a[x][3]*b[3][y];
void ksMatrixMultiply(ksMatrix4 * result, const ksMatrix4 *a, const ksMatrix4 *b)
{
    ksMatrix4 tmp;
    int i;
   
    for (i = 0; i < 4; i++)
    {
        tmp.m[i][0] = (a->m[i][0] * b->m[0][0]) +
        (a->m[i][1] * b->m[1][0]) +
        (a->m[i][2] * b->m[2][0]) +
        (a->m[i][3] * b->m[3][0]) ;
       
        tmp.m[i][1] = (a->m[i][0] * b->m[0][1]) +
        (a->m[i][1] * b->m[1][1]) +
        (a->m[i][2] * b->m[2][1]) +
        (a->m[i][3] * b->m[3][1]) ;
       
        tmp.m[i][2] = (a->m[i][0] * b->m[0][2]) +
        (a->m[i][1] * b->m[1][2]) +
        (a->m[i][2] * b->m[2][2]) +
        (a->m[i][3] * b->m[3][2]) ;
       
        tmp.m[i][3] = (a->m[i][0] * b->m[0][3]) +
        (a->m[i][1] * b->m[1][3]) +
        (a->m[i][2] * b->m[2][3]) +
        (a->m[i][3] * b->m[3][3]) ;
    }
   
    memcpy(result, &tmp, sizeof(ksMatrix4));
}

void ksMatrixDotVector(ksVec4 * out, const ksMatrix4 * m, const ksVec4 * v)
{
    out->x = m->m[0][0] * v->x + m->m[0][1] * v->y + m->m[0][2] * v->z + m->m[0][3] * v->w;
    out->y = m->m[1][0] * v->x + m->m[1][1] * v->y + m->m[1][2] * v->z + m->m[1][3] * v->w;
    out->z = m->m[2][0] * v->x + m->m[2][1] * v->y + m->m[2][2] * v->z + m->m[2][3] * v->w;
    out->w = m->m[3][0] * v->x + m->m[3][1] * v->y + m->m[3][2] * v->z + m->m[3][3] * v->w;
}

void ksMatrixCopy(ksMatrix4 * target, const ksMatrix4 * src)
{
    memcpy(target, src, sizeof(ksMatrix4));
}

int ksMatrixInvert(ksMatrix4 * out, const ksMatrix4 * in)
{
    float * m = (float *)(&in->m[0][0]);
    float * om = (float *)(&out->m[0][0]);
    double inv[16], det;
    int i;
   
    inv[0] = m[5]  * m[10] * m[15] -
    m[5]  * m[11] * m[14] -
    m[9]  * m[6]  * m[15] +
    m[9]  * m[7]  * m[14] +
    m[13] * m[6]  * m[11] -
    m[13] * m[7]  * m[10];
   
    inv[4] = -m[4]  * m[10] * m[15] +
    m[4]  * m[11] * m[14] +
    m[8]  * m[6]  * m[15] -
    m[8]  * m[7]  * m[14] -
    m[12] * m[6]  * m[11] +
    m[12] * m[7]  * m[10];
   
    inv[8] = m[4]  * m[9] * m[15] -
    m[4]  * m[11] * m[13] -
    m[8]  * m[5] * m[15] +
    m[8]  * m[7] * m[13] +
    m[12] * m[5] * m[11] -
    m[12] * m[7] * m[9];
   
    inv[12] = -m[4]  * m[9] * m[14] +
    m[4]  * m[10] * m[13] +
    m[8]  * m[5] * m[14] -
    m[8]  * m[6] * m[13] -
    m[12] * m[5] * m[10] +
    m[12] * m[6] * m[9];
   
    inv[1] = -m[1]  * m[10] * m[15] +
    m[1]  * m[11] * m[14] +
    m[9]  * m[2] * m[15] -
    m[9]  * m[3] * m[14] -
    m[13] * m[2] * m[11] +
    m[13] * m[3] * m[10];
   
    inv[5] = m[0]  * m[10] * m[15] -
    m[0]  * m[11] * m[14] -
    m[8]  * m[2] * m[15] +
    m[8]  * m[3] * m[14] +
    m[12] * m[2] * m[11] -
    m[12] * m[3] * m[10];
   
    inv[9] = -m[0]  * m[9] * m[15] +
    m[0]  * m[11] * m[13] +
    m[8]  * m[1] * m[15] -
    m[8]  * m[3] * m[13] -
    m[12] * m[1] * m[11] +
    m[12] * m[3] * m[9];
   
    inv[13] = m[0]  * m[9] * m[14] -
    m[0]  * m[10] * m[13] -
    m[8]  * m[1] * m[14] +
    m[8]  * m[2] * m[13] +
    m[12] * m[1] * m[10] -
    m[12] * m[2] * m[9];
   
    inv[2] = m[1]  * m[6] * m[15] -
    m[1]  * m[7] * m[14] -
    m[5]  * m[2] * m[15] +
    m[5]  * m[3] * m[14] +
    m[13] * m[2] * m[7] -
    m[13] * m[3] * m[6];
   
    inv[6] = -m[0]  * m[6] * m[15] +
    m[0]  * m[7] * m[14] +
    m[4]  * m[2] * m[15] -
    m[4]  * m[3] * m[14] -
    m[12] * m[2] * m[7] +
    m[12] * m[3] * m[6];
   
    inv[10] = m[0]  * m[5] * m[15] -
    m[0]  * m[7] * m[13] -
    m[4]  * m[1] * m[15] +
    m[4]  * m[3] * m[13] +
    m[12] * m[1] * m[7] -
    m[12] * m[3] * m[5];
   
    inv[14] = -m[0]  * m[5] * m[14] +
    m[0]  * m[6] * m[13] +
    m[4]  * m[1] * m[14] -
    m[4]  * m[2] * m[13] -
    m[12] * m[1] * m[6] +
    m[12] * m[2] * m[5];
   
    inv[3] = -m[1] * m[6] * m[11] +
    m[1] * m[7] * m[10] +
    m[5] * m[2] * m[11] -
    m[5] * m[3] * m[10] -
    m[9] * m[2] * m[7] +
    m[9] * m[3] * m[6];
   
    inv[7] = m[0] * m[6] * m[11] -
    m[0] * m[7] * m[10] -
    m[4] * m[2] * m[11] +
    m[4] * m[3] * m[10] +
    m[8] * m[2] * m[7] -
    m[8] * m[3] * m[6];
   
    inv[11] = -m[0] * m[5] * m[11] +
    m[0] * m[7] * m[9] +
    m[4] * m[1] * m[11] -
    m[4] * m[3] * m[9] -
    m[8] * m[1] * m[7] +
    m[8] * m[3] * m[5];
   
    inv[15] = m[0] * m[5] * m[10] -
    m[0] * m[6] * m[9] -
    m[4] * m[1] * m[10] +
    m[4] * m[2] * m[9] +
    m[8] * m[1] * m[6] -
    m[8] * m[2] * m[5];
   
    det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
   
    if (det == 0)
        return 0;
   
    det = 1.0 / det;
    for (i = 0; i < 16; i++)
        *om++ = (float)(inv[i] * det);
   
    return 1;
}

void ksMatrixTranspose(ksMatrix4 * result, const ksMatrix4 * src)
{
    ksMatrix4 tmp;
    tmp.m[0][0] = src->m[0][0];
    tmp.m[0][1] = src->m[1][0];
    tmp.m[0][2] = src->m[2][0];
    tmp.m[0][3] = src->m[3][0];
   
    tmp.m[1][0] = src->m[0][1];
    tmp.m[1][1] = src->m[1][1];
    tmp.m[1][2] = src->m[2][1];
    tmp.m[1][3] = src->m[3][1];
   
    tmp.m[2][0] = src->m[0][2];
    tmp.m[2][1] = src->m[1][2];
    tmp.m[2][2] = src->m[2][2];
    tmp.m[2][3] = src->m[3][2];
   
    tmp.m[3][0] = src->m[0][3];
    tmp.m[3][1] = src->m[1][3];
    tmp.m[3][2] = src->m[2][3];
    tmp.m[3][3] = src->m[3][3];
   
    memcpy(result, &tmp, sizeof(ksMatrix4));
}

void ksMatrix4ToMatrix3(ksMatrix3 * result, const ksMatrix4 * src)
{
    result->m[0][0] = src->m[0][0];
    result->m[0][1] = src->m[0][1];
    result->m[0][2] = src->m[0][2];
    result->m[1][0] = src->m[1][0];
    result->m[1][1] = src->m[1][1];
    result->m[1][2] = src->m[1][2];
    result->m[2][0] = src->m[2][0];
    result->m[2][1] = src->m[2][1];
    result->m[2][2] = src->m[2][2];
}

void ksMatrixLoadIdentity(ksMatrix4 * result)
{
    memset(result, 0x0, sizeof(ksMatrix4));
   
    result->m[0][0] = 1.0f;
    result->m[1][1] = 1.0f;
    result->m[2][2] = 1.0f;
    result->m[3][3] = 1.0f;
}

void ksFrustum(ksMatrix4 * result, float left, float right, float bottom, float top, float nearZ, float farZ)
{
    float       deltaX = right - left;
    float       deltaY = top - bottom;
    float       deltaZ = farZ - nearZ;
    ksMatrix4    frust;
   
    if ( (nearZ <= 0.0f) || (farZ <= 0.0f) ||
        (deltaX <= 0.0f) || (deltaY <= 0.0f) || (deltaZ <= 0.0f) )
        return;
   
    frust.m[0][0] = 2.0f * nearZ / deltaX;
    frust.m[0][1] = frust.m[0][2] = frust.m[0][3] = 0.0f;
   
    frust.m[1][1] = 2.0f * nearZ / deltaY;
    frust.m[1][0] = frust.m[1][2] = frust.m[1][3] = 0.0f;
   
    frust.m[2][0] = (right + left) / deltaX;
    frust.m[2][1] = (top + bottom) / deltaY;
    frust.m[2][2] = -(nearZ + farZ) / deltaZ;
    frust.m[2][3] = -1.0f;
   
    frust.m[3][2] = -2.0f * nearZ * farZ / deltaZ;
    frust.m[3][0] = frust.m[3][1] = frust.m[3][3] = 0.0f;
   
    ksMatrixMultiply(result, &frust, result);
}

void ksPerspective(ksMatrix4 * result, float fovy, float aspect, float nearZ, float farZ)
{
    float frustumW, frustumH;
   
    frustumH = tanf( fovy / 360.0f * M_PI ) * nearZ;
    frustumW = frustumH * aspect;
   
    ksFrustum(result, -frustumW, frustumW, -frustumH, frustumH, nearZ, farZ);
}

void ksOrtho(ksMatrix4 * result, float left, float right, float bottom, float top, float nearZ, float farZ)
{
    float       deltaX = right - left;
    float       deltaY = top - bottom;
    float       deltaZ = farZ - nearZ;
    ksMatrix4    ortho;
   
    if ((deltaX == 0.0f) || (deltaY == 0.0f) || (deltaZ == 0.0f))
        return;
   
    ksMatrixLoadIdentity(&ortho);
    ortho.m[0][0] = 2.0f / deltaX;
    ortho.m[3][0] = -(right + left) / deltaX;
    ortho.m[1][1] = 2.0f / deltaY;
    ortho.m[3][1] = -(top + bottom) / deltaY;
    ortho.m[2][2] = -2.0f / deltaZ;
    ortho.m[3][2] = -(nearZ + farZ) / deltaZ;
   
    ksMatrixMultiply(result, &ortho, result);
}

void ksLookAt(ksMatrix4 * result, const ksVec3 * eye, const ksVec3 * target, const ksVec3 * up)
{
    ksVec3 side, up2, forward ;
    //ksVec4 eyePrime;
    ksMatrix4 transMat;
   
    ksVectorSubtract(&forward, target, eye);
    ksVectorNormalize(&forward);
   
    ksCrossProduct(&side, up, &forward);
    ksVectorNormalize(&side );
   
    ksCrossProduct(&up2, &side, &forward);
    ksVectorNormalize(&up2);
   
    ksMatrixLoadIdentity(result);
    result->m[0][0] = side.x;
    result->m[0][1] = side.y;
    result->m[0][2] = side.z;
    result->m[1][0] = up2.x;
    result->m[1][1] = up2.y;
    result->m[1][2] = up2.z;
    result->m[2][0] = -forward.x;
    result->m[2][1] = -forward.y;
    result->m[2][2] = -forward.z;
   
    ksMatrixLoadIdentity(&transMat);
    ksMatrixTranslate(&transMat, -eye->x, -eye->y, -eye->z);
   
    ksMatrixMultiply(result, result, &transMat);
   
    //eyePrime.x = -eye->x;
    //eyePrime.y = -eye->y;
    //eyePrime.z = -eye->z;
    //eyePrime.w = 1;
   
    //ksMatrixMultiplyVector(&eyePrime, result, &eyePrime);
    //ksMatrixTranspose(result, result);
   
    //result->m[3][0] = eyePrime.x;
    //result->m[3][1] = eyePrime.y;
    //result->m[3][2] = eyePrime.z;
    //result->m[3][3] = eyePrime.w;
}


ksMatrix.h

#ifndef __KS_MATRIX_H__
#define __KS_MATRIX_H__

#include <math.h>
#include "ksVector.h"

#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795f
#endif

#define DEG2RAD( a ) (((a) * M_PI) / 180.0f)
#define RAD2DEG( a ) (((a) * 180.f) / M_PI)

// angle indexes
#define    PITCH                0        // up / down
#define    YAW                    1        // left / right
#define    ROLL                2        // fall over

typedef struct ksMatrix3
{
    float   m[3][3];
} ksMatrix3;

typedef struct ksMatrix4
{
    float   m[4][4];
} ksMatrix4;

#ifdef __cplusplus
extern "C" {
#endif
   
    unsigned int ksNextPot(unsigned int n);
   
    void ksMatrixCopy(ksMatrix4 * target, const ksMatrix4 * src);
   
    int ksMatrixInvert(ksMatrix4 * out, const ksMatrix4 * in);
   
    void ksMatrixTranspose(ksMatrix4 * result, const ksMatrix4 * src);
   
    void ksMatrix4ToMatrix3(ksMatrix3 * target, const ksMatrix4 * src);
   
    void ksMatrixDotVector(ksVec4 * out, const ksMatrix4 * m, const ksVec4 * v);
   
    //
    /// multiply matrix specified by result with a scaling matrix and return new matrix in result
    /// result Specifies the input matrix.  Scaled matrix is returned in result.
    /// sx, sy, sz Scale factors along the x, y and z axes respectively
    //
    void ksMatrixScale(ksMatrix4 * result, float sx, float sy, float sz);
   
    //
    /// multiply matrix specified by result with a translation matrix and return new matrix in result
    /// result Specifies the input matrix.  Translated matrix is returned in result.
    /// tx, ty, tz Scale factors along the x, y and z axes respectively
    //
    void ksMatrixTranslate(ksMatrix4 * result, float tx, float ty, float tz);
   
    //
    /// multiply matrix specified by result with a rotation matrix and return new matrix in result
    /// result Specifies the input matrix.  Rotated matrix is returned in result.
    /// angle Specifies the angle of rotation, in degrees.
    /// x, y, z Specify the x, y and z coordinates of a vector, respectively
    //
    void ksMatrixRotate(ksMatrix4 * result, float angle, float x, float y, float z);
   
    //
    /// perform the following operation - result matrix = srcA matrix * srcB matrix
    /// result Returns multiplied matrix
    /// srcA, srcB Input matrices to be multiplied
    //
    void ksMatrixMultiply(ksMatrix4 * result, const ksMatrix4 *srcA, const ksMatrix4 *srcB);
   
    //
    //// return an identity matrix
    //// result returns identity matrix
    //
    void ksMatrixLoadIdentity(ksMatrix4 * result);
   
    //
    /// multiply matrix specified by result with a perspective matrix and return new matrix in result
    /// result Specifies the input matrix.  new matrix is returned in result.
    /// fovy Field of view y angle in degrees
    /// aspect Aspect ratio of screen
    /// nearZ Near plane distance
    /// farZ Far plane distance
    //
    void ksPerspective(ksMatrix4 * result, float fovy, float aspect, float nearZ, float farZ);
   
    //
    /// multiply matrix specified by result with a perspective matrix and return new matrix in result
    /// result Specifies the input matrix.  new matrix is returned in result.
    /// left, right Coordinates for the left and right vertical clipping planes
    /// bottom, top Coordinates for the bottom and top horizontal clipping planes
    /// nearZ, farZ Distances to the near and far depth clipping planes.  These values are negative if plane is behind the viewer
    //
    void ksOrtho(ksMatrix4 * result, float left, float right, float bottom, float top, float nearZ, float farZ);
   
    //
    // multiply matrix specified by result with a perspective matrix and return new matrix in result
    /// result Specifies the input matrix.  new matrix is returned in result.
    /// left, right Coordinates for the left and right vertical clipping planes
    /// bottom, top Coordinates for the bottom and top horizontal clipping planes
    /// nearZ, farZ Distances to the near and far depth clipping planes.  Both distances must be positive.
    //
    void ksFrustum(ksMatrix4 * result, float left, float right, float bottom, float top, float nearZ, float farZ);
   
    void ksLookAt(ksMatrix4 * result, const ksVec3 * eye, const ksVec3 * target, const ksVec3 * up);
   
#ifdef __cplusplus
}
#endif

#endif // __KS_MATRIX_H__


6. 加入矩陣運算所需要的向量運算函數碼
ksVector.c

#include "ksVector.h"
#include <math.h>

void ksVectorCopy(ksVec3 * out, const ksVec3 * in)
{
    out->x = in->x;
    out->y = in->y;
    out->z = in->z;
}

void ksVectorAdd(ksVec3 * out, const ksVec3 * a, const ksVec3 * b)
{
    out->x = a->x + b->x;
    out->y = a->y + b->y;
    out->z = a->z + b->z;
}

void ksVectorSubtract(ksVec3 * out, const ksVec3 * a, const ksVec3 * b)
{
    out->x = a->x - b->x;
    out->y = a->y - b->y;
    out->z = a->z - b->z;
}

void ksCrossProduct(ksVec3 * out, const ksVec3 * a, const ksVec3 * b)
{
    out->x = a->y * b->z - a->z * b->y;
    out->y = a->z * b->x - a->x * b->z;
    out->z = a->x * b->y - b->y * a->x;
}

float ksDotProduct(const ksVec3 * a, const ksVec3 * b)
{
    return (a->x * b->x + a->y * b->y + a->z * b->z);
}

void ksVectorLerp(ksVec3 * out, const ksVec3 * a, const ksVec3 * b, float t)
{
    out->x = (a->x * (1 - t) + b->x * t);
    out->y = (a->y * (1 - t) + b->y * t);
    out->z = (a->z * (1 - t) + b->z * t);
}

void ksVectorScale(ksVec3 * v, float scale)
{
    v->x *= scale;
    v->y *= scale;
    v->z *= scale;
}

void ksVectorInverse(ksVec3 * v)
{
    v->x = -v->x;
    v->y = -v->y;
    v->z = -v->z;
}

void ksVectorNormalize(ksVec3 * v)
{
    float length = ksVectorLength(v);
    if (length != 0)
    {
        length = 1.0 / length;
        v->x *= length;
        v->y *= length;
        v->z *= length;
    }
}

int ksVectorCompare(const ksVec3 * a, const ksVec3 * b)
{
    if (a == b)
        return 1;
   
    if (a->x != b->x || a->y != b->y || a->z != b->z)
        return 0;
    return 1;
}

float ksVectorLength(const ksVec3 * in)
{
    return (float)sqrt(in->x * in->x + in->y * in->y + in->z * in->z);
}

float ksVectorLengthSquared(const ksVec3 * in)
{
    return (in->x * in->x + in->y * in->y + in->z * in->z);
}

float ksVectorDistance(const ksVec3 * a, const ksVec3 * b)
{
    ksVec3 v;
    ksVectorSubtract(&v, a, b);
    return ksVectorLength(&v);
}

float ksVectorDistanceSquared(const ksVec3 * a, const ksVec3 * b)
{
    ksVec3 v;
    ksVectorSubtract(&v, a, b);
    return (v.x * v.x + v.y * v.y + v.z * v.z);
}


ksVector.h

#ifndef __KS_VECTOR_H__
#define __KS_VECTOR_H__

typedef struct
{
    float x;
    float y;
    float z;
} ksVec3;

typedef struct
{
    float x;
    float y;
    float z;
    float w;
} ksVec4;

typedef struct
{
    float r;
    float g;
    float b;
    float a;
} ksColor;

typedef unsigned char byte;

#ifdef __cplusplus
extern "C" {
#endif
   
    void ksVectorCopy(ksVec3 * out, const ksVec3 * in);
    void ksVectorAdd(ksVec3 * out, const ksVec3 * a, const ksVec3 * b);
    void ksVectorSubtract(ksVec3 * out, const ksVec3 * a, const ksVec3 * b);
    void ksVectorLerp(ksVec3 * out, const ksVec3 * a, const ksVec3 * b, float t);
    void ksCrossProduct(ksVec3 * out, const ksVec3 * a, const ksVec3 * b);
    float ksDotProduct(const ksVec3 * a, const ksVec3 * b);
   
    float ksVectorLengthSquared(const ksVec3 * in);
    float ksVectorDistanceSquared(const ksVec3 * a, const ksVec3 * b);
   
    void ksVectorScale(ksVec3 * v, float scale);
    void ksVectorNormalize(ksVec3 * v);
    void ksVectorInverse(ksVec3 * v);
   
    int ksVectorCompare(const ksVec3 * a, const ksVec3 * b);
    float ksVectorLength(const ksVec3 * in);
    float ksVectorDistance(const ksVec3 * a, const ksVec3 * b);
   
#ifdef __cplusplus
}
#endif

#endif    //__KS_VECTOR_H__

7.  加入OPENGL 的程式碼,首先要新增一個UIView的Class,因為要顯示在其中。
OpenGLView.h

#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include "ksMatrix.h"

@interface OpenGLView : UIView
{
    CAEAGLLayer* _eaglLayer;
    EAGLContext* _context;
    GLuint _colorRenderBuffer;
    GLuint _frameBuffer;
   
    GLuint _programHandle;
    GLuint _positionSlot;
    GLint _modelViewSlot;
    GLint _projectionSlot;
   
    ksMatrix4 _modelViewMatrix;
    ksMatrix4 _projectionMatrix;
   
    float _posX;
    float _posY;
    float _posZ;
   
    float _rotateX;
    float _scaleZ;
   
    float _rotateY;
   
}

@property (nonatomic, assign) float posX;
@property (nonatomic, assign) float posY;
@property (nonatomic, assign) float posZ;

@property (nonatomic, assign) float scaleZ;
@property (nonatomic, assign) float rotateX;
@property (nonatomic, assign) float rotateY;

- (void)resetTransform;
- (void)render;
- (void)cleanup;
- (void)toggleDisplayLink;

@end


8. OpenGLView.m
#import "OpenGLView.h"
#import "GLESUtils.h"

@interface OpenGLView()
{
    CADisplayLink * _displayLink;
}

- (void)setupLayer;
- (void)setupContext;
- (void)setupBuffers;
- (void)destoryBuffers;

- (void)setupProgram;
- (void)setupProjection;

- (void)updateTransform;
- (void)displayLinkCallback:(CADisplayLink*)displayLink;

@end


@implementation OpenGLView

@synthesize posX = _posX;
@synthesize posY = _posY;
@synthesize posZ = _posZ;
@synthesize scaleZ = _scaleZ;
@synthesize rotateX = _rotateX;

@synthesize rotateY = _rotateY;

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self) {
        // Initialization code
    }
    return self;
}

+ (Class)layerClass {
    // 只有 [CAEAGLLayer class] 类型的 layer 才支持在其上描绘 OpenGL 内容。
    return [CAEAGLLayer class];
}

- (void)setupLayer
{
    _eaglLayer = (CAEAGLLayer*) self.layer;
   
    // CALayer 默认是透明的,必须将它设为不透明才能让其可见
    _eaglLayer.opaque = YES;
   
    // 设置描绘属性,在这里设置不维持渲染内容以及颜色格式为 RGBA8
    _eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:
                                     [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil];
}

- (void)setupContext {
    // 指定 OpenGL 渲染 API 的版本,在这里我们使用 OpenGL ES 2.0
    EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
    _context = [[EAGLContext alloc] initWithAPI:api];
    if (!_context) {
        NSLog(@" >> Error: Failed to initialize OpenGLES 2.0 context");
        exit(1);
    }
   
    // 设置为当前上下文
    if (![EAGLContext setCurrentContext:_context]) {
        _context = nil;
        NSLog(@" >> Error: Failed to set current OpenGL context");
        exit(1);
    }
}

- (void)setupBuffers {
    glGenRenderbuffers(1, &_colorRenderBuffer);
    // 设置为当前 renderbuffer
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
    // 为 color renderbuffer 分配存储空间
    [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
   
    glGenFramebuffers(1, &_frameBuffer);
    // 设置为当前 framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    // 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                              GL_RENDERBUFFER, _colorRenderBuffer);
}

- (void)destoryBuffers
{
    glDeleteRenderbuffers(1, &_colorRenderBuffer);
    _colorRenderBuffer = 0;
   
    glDeleteFramebuffers(1, &_frameBuffer);
    _frameBuffer = 0;
}

- (void)cleanup
{
    [self destoryBuffers];
   
    if (_programHandle != 0) {
        glDeleteProgram(_programHandle);
        _programHandle = 0;
    }
   
    if (_context && [EAGLContext currentContext] == _context)
        [EAGLContext setCurrentContext:nil];
   
    _context = nil;
}

- (void)setupProgram
{
    // Load shaders
    //
    NSString * vertexShaderPath = [[NSBundle mainBundle] pathForResource:@"VertexShader"
                                                                  ofType:@"glsl"];
    NSString * fragmentShaderPath = [[NSBundle mainBundle] pathForResource:@"FragmentShader"
                                                                    ofType:@"glsl"];
   
    _programHandle = [GLESUtils loadProgram:vertexShaderPath
                 withFragmentShaderFilepath:fragmentShaderPath];
    if (_programHandle == 0) {
        NSLog(@" >> Error: Failed to setup program.");
        return;
    }
   
    glUseProgram(_programHandle);
   
    // Get the attribute position slot from program
    //
    _positionSlot = glGetAttribLocation(_programHandle, "vPosition");
   
    // Get the uniform model-view matrix slot from program
    //
    _modelViewSlot = glGetUniformLocation(_programHandle, "modelView");
   
    // Get the uniform projection matrix slot from program
    //
    _projectionSlot = glGetUniformLocation(_programHandle, "projection");
}

-(void)setupProjection
{
    // Generate a perspective matrix with a 60 degree FOV
    //
    float aspect = self.frame.size.width / self.frame.size.height;
    ksMatrixLoadIdentity(&_projectionMatrix);
    ksPerspective(&_projectionMatrix, 60.0, aspect, 1.0f, 20.0f);
   
    // Load projection matrix
    glUniformMatrix4fv(_projectionSlot, 1, GL_FALSE, (GLfloat*)&_projectionMatrix.m[0][0]);
}

- (void)updateTransform
{
    // Generate a model view matrix to rotate/translate/scale
    //
    ksMatrixLoadIdentity(&_modelViewMatrix);
   
    // Translate away from the viewer
    //
    ksMatrixTranslate(&_modelViewMatrix, self.posX, self.posY, self.posZ);
   
    // Rotate the triangle
    //
    ksMatrixRotate(&_modelViewMatrix, self.rotateX, 1.0, 0.0, 0.0);  // rotate X
   
    ksMatrixRotate(&_modelViewMatrix, self.rotateY, 0.0, 1.0, 0.0);  // rotate Y
   
   
    // Scale the triangle
    ksMatrixScale(&_modelViewMatrix, 1.0, 1.0, self.scaleZ);
   
    // Load the model-view matrix
    glUniformMatrix4fv(_modelViewSlot, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);
}

- (void)drawTriangle
{
    GLfloat vertices[] = {
        0.0f,  0.7f, 0.0f,
        -0.7f, -0.7f, 0.0f,
        0.7f,  -0.7f, 0.0f };
   
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices );
    glEnableVertexAttribArray(_positionSlot);
   
    // Draw triangle
    //
    glDrawArrays(GL_TRIANGLES, 0, 3);
}

- (void)drawTriCone
{
    GLfloat vertices[] = {
        0.7f, 0.7f, 0.0f,
        0.7f, -0.7f, 0.0f,
        -0.7f, -0.7f, 0.0f,
        -0.7f, 0.7f, 0.0f,
        0.0f, 0.0f, -1.0f,
    };
   
    GLubyte indices[] = {
        0, 1, 1, 2, 2, 3, 3, 0,
        4, 0, 4, 1, 4, 2, 4, 3
    };
   
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices );
    glEnableVertexAttribArray(_positionSlot);
   
    // Draw lines
    //
    glDrawElements(GL_LINES, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices);
}

- (void)drawCube  //方塊設定
{
    GLfloat vertices[] = {
        0.7f, 0.7f, 0.0f,       //  point 0
        0.7f, -0.7f, 0.0f,      //  point 1
        -0.7f, -0.7f, 0.0f,
        -0.7f, 0.7f, 0.0f,
        0.7f, 0.7f, -2.0f,
        0.7f, -0.7f, -2.0f,
        -0.7f, -0.7f, -2.0f,
        -0.7f, 0.7f, -2.0f,    // point 7
    };
   
    GLubyte indices[] = { // 此處為兩兩一組,設定點與點之間的連線
        0, 1, 1, 2, 2, 3, 3, 0,
        4, 0, 4,5, 5,1 , 6,2, 5,6 , 6,7, 7,4 ,7,3
    };
   
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices );
    glEnableVertexAttribArray(_positionSlot);
   
    // Draw lines
    glDrawElements(GL_LINES, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices);
}


- (void)render
{
    if (_context == nil)
        return;
   
    glClearColor(0, 1.0, 0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
   
    // Setup viewport
    //
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
   
    //[self drawTriangle];
    //[self drawTriCone];
    [self drawCube];
   
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self) {
        [self setupLayer];
        [self setupContext];
        [self setupProgram];
        [self setupProjection];
       
        [self resetTransform];
    }
   
    return self;
}

- (void)layoutSubviews
{
    [EAGLContext setCurrentContext:_context];
    glUseProgram(_programHandle);
   
    [self destoryBuffers];
   
    [self setupBuffers];
   
    [self updateTransform];
   
    [self render];
}

/*
 // Only override drawRect: if you perform custom drawing.
 // An empty implementation adversely affects performance during animation.
 - (void)drawRect:(CGRect)rect
 {
 // Drawing code
 }
 */

#pragma mark - Transform properties

- (void)toggleDisplayLink
{
    if (_displayLink == nil) {
        _displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkCallback:)];
        [_displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    else {
        [_displayLink invalidate];
        [_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        _displayLink = nil;
    }
}

- (void)displayLinkCallback:(CADisplayLink*)displayLink
{
    self.rotateX += displayLink.duration * 90; // 每 1/60 秒 會加一次角度   (duration = 1/60)*90  = 1.5
}

- (void)resetTransform
{
    if (_displayLink != nil) {
        [_displayLink removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
        _displayLink = nil;
    }
   
    _posX = 0.0;
    _posY = 0.0;
    _posZ = -5.5;
   
    _scaleZ = 1.0;
    _rotateX = 0.0;
    _rotateY = 0.0;
   
    [self updateTransform];
}

- (void)setPosX:(float)x
{
    _posX = x;
   
    [self updateTransform];
    [self render];
}

- (float)posX
{
    return _posX;
}

- (void)setPosY:(float)y
{
    _posY = y;
   
    [self updateTransform];
    [self render];
}

- (float)posY
{
    return _posY;
}

- (void)setPosZ:(float)z
{
    _posZ = z;
   
    [self updateTransform];
    [self render];
}

- (float)posZ
{
    return _posZ;
}

- (void)setScaleZ:(float)scaleZ
{
    _scaleZ = scaleZ;
   
    [self updateTransform];
    [self render];
}

- (float)scaleZ
{
    return _scaleZ;
}

- (void)setRotateX:(float)rotateX
{
    _rotateX = rotateX;
   
    [self updateTransform];
    [self render];
}

- (float)rotateX
{
    return _rotateX;
}

- (void)setRotateY:(float)rotateY
{
    _rotateY = rotateY;
   
    [self updateTransform];
    [self render];
}

- (float)rotateY
{
    return _rotateY;
}

#pragma mark

@end

9. 設定主要的運作檔 mainViewController.h,將Storyboard上的元件設定到此檔。
#import <UIKit/UIKit.h>
#import "OpenGLView.h"

@interface mainViewController : UIViewController

@property (strong, nonatomic) IBOutlet UIView *controlView;   最外圍的控制區
@property (strong, nonatomic) IBOutlet OpenGLView *openGLView;  // OPENGL 顯示區
@property (strong, nonatomic) IBOutlet UISlider *posXSlider;

@property (strong, nonatomic) IBOutlet UISlider *posYSlider;

@property (strong, nonatomic) IBOutlet UISlider *posZSlider;

@property (strong, nonatomic) IBOutlet UISlider *scaleZSlider;

@property (strong, nonatomic) IBOutlet UISlider *rotateXSlider;

@property (strong, nonatomic) IBOutlet UISlider *rotateYSlider;


@end



10. 設定mainViewController.m,設定Slider/Button的控制碼,此處是改動較多的地方

#import "mainViewController.h"

@interface mainViewController ()

@end

@implementation mainViewController

@synthesize posXSlider,posYSlider, posZSlider;
@synthesize scaleZSlider;
@synthesize rotateXSlider, rotateYSlider;

@synthesize openGLView;

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    [self resetControls];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)posXChange:(id)sender {
   
    openGLView.posX  = posXSlider.value;
   
    NSLog(@" >> current x is %f", posXSlider.value);
}

- (IBAction)posYChange:(id)sender {
   
    openGLView.posY = posYSlider.value;
   
     NSLog(@" >> current y is %f", posYSlider.value);
}

- (IBAction)posZChange:(id)sender {
   
    openGLView.posZ = posZSlider.value;
   
     NSLog(@" >> current z is %f", posZSlider.value);
}

- (IBAction)scaleZ:(id)sender {
   
    openGLView.scaleZ = scaleZSlider.value;
   
    NSLog(@" >> scale z is %3.3f", scaleZSlider.value);
}

- (IBAction)rotateX:(id)sender {
   
    //rotateYSlider.value = 0;
   // openGLView.rotateY = 0;
   
    openGLView.rotateX = rotateXSlider.value;

    NSLog(@" >> rotate x is %f", rotateXSlider.value);
}

- (IBAction)rotateY:(id)sender {
   
    //rotateXSlider.value = 0;
   // openGLView.rotateX = 0;
   
    openGLView.rotateY = rotateYSlider.value;
   
    NSLog(@" >> rotate Y is %f", rotateYSlider.value);
}


- (IBAction)autoButton:(id)sender {
   
    [openGLView toggleDisplayLink];
   
    UIButton * button = (UIButton *)sender;
    NSString * text = button.titleLabel.text;
    if ([text isEqualToString:@"Auto"]) {
        [button setTitle: @"Stop" forState: UIControlStateNormal];
    }
    else {
        [button setTitle: @"Auto" forState: UIControlStateNormal];
    }

}

- (IBAction)resetButton:(id)sender {

    [openGLView resetTransform];
    [openGLView render];
   
    [self resetControls];

}

- (void)resetControls
{
    [posXSlider setValue:self.openGLView.posX];
    [posYSlider setValue:self.openGLView.posY];
    [posZSlider setValue:self.openGLView.posZ];
   
    [scaleZSlider setValue:self.openGLView.scaleZ];
    [rotateXSlider setValue:self.openGLView.rotateX];
}

@end



11. 加入兩個GLSL檔,
FragmentShader.glsl

precision mediump float;

void main()
{
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

VertexShader.glsl
uniform mat4 projection;
uniform mat4 modelView;
attribute vec4 vPosition;

void  main(void)
{
    gl_Position = projection * modelView * vPosition;
}

12 結果顯示