尝试用可编程管线简单地加载一张图片。
准备工作
修改main.storyboard默认View、Controller,关联View,CCViewController继承自UIViewController,CCView继承自UIView image.png创建可编程管线的顶点着色器文件以及片元着色器文件 image.png
值得注意的是,文件命名及后缀不重要,只要不与系统的文件后缀冲突,以及不影响自己辨别均可
顶点着色器(shaderv.vsh)
自己写记得把中文注释去掉,因为随时可能发生报错的情况
//Vertex shader
//顶点数据
attribute vec4 position;
//纹理
attribute vec2 textCoordinate;
//旋转矩阵
uniform mat4 rotateMatrix;
//将纹理数据传递到片元着色器去
varying lowp vec2 varyTextCoord;
void main(){
//将textCoordinate 通过 varyTextCoord 传递到片元着色器中去
varyTextCoord = textCoordinate;
vec4 vPos = position;
//将顶点应用旋转变换
vPos = vPos * rotateMatrix;
//内建变量,gl_Position 必须赋值
gl_Position = vPos;
}
片元着色器(shaderf.fsh)
//OpenGL ES 中的三种修饰类型 uniform attribute varying
//GLSL是一门写着色器程序的语言OpenGL shading language
//OpenGL 固定管线:高级API,通过传递参数实现效果
//可编程管线:能让你对OpenGL渲染过程中其中一些着色环节进行自定义
//可以自定义的环节
/*
1.顶点着色器->处理每个顶点,确定位置,以及变换
2.片元着色器->片元,定义每一个像素
不采用GLBaseEffect,使用编译链接自定义shader,用简单GLSL来实现顶点着色器\片元着色器,并且实现图形简单变换
思路:
1.创建图层
2.创建上下文
3.清空缓存区
4.设置RenderBuffer,FrameBuffer
5.kai'shi'hui'zhi
*/
/*
-------------------------uniform--------------------------------------
uniform是由外部application程序传递给Vertex Shader,fragment Shader变量
A.由application通过glUniform**()函数传递值的
B.在Vertex Shader,fragment Shader内部中,类似C语言的const,它不能被shader修改
注意:Uniform变量,shader只能用,不能改!!!
例如:
Uniform mat4 viewProjectMatrix;
Uniform mat4 viewMatrix;
Uniform vec3 lightPosition;
-------------------------attribute--------------------------------------
一般attribute来表示顶点坐标\法线\纹理坐标\顶点颜色
attribute vec4 a_position;
attribute vec2 a_textCoord0;
注意:attribute只能在Vertex shader中使用,不能再fragment shader中声明attribute变量,也不能被fragment shader使用
-------------------------------varying-------------------------------------
varying,在vertex和fragement shader之间传递数据用
varying vec2 a_textCoord0
*/
varying lowp vec2 varyTextCoord;
//2D纹理贴图
uniform sampler2D colorMap;
void main(){
//内建变量gl_FragColor必须赋值
gl_FragColor = texture2D(colorMap,varyTextCoord);
}
CCView.m
#import <OpenGLES/ES2/gl.h>
@interface CCView ()
/** CAEAGLLayer */
@property(nonatomic,strong)CAEAGLLayer *myEAGLayer;
/** Context */
@property(nonatomic,strong)EAGLContext *myContext;
/** RenderColor */
@property(nonatomic,assign)GLuint myColorRenderBuffer;
/** FrameColor */
@property(nonatomic,assign)GLuint myColorFrameBuffer;
/** program */
@property(nonatomic,assign)GLuint myProgram;
@end
//入口函数,需要调用的步骤
-(void)layoutSubviews{
//1.设置图层
[self setUpLayer];
//2.创建上下文
[self setUpContext];
//3.清空缓冲区
[self deleteRenderAndFrameBuffer];
//4.设置RenderBuffer
[self setUPRenderBuffer];
//5.设置FrameBuffer
[self setUpFrameBuffer];
//6.开始绘制
[self renderLayer];
}
1.设置图层
-(void)setUpLayer{
self.myEAGLayer = (CAEAGLLayer *)self.layer;
//2.设置比例因子
[self setContentScaleFactor:[UIScreen mainScreen].scale];
//3.默认不透明,想要其可见,需设置为不透明
self.myEAGLayer.opaque = YES;
//4.描述因子
/*
kEAGLDrawablePropertyRetainedBacking
绘图表面显示之后是否保留其内容,一般设置为false;
它是一个key值,通过一个nsnumber包装bool值
kEAGLDrawablePropertyColorFormat
绘制对象内部的颜色缓冲区格式
kEAGLColorFormatRGBA8 ,4*8,32位的颜色
kEAGLColorFormatRGB565; 16位
const kEAGLColorFormatSRGBA8 SRGB
*/
self.myEAGLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false],kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat, nil];
}
+(Class)layerClass{
return [CAEAGLLayer class];
}
2.创建上下文
-(void)setUpContext{
//1.指定API版本
EAGLRenderingAPI api = kEAGLRenderingAPIOpenGLES2;
//2.创建图形上下文
EAGLContext *context = [[EAGLContext alloc] initWithAPI:api];
//3.创建是否成功
if (context == NULL) {
NSLog(@"Create Context Failed!!!");
return;
}
//4.设置图形上下文
if (![EAGLContext setCurrentContext:context]) {
NSLog(@"setCurrentContext Failed!");
return;
}
//5.将局部变成全局
self.myContext = context;
}
3.清空缓冲区
-(void)deleteRenderAndFrameBuffer{
/*
可参考PPT
buffer氛围FrameBuffer和RenderBuffer 2大类
frameBuffer(FBO)相当于RenderBuffer的管理者
rendeerbuffer又分为三类:colorbuffer,depthbuffer,setncilbuffer
常用函数
1.绑定buffer标识
glGenBuffers(<#GLsizei n#>, <#GLuint *buffers#>)
glGenRenderbuffers(<#GLsizei n#>, <#GLuint *renderbuffers#>)
glGenFramebuffers(<#GLsizei n#>, <#GLuint *framebuffers#>)
2.绑定空间
glBindBuffer(<#GLenum target#>, <#GLuint buffer#>)
glBindRenderbuffer(<#GLenum target#>, <#GLuint renderbuffer#>)
glBindFramebuffer(<#GLenum target#>, <#GLuint framebuffer#>)
3.删除缓冲区空间
glDeleteBuffers(1, &_myColorRenderBuffer);
*/
glDeleteBuffers(1, &_myColorRenderBuffer);
self.myColorRenderBuffer = 0;
glDeleteBuffers(1, &_myColorFrameBuffer);
self.myColorFrameBuffer = 0;
}
4.设置RenderBuffer
-(void)setUPRenderBuffer{
//1.定义一个缓冲区标记
GLuint buffer;
//2.申请一个缓冲区标记
glGenRenderbuffers(1, &buffer);
//3.
self.myColorRenderBuffer = buffer;
//4将标识符绑定GL_RENDERBUFFER
glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
//5.
[self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myEAGLayer];
}
5. 设置FrameBuffer
-(void)setUpFrameBuffer{
//1.定义一个缓存区标记
GLuint buffer;
//2.三个作用都一样都是申请缓冲区标记
glGenFramebuffers(1, &buffer);
// glGenRenderbuffers(<#GLsizei n#>, <#GLuint *renderbuffers#>)
// glGenBuffers(<#GLsizei n#>, <#GLuint *buffers#>)
//3.
self.myColorFrameBuffer = buffer;
//4.
glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
//5.将myColorRenderBuffer通过glFrameBufferRenderBuffer绑定到附着点上,GL_COLOR_ATTACHEMENT0(颜色附着点)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
//6.frame buffer仅仅是管理者,不需要分配空间;render buffer的存储空间的分配,对于不同的render buffer,使用不同的API进行分配,而只有分配空间的时候,render buffer句柄才确定其类型
//myColorRenderBuffer渲染缓存区分配存储空间
[self.myContext renderbufferStorage:GL_FRAMEBUFFER fromDrawable:self.myEAGLayer];
}
6. 开始绘制
-(void)renderLayer{
//开始写顶点着色器/片元着色器
//Vertex shader
//fragment Shader
//已经写好了顶点shaderv.vsh/片元shaderf.fsh
glClearColor(0, 1, 0, 1);
glClear(GL_COLOR_BUFFER_BIT);
//2.设置视口大小
CGFloat scale = [UIScreen mainScreen].scale;
glViewport(self.frame.origin.x *scale, self.frame.origin.y*scale, self.frame.size.width*scale, self.frame.size.height*scale);
//3.读取顶点、片元着色器程序
//获取路径
NSString *vertFile = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"vsh"];
NSString *fragFile = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"fsh"];
NSLog(@"vertFile:%@",vertFile);
NSLog(@"fragFile:%@",fragFile);
//4.加载shader
self.myProgram = [self LoadShader:vertFile withFrag:fragFile];
//5.链接
glLinkProgram(self.myProgram);
//获取link的状态是否失败
GLint linkStatus;
glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE) {
//获取失败信息
GLchar message[512];
glGetProgramInfoLog(self.myProgram, sizeof(message), 0, &message[0]);
//将C字符串转换为OC字符串
NSString *messageStr = [NSString stringWithUTF8String:message];
NSLog(@"Program Link Error: %@",messageStr);
return;
}
//5.使用program
glUseProgram(self.myProgram);
//6.设置顶点、纹理坐标
//前3个是顶点坐标,后2个是纹理坐标
GLfloat attrArr[] =
{
0.5f, -0.5f, 1.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 1.0f, 1.0f, 0.0f,
};
//--处理顶点数据
GLuint attrBuffer;
//申请一个缓冲标记
glGenBuffers(1, &attrBuffer);
//绑定缓冲区
glBindBuffer(GL_ARRAY_BUFFER, attrBuffer);
//将顶点缓冲区从CPU内存复制到GPU内存中
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
//1.
GLuint position = glGetAttribLocation(self.myProgram, "position");
//2.
glEnableVertexAttribArray(position);
//3.设置读取方式
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);
//处理纹理数据
//1.获取纹理的位置-Program
GLuint textCoor = glGetAttribLocation(self.myProgram, "textCoordinate");
//2.同顶点
glEnableVertexAttribArray(textCoor);
//3.//最后一个参数,起点
glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);
//加载纹理
//通过一个自定义的方法加载纹理
[self setupTexture:@"timg-2"];
//1.直接用3D数学的公式来实现旋转
//2.Uniform
//旋转!!!矩阵 -> Uniform 传递到vsh,fsh
//旋转180度-->转弧度
float radius = 180 * 3.1415925f/180.0f;
//旋转矩阵公式
float s = sin(radius);
float c = cos(radius);
//构建旋转矩阵-围绕z轴旋转-列矩阵
GLfloat zRotation[16] = {
c,-s,0 , 0,
s, c,0 , 0,
0, 0,1.0, 0,
0, 0,0 ,1.0,
};
//获取位置
GLuint rotate = glGetUniformLocation(self.myProgram, "rotateMatrix");
//将旋转矩阵通过Uniform传递进去
glUniformMatrix4fv(rotate, 1, GL_FALSE, (GLfloat *)&zRotation[0]);
//绘制,三角形绘制,从0开始,6个顶点
glDrawArrays(GL_TRIANGLES, 0, 6);
[self.myContext presentRenderbuffer:GL_RENDERBUFFER];
}
封装的辅助函数
-(GLuint)LoadShader:(NSString *)vert withFrag:(NSString *)frag{
//1.定义2个临时着色器变量对象
GLuint verShader,fragShader;
GLuint program = glCreateProgram();
//2.编译shader
[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.释放
glDeleteShader(verShader);
glDeleteShader(fragShader);
return program;
}
//编译shader
-(void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
//读取shader路径
NSString *content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
//将oc字符串转为C字符串
const GLchar *source = (GLchar *)[content UTF8String];
//创建shader
*shader = glCreateShader(type);
//将着色器的代码,附到shader上
glShaderSource(*shader, 1, &source, NULL);
//将着色器代码编译成目标代码
glCompileShader(*shader);
}
网友评论