原文参考地址:http://www.cnblogs.com/zilongshanren/archive/2011/08/08/2131019.html
一、编译Vertex Shaders和Fragment Shaders
目前为止,xcode仅仅会把这个两个文件(simple.vertsh和simple.fragsh)copy到application bundle中。我们还需要在运行编译和运行这些Shaders。
你会很诧异,为什么要在app运行时编译代码?
这样做的好处是:我们的Shaders不用依赖于某种图形芯片(这样可以跨平台嘛)。
下面开始加入动态编译的代码(两种方式。1.直接在OpenGLView写入;2.创建工具类)
我选用第二种方式创建动态编译的代码:
在Project中,新建继承NSObject的LVApplicationShaderUtils类。
贴下代码:
- (GLuint)compileShader:(NSString *)shaderFileName withType:(GLenum)shaderType { NSString * shaderName = [[shaderFileName lastPathComponent] stringByDeletingPathExtension]; NSString * shaderFileType = [shaderFileName pathExtension]; NSLog(@"文件名称:%@ 文件类型:%@",shaderName,shaderFileType); //!> 1 NSString * shaderPath = [[NSBundle mainBundle] pathForResource:shaderName ofType:shaderFileType]; NSLog(@"文件路径:%@",shaderPath); NSError * error = nil; NSString * shaderString = [NSString stringWithContentsOfFile:shaderPath encoding:NSUTF8StringEncoding error:&error]; if (!shaderPath) { NSLog(@"错误加载shader(%@):%@",shaderFileName,error.localizedDescription); return 0; } //!> 2 GLuint shaderHandle = glCreateShader(shaderType); //!> 3 const char * shaderStringUTF8 = [shaderString UTF8String]; GLint shaderStringLength = (GLint)[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(@"错误编译shader(%@):%@",shaderFileName,messageString); return 0; } return shaderHandle; }
如果你感觉跟入门笔记一中的方法有很多不同,恭喜你。
1.找到.vertsh 和 .fragsh文件路径。
2.调用glCreateShader来创建一个代表shader的OpenGL对象,需要一个参数
/* Shaders */ #define GL_FRAGMENT_SHADER 0x8B30 // 代表片元着色器类型对象 #define GL_VERTEX_SHADER 0x8B31 // 代表顶点着色器类型对象
3.glShaderSource,让OpenGL获取到这个Shader的源代码,把NSString文件内容转换成C-String(null-terminated string)。
4.最后,调用glCompileShader在运行时编译Shader。
5.使用glGetShaderiv和glGetShaderInfoLog打印error日志。
/* Boolean */ #define GL_FALSE 0 #define GL_TRUE 1
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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(@"错误编译shader(%@):%@",shaderFileName,messageString); return 0; }
这个代码写法一般都是这样写的。可以直接copy~~
二、如果编译好了Vertex Shader和Fragment Shader之后,我们需要把两个Shader关联起来。
typedef NS_ENUM(NSInteger,compileProgramError) { compileProgramErrorShaders = -1101, //!> Shader创建失败 compileProgramErrorProgram = -1102, //!> Program object 创建失败 compileProgramErrorLink = -1103 //!> link 失败 };
- (NSInteger)linkProgramHandle { //!> 1 GLuint vertexShader = [self compileShader:@"Simple.vertsh" withType:GL_VERTEX_SHADER]; GLuint fragmentShader = [self compileShader:@"Simple.fragsh" withType:GL_FRAGMENT_SHADER]; if ((vertexShader == 0)||(fragmentShader == 0)) { NSLog(@"错误:错误编译Shaders"); return compileProgramErrorShaders; } //!> 2 GLuint programHandle = glCreateProgram(); if (programHandle == 0) { NSLog(@"错误:不能创建Program object"); return compileProgramErrorProgram; } 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(@"Error link Shaders:%@",messageString); return compileProgramErrorLink; } return programHandle; }
解析:
1.用compileShader方法动态编译了Vertex Shader和Fragment Shader两个对象。
2.调用glCreateProgram、glAttachShader、glLinkProgram连接Vertex Shader和Fragment Shader形成一个完成的Program。
3.调用glGetProgram、glGetProgramInfoLog方法来检查error,跟动态编译Shader的方法中检查方法类似。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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(@"Error link Shaders:%@",messageString); return compileProgramErrorLink; }
常规方法都是这样写的,也是可以直接copy的哦~~
最后准确无误了,就返回programHandle,项目句柄
三、接下来让OpenGL执行Program,使用的就是programHandle。
OpenGL示例代码:https://github.com/nLoser/OpenGLES_Study