建立一個空白檔案,取名為 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,用下面的程式碼取代原來的。
如果對 Shader 的運作方式不了解的話,建議先查一下 GLSL 相關的資料,我也發現 OpenGL ES 的版本也有關係,1.1 和 2.0 的呼叫方式並不相同,最好能清楚它們的差別。 |
沒有留言:
張貼留言