自定义着色器基本步骤
- 需要创建2个基本对象才能用着⾊器进⾏渲染: 着⾊器对象和程序对象.
- 获取链接后着⾊器对象的⼀般过程包括6个步骤:
- 创建⼀个顶点着⾊器对象和⼀个⽚段着⾊器对象
- 将源代码链接到每个着⾊器对象
- 编译着⾊器对象
- 创建⼀个程序对象
- 将编译后的着⾊器对象连接到程序对象
- 链接程序对象
绘制图片基本思路
- 初始化图层使用、图形上下文,相关缓冲区的设置和清理。
- 创建、编译、链接着色器程序。
- 读取、加载相关数据。
- 执行绘制流程。
第一部分
使用OpenGL ES自定义着色器程序加载图片,只需要使用UIView
即可。
第一步主要是做好初始化准备:
- 重写
layerClass
,将返回的图层从CALayer
替换成CAEAGLLayer
,并完成参数设置。 - 初始化图形上下文,设置好OpenGL ES的API版本。
- 设置
Render buffer Object
和Frame buffer Object
。
Frame buffer Object:是一组颜色、深度、模板附着点简称为FBO
。
纹理对象可以连接到FBO中的颜色附着点和深度附着点,另一种连接到深度附着点和模板附着点的叫做渲染缓冲对象(RBO)。
Render buffer Object:应用分配的2D图像缓冲区,需要附着在FrameBuffer上(FBO)。
RenderBuffer又可分为3类:colorBuffer
、depthBuffer
、stencilBuffer
。
在一个FBO中有多个*颜色关联点、一个深度关联点,和一个模板关联*
。每个FBO中至少有一个颜色关联点,其数目与实体显卡相关。可以通过GL_MAX_COLOR_ATTACHMENTS_EXT
来查询颜色关联点的最大数目。
需要注意:FBO中并没有存储图像,只有多个关联点。
实际上可以把帧缓冲对象理解为一个插线板,自己本身没有内存,但是可以连接纹理对象和渲染缓冲对象两种外设,这两种外设是有内存的来存储图像数据。

-(void)setupFrameBuffer{
GLuint buffer;
glGenBuffers(1, &buffer);
self.frameBuffer = buffer;
glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.renderBuffer);
}
-(void)setupRenderBuffer{
GLuint buffer;
glGenRenderbuffers(1, &buffer);
self.renderBuffer = buffer;
glBindRenderbuffer(GL_RENDERBUFFER, self.renderBuffer);
[self.Context renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEagLayer];
}
第二部分
创建、编译、链接着色器程序。
attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main()
{
varyTextCoord = textCoordinate;
gl_Position = position;
}
precision highp float;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
gl_FragColor = texture2D(colorMap, varyTextCoord);
}
-(GLuint)loadShaders:(NSString *)vert Withfrag:(NSString *)frag
{
GLuint verShader, fragShader;
GLint program = glCreateProgram();
[self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
[self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
glAttachShader(program, verShader);
glAttachShader(program, fragShader);
glDeleteShader(verShader);
glDeleteShader(fragShader);
return program;
}
- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
const GLchar* source = (GLchar *)[content UTF8String];
*shader = glCreateShader(type);
glShaderSource(*shader, 1, &source,NULL);
glCompileShader(*shader);
}
创建好顶点着色器对象和片元着色器对象后把这两个对象链接到程序对象,最后链接程序对象。
glLinkProgram(self.programe);
glUseProgram(self.programe);
第三步 读取顶点、纹理数据。
参考步骤如下
{
//顶点缓存区
GLuint attrBuffer;
//申请识符
glGenBuffers(1, &attrBuffer);
//将attrBuffer绑定到GL_ARRAY_BUFFER标识符上
glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
//把顶点数据从CPU内存复制到GPU上
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
//将顶点数据通过myPrograme中的传递到顶点着色程序的position
GLuint position = glGetAttribLocation(self.programe, "position");
//设置合适的格式从buffer里面读取数据
glEnableVertexAttribArray(position);
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
}
读取纹理:
读取纹理图片时,需要先将所读取的压缩格式图片还原成位图。
//1、将 UIImage 转换为 CGImageRef
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
//获取图片字节数 宽*高*4(RGBA)
GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
CGRect rect = CGRectMake(0, 0, width, height);
CGContextDrawImage(spriteContext, rect, spriteImage);
CGContextRelease(spriteContext);
绘制图形
设置好纹理采样器之后就可以选择以指定的图元方式绘制内容。
glUniform1i(glGetUniformLocation(self.myPrograme, "colorMap"), 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
[self.context presentRenderbuffer:GL_RENDERBUFFER];
案例查看;
网友评论