上一篇我们提到固定着色器同时继承GLKViewController
绘制图片,那么现在,将讲述如何自定义着色器绘制图片。
由于苹果没有提供·相应的文件,因此我们需要自己创建文件使用(相当于一个字符串),一般后缀使用代表顶点着色器,表示片元着色器,现在看看两个文件的内容。
在顶点着色器中position定义了图片的顶点位置,textCoordinate是纹理坐标,varyTextCoodi其实也是一个纹理坐标,但是它的作用是一个的角色,把顶点着色器的纹理传递到片元着色器中,为了达到这个目地,我们需要注意,,如果不需要传递,可以不用保持一致。
那么在函数中,我们进行纹理赋值,由于案例中的图片不需要做形变,因此直接将坐标传出去。
//顶点坐标
attribute vec4 position;
//纹理坐标
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main(){
varyTextCoord = textCoordinate;
gl_Position = position;
}
下面,让我们看看片元着色器需要做的事情,首先定义了一片元着色器的精度,然后varyTextCoord获得顶点着色器的纹理坐标,并且定义了纹理。这是两个必须要获取的数据。
main函数中我们获取当前像素下面的纹素。
函数是一个常用的函数,当我们调用这个内建函数之后,会返回一个(指的颜色),最后要给到最终结果的内建变量。
precision highp float;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
//lowp vec4 temp = texture2D(colorMap, varyTextCoord);
//gl_FragColor = temp;
//参数1:纹理
//参数2:纹理坐标
gl_FragColor = texture2D(colorMap, varyTextCoord);
}
顶点着色器/片元着色器的手动编译连接使用
在开始绘制之前,我们需要加载编译shader
/加载shader
-(GLuint)loadShaders:(NSString *)vert Withfrag:(NSString *)frag
{
//1.定义2个零时着色器对象
GLuint verShader, fragShader;
//创建program
GLint program = glCreateProgram();
//2.编译顶点着色程序、片元着色器程序
//参数1:编译完存储的底层地址
//参数2:编译的类型,GL_VERTEX_SHADER(顶点)、GL_FRAGMENT_SHADER(片元)
//参数3:文件路径
[self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
[self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
//3.创建最终的程序
glAttachShader(program, verShader);
glAttachShader(program, fragShader);
//4.释放不需要的shader
glDeleteShader(verShader);
glDeleteShader(fragShader);
return program;
}
//编译shader
- (void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
//1.读取文件路径字符串
NSString* content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
const GLchar* source = (GLchar *)[content UTF8String];
//2.创建一个shader(根据type类型)
*shader = glCreateShader(type);
//3.将着色器源码附加到着色器对象上。
//参数1:shader,要编译的着色器对象 *shader
//参数2:numOfStrings,传递的源码字符串数量 1个
//参数3:strings,着色器程序的源码(真正的着色器程序源码)
//参数4:lenOfStrings,长度,具有每个字符串长度的数组,或NULL,这意味着字符串是NULL终止的
glShaderSource(*shader, 1, &source,NULL);
//4.把着色器源代码编译成目标代码
glCompileShader(*shader);
}
接下来,就开始我们绘制图片的思路:
1.创建图层
2.创建上下文
3.清空缓存区
4.设置RenderBuffer
5.设置FrameBuffer
6.开始绘制
创建图层
//1.设置图层
-(void)setupLayer
{
//1.创建特殊图层
/*
重写layerClass,将CCView返回的图层从CALayer替换成CAEAGLLayer
*/
self.myEagLayer = (CAEAGLLayer *)self.layer;
//2.设置scale
[self setContentScaleFactor:[[UIScreen mainScreen]scale]];
NSLog(@"%f",[[UIScreen mainScreen]scale]);
//3.设置描述属性,这里设置不维持渲染内容以及颜色格式为RGBA8
/*
kEAGLDrawablePropertyRetainedBacking 表示绘图表面显示后,是否保留其内容。
kEAGLDrawablePropertyColorFormat
可绘制表面的内部颜色缓存区格式,这个key对应的值是一个NSString指定特定颜色缓存区对象。默认是kEAGLColorFormatRGBA8;
kEAGLColorFormatRGBA8:32位RGBA的颜色,4*8=32位
kEAGLColorFormatRGB565:16位RGB的颜色,
kEAGLColorFormatSRGBA8:sRGB代表了标准的红、绿、蓝,即CRT显示器、LCD显示器、投影机、打印机以及其他设备中色彩再现所使用的三个基本色素。sRGB的色彩空间基于独立的色彩坐标,可以使色彩在不同的设备使用传输中对应于同一个色彩坐标体系,而不受这些设备各自具有的不同色彩坐标的影响。
*/
self.myEagLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:@false,kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat,nil];
}
+(Class)layerClass
{
return [CAEAGLLayer class];
}
设置上下文
我们依然使用EAGLContext创建上下文,这个必须要做事情。
//2.设置上下文
-(void)setupContext
{
//1.指定OpenGL ES 渲染API版本,我们使用2.0
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
//2.创建图形上下文
EAGLContext *context = [[EAGLContext alloc]initWithAPI:api];
//3.判断是否创建成功
if (!context) {
NSLog(@"Create context failed!");
return;
}
//4.设置图形上下文
if (![EAGLContext setCurrentContext:context]) {
NSLog(@"setCurrentContext failed!");
return;
}
//5.将局部context,变成全局的
self.myContext = context;
清空缓存区
缓存区使用完之后,是需要清空的,不然会照成不必要的内存浪费。
//3.清空缓存区
-(void)deleteRenderAndFrameBuffer
{
/*
buffer分为frame buffer 和 render buffer2个大类。
其中frame buffer 相当于render buffer的管理者。
frame buffer object即称FBO。
render buffer则又可分为3类。colorBuffer、depthBuffer、stencilBuffer。
*/
glDeleteBuffers(1, &_myColorRenderBuffer);
self.myColorRenderBuffer = 0;
glDeleteBuffers(1, &_myColorFrameBuffer);
self.myColorFrameBuffer = 0;
}
设置RenderBuffer
RenderBuffer是由FrameBuffer管理的,所以先设置RenderBuffer
//4.设置RenderBuffer
-(void)setupRenderBuffer
{
//1.定义一个缓存区ID
GLuint buffer;
//2.申请一个缓存区标志
glGenRenderbuffers(1, &buffer);
//3.
self.myColorRenderBuffer = buffer;
//4.将标识符绑定到GL_RENDERBUFFER
glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
//5.将可绘制对象drawable object's CAEAGLLayer的存储绑定到OpenGL ES renderBuffer对象
[self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEagLayer];
}
设置FrameBuffer
//5.设置FrameBuffer
-(void)setupFrameBuffer
{
//1.定义一个缓存区ID
GLuint buffer;
//2.申请一个缓存区标志
//glGenRenderbuffers(1, &buffer);
//glGenFramebuffers(1, &buffer);
glGenBuffers(1, &buffer);
//3.
self.myColorFrameBuffer = buffer;
//4.
glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
/*生成帧缓存区之后,则需要将renderbuffer跟framebuffer进行绑定,
调用glFramebufferRenderbuffer函数进行绑定到对应的附着点上,后面的绘制才能起作用
*/
//5.将渲染缓存区myColorRenderBuffer 通过glFramebufferRenderbuffer函数绑定到 GL_COLOR_ATTACHMENT0上。
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
}
纹理加载流程
纹理加载.png开始绘制
绘制流程图这就是我们开始绘制图片的流程,我们最开始加载的shader也是在这里调用
网友评论