2013年1月9日 星期三

OpenGL ES 2.0 tutorial for iOS 6 and Xcode 4.5.2 ﹣Part 2

在 OpenGL ES 2.0 tutorial for iOS 6 and Xcode 4.5.2 ﹣Part 1 中已經試過了最簡易實現的步驟,接著繼續加入 Shader  的使用。Shader 使用像 C 語言一樣的 GLSL 語言,主要有2種 Shaders,Vertex Shader 和 Fragment Shader,各自有各自的作用。讓我們加入 Shader!

建立一個空白檔案,取名為 SimpleVertex.glsl






在 SimpleVertex.glsl 輸入下面程式碼

attribute vec4 Position; // 1
attribute vec4 SourceColor; // 2

varying vec4 DestinationColor; // 3

void main(void) { // 4
    DestinationColor = SourceColor; // 5
    gl_Position = Position; // 6
}

接著建立另一個空白檔案,取名為 SimpleFragment.glsl


在 SimpleFragment.glsl 輸入下面程式碼

varying lowp vec4 DestinationColor; // 1

void main(void) { // 2
    gl_FragColor = DestinationColor; // 3
}

要執行 shader 必須編譯它,所以我們要建立一個 method 來做這件事。在 OpenGLView.m 輸入下面的程式碼,要放在 initWithFrame: 前面。

- (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
 
    // 1
    NSString* shaderPath = [[NSBundle mainBundle] pathForResource:shaderName 
        ofType:@"glsl"];
    NSError* error;
    NSString* shaderString = [NSString stringWithContentsOfFile:shaderPath 
        encoding:NSUTF8StringEncoding error:&error];
    if (!shaderString) {
        NSLog(@"Error loading shader: %@", error.localizedDescription);
        exit(1);
    }
 
    // 2
    GLuint shaderHandle = glCreateShader(shaderType);    
 
    // 3
    const char * shaderStringUTF8 = [shaderString UTF8String];    
    int shaderStringLength = [shaderString length];
    glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
 
    // 4
    glCompileShader(shaderHandle);
 
    // 5
    GLint compileSuccess;
    glGetShaderiv(shaderHandle, GL_COMPILE_STATUS, &compileSuccess);
    if (compileSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetShaderInfoLog(shaderHandle, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        exit(1);
    }
 
    return shaderHandle;
 
}

- (void)compileShaders {
 
    // 1
    GLuint vertexShader = [self compileShader:@"SimpleVertex" 
        withType:GL_VERTEX_SHADER];
    GLuint fragmentShader = [self compileShader:@"SimpleFragment" 
        withType:GL_FRAGMENT_SHADER];
 
    // 2
    GLuint programHandle = glCreateProgram();
    glAttachShader(programHandle, vertexShader);
    glAttachShader(programHandle, fragmentShader);
    glLinkProgram(programHandle);
 
    // 3
    GLint linkSuccess;
    glGetProgramiv(programHandle, GL_LINK_STATUS, &linkSuccess);
    if (linkSuccess == GL_FALSE) {
        GLchar messages[256];
        glGetProgramInfoLog(programHandle, sizeof(messages), 0, &messages[0]);
        NSString *messageString = [NSString stringWithUTF8String:messages];
        NSLog(@"%@", messageString);
        exit(1);
    }
 
    // 4
    glUseProgram(programHandle);
 
    // 5
    _positionSlot = glGetAttribLocation(programHandle, "Position");
    _colorSlot = glGetAttribLocation(programHandle, "SourceColor");
    glEnableVertexAttribArray(_positionSlot);
    glEnableVertexAttribArray(_colorSlot);
}


接著,在 initWithFrame 中,在呼叫 render 之前插入下面這一行:

[self compileShaders];

這時 Xcode 應該會出現警告,因為還有變數還沒有定義,在 OpenGLView.h 加入下面2個變數:

GLuint _positionSlot;
GLuint _colorSlot;

定義我們要的 Vertex 結構,放到 OpenGLView.m 裡

typedef struct {
    float Position[3];
    float Color[4];
} Vertex;

const Vertex Vertices[] = {
    {{1, -1, 0}, {1, 0, 0, 1}},
    {{1, 1, 0}, {0, 1, 0, 1}},
    {{-1, 1, 0}, {0, 0, 1, 1}},
    {{-1, -1, 0}, {0, 0, 0, 1}}
};

const GLubyte Indices[] = {
    0, 1, 2,
    2, 3, 0
};

透過 VBO 的方式來存取我們要的 Vertices 和 Indices

- (void)setupVBOs {
 
    GLuint vertexBuffer;
    glGenBuffers(1, &vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);
 
    GLuint indexBuffer;
    glGenBuffers(1, &indexBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
 
}


別忘了在 initWithFrame 中加入這一行,要放在 render 之前

[self setupVBOs];

更新 render: method,用下面的程式碼取代原來的。


- (void)render {
    glClearColor(0, 104.0/255.0, 55.0/255.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
 
    // 1
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);
 
    // 2
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 
        sizeof(Vertex), 0);
    glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, 
        sizeof(Vertex), (GLvoid*) (sizeof(float) * 3));
 
    // 3
    glDrawElements(GL_TRIANGLES, sizeof(Indices)/sizeof(Indices[0]), 
        GL_UNSIGNED_BYTE, 0);
 
    [_context presentRenderbuffer:GL_RENDERBUFFER];
}

完成了!執行後的結果如下:




如果對 Shader 的運作方式不了解的話,建議先查一下 GLSL 相關的資料,我也發現 OpenGL ES 

的版本也有關係,1.1 和 2.0 的呼叫方式並不相同,最好能清楚它們的差別。



沒有留言:

張貼留言