在上一篇博客 iOS — OpenGLES之着色器(shader)语法介绍 中,简要介绍了OpenGLES的着色器shader的基本语法,以及Vertex Shader和Fragment Shader的差异。本文中,将简要介绍着色器(shader)的编译、链接及使用。
Vertex Shader如下:
// variable pass into
attribute vec4 Position; // position of vertex
attribute vec4 SourceColor; // color of vertex
// variable pass out into fragment shader
// varying means that calculate the color of every pixel between two vertex linearly(smoothly) according to the 2 vertex's color
varying vec4 DestinationColor;
void main(void) {
DestinationColor = SourceColor;
// gl_Position is built-in pass-out variable. Must config for in vertex shader
gl_Position = Position;
}
Fragment Shader如下:
varying lowp vec4 DestinationColor;
void main(void) {
// must set gl_FragColor for fragment shader
gl_FragColor = DestinationColor;
}
关于Shader的具体含义,请参考上一篇博客 iOS — OpenGLES之着色器(shader)语法介绍 。
着色器脚本的编译过程比较固定,主要是以下步骤:
封装函数如下:
+ (GLuint)compileShader:(NSString*)shaderName withType:(GLenum)shaderType {
// 1 查找shader文件
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 创建一个代表shader的OpenGL对象, 指定vertex或fragment shader
GLuint shaderHandle = glCreateShader(shaderType);
// 3 获取shader的source
const char* shaderStringUTF8 = [shaderString UTF8String];
int shaderStringLength = [shaderString length];
glShaderSource(shaderHandle, 1, &shaderStringUTF8, &shaderStringLength);
// 4 编译shader
glCompileShader(shaderHandle);
// 5 查询shader对象的信息
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;
}
对于Vertex Shader和Fragment Shader都要编译:
GLuint vertexShader = [ShaderOperations compileShader:shaderVertex withType:GL_VERTEX_SHADER];
GLuint fragmentShader = [ShaderOperations compileShader:shaderFragment withType:GL_FRAGMENT_SHADER];
该函数接收的第二个参数用于指定Vertex或Fragment:
#define GL_FRAGMENT_SHADER 0x8B30
#define GL_VERTEX_SHADER 0x8B31
连接Vertex Shader和Fragment Shader成一个完整的OpenGL Shader Program。
GLuint _glProgram = glCreateProgram();
glAttachShader(_glProgram, vertexShader);
glAttachShader(_glProgram, fragmentShader);
glLinkProgram(_glProgram);
// 检查link状态
GLint linkSuccess;
glGetProgramiv(_glProgram, GL_LINK_STATUS, &linkSuccess);
if (linkSuccess == GL_FALSE) {
GLchar messages[256];
glGetProgramInfoLog(_glProgram, sizeof(messages), 0, &messages[0]);
NSString *messageString = [NSString stringWithUTF8String:messages];
NSLog(@"%@", messageString);
exit(1);
}
GLuint _positionSlot; // 用于绑定shader中的Position参数
GLuint _colorSlot; // 用于绑定shader中的SourceColor参数
glUseProgram(_glProgram); // 让OpenGL执行glProgram
_positionSlot = glGetAttribLocation(_glProgram, "Position");
_colorSlot = glGetAttribLocation(_glProgram, "SourceColor");
这样,通过 _positionSlot 和_colorSlot 就可以向Shader中传递所需参数,分别对应Position和SourceColor。如给_positionSlot传递数据,即顶点数组数据:
GLfloat vertices[] = {
0.0f, 0.5f, 0.0f,
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f };
// 给_positionSlot传递vertices数据
glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices );
glEnableVertexAttribArray(_positionSlot); //使用时要enable
本文的一系列demo都可在github中找到,DemoOpenGL,如有不准确的地方,欢迎指正。
以上部分,简要介绍了着色器(Shader)的基本使用情况。主要参考资料:
OpenGL Tutorial for iOS: OpenGL ES 2.0
iOS — OpenGLES之着色器(shader)语法介绍
OpenGL ES渲染管线与着色器