OpenGL ES内容(2)
2020-09-30 本文已影响0人
ZZ_军哥
废话不多说.开始写代码,用OpenGL ES加载一张图片(代码已经经过我测试)
导入系统库
#import <GLKit/GLKit.h> //由iOS提供,里面提供了一些对OpenGL ES封装的内容
#import <OpenGLES/ES3/gl.h> //OpenGL ES 系统库
#import <OpenGLES/ES3/glext.h> //OpenGL ES 系统库
@interface TestView ()
@property(nonatomic,strong)CAEAGLLayer *myLayer; //显示的layer
@property(nonatomic,strong)EAGLContext *myContext; //图形上下文
@property(nonatomic,assign)GLuint myProgram; //链接两个shader的连接器
@property(nonatomic,assign)GLuint frameBuffer; //帧缓冲区
@property(nonatomic,assign)GLuint renderBuffer; //渲染缓冲区
@end
新建一个TestView类,添加到VC的View上,重写TestView 的Layer系统方法,将其替换为显示OpenGl ES的图层
+ (Class)layerClass
{
return [CAEAGLLayer class];
}
在layoutSubviews里分了5个阶段内容,前面4个阶段基本是固定死的,不会有啥变动
主要是第5个阶段
- (void)layoutSubviews
{
//1.设置图层
[self setUpLayer];
//2.设置上下文
[self setUpContext];
//3.设置渲染缓冲区
[self setUpRenderBuffer];
//4.设置帧缓冲区
[self setUpFrameBuffer];
//5.开始渲染
[self render];
}
1.设置图层
- (void)setUpLayer
{
//将CALayer变成CAEAGLLayer
self.myLayer = (CAEAGLLayer *)self.layer;
//设置View的比例系数
[self setContentScaleFactor:[UIScreen mainScreen].scale];
//设置CAEAGLLayer的颜色空间
self.myLayer.drawableProperties = @{kEAGLDrawablePropertyColorFormat:kEAGLColorFormatRGBA8,kEAGLDrawablePropertyRetainedBacking:@(false)};
}
2.设置上下文
- (void)setUpContext
{
//创建一个ES3.0的上下文,并将其设置为当前上下文
self.myContext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
[EAGLContext setCurrentContext:self.myContext];
}
3.设置渲染缓冲区
- (void)setUpRenderBuffer
{
//申请渲染缓存区标识
glGenRenderbuffers(1, &_renderBuffer);
//绑定渲染缓存区
glBindRenderbuffer(GL_RENDERBUFFER, self.renderBuffer);
//将标识符绑定到GL_RENDERBUFFER
[self.myContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.myLayer];
}
4.设置帧缓存区
- (void)setUpFrameBuffer
{
//申请帧缓冲区标识
glGenFramebuffers(1, &_frameBuffer);
//绑定帧缓冲区
glBindFramebuffer(GL_FRAMEBUFFER, self.frameBuffer);
//将_renderBuffer 装配到GL_COLOR_ATTACHMENT0 附着点上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.renderBuffer);
}
5.接下来就是在render中的代码
第一步:
//1.设置背景色绿色(RGBA)
glClearColor(0, 1, 0, 1);
//2.清理颜色缓存区
glClear(GL_COLOR_BUFFER_BIT);
//3.创建视口
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);
第二步.配置顶点和纹理坐标
GLfloat vertice[] = {
//下面的三角形
-0.55,0.5,0, 1,0, //左上角
-0.55,-0.5,0, 0,0, //左下角
0.45,-0.5,0, 1,0, //右下角
//上面的三角形
-0.45,0.5,0, 1,0, //左上角
0.55,0.5,0, 1,1, //右上角
0.55,-0.5,0, 1,1 //右下角
};
第三步:编写着色器,新建两个empty文件,shader.vsh和shader.fsh,文件名和后缀名按你喜好随便写.
1.shader.vsh中的代码:注意本来这个文件中时不能有注释的,否则编译该字符串时容易失败,只是为了讲解写的注释
//1.申明position 和 textureCoord 用来接收外部传过来的顶点坐标和纹理坐标
attribute vec3 position;
attribute vec2 textureCoord;
//2.将纹理坐标从顶点着色器传入到片元着色器的通道
varying lowp vec2 varingPosition;
void main()
{
varingPosition = textureCoord;
//3. gl_Position为OpenGL ES内部已经创建的变量,顶点数据一定要给它,否则无效
gl_Position = position;
}
2.shader.fsh的代码:注意一点,片元主色器中未申明的变量不会配给精度,需要自己设置精度,否则运算会出错.而顶点着色器会配默认精度
//由于片元着色器的没有申明精度的变量不会默认精度,如果没有精度,会出错,全局申明float类型为高精度
precision highp float;
//和顶点着色器中的通道必须一样
varying lowp vec2 varingPosition;
//纹理采样器
uniform lowp sampler2D colorMap;
void main()
{
/*gl_FragColor为内建变量,颜色赋值给该变量,OpenGL ES才能获取
texture2D为内建函数,根据采样器和顶点获取纹理数据
*/
gl_FragColor = texture2D(colorMap,varingPosition);
}
3.将两个shader文件字符串加载运行到program中
- (GLuint)loadProgramVerFile:(NSString *)verFile fragFile:(NSString *)fragFile
{
//声明顶点,片元着色器变量
GLuint vershader,fragmentShader;
//创建program
GLint program = glCreateProgram();
//将两个着色器中的字符串编译成程序
[self compileShader:&vershader file:verFile type:GL_VERTEX_SHADER];
[self compileShader:&fragmentShader file:fragFile type:GL_FRAGMENT_SHADER];
//将两个着色器程序附着在program中,后面用program对着色器进行操作
glAttachShader(program, vershader);
glAttachShader(program, fragmentShader);
//删除已经附着完的shader
glDeleteShader(vershader);
glDeleteShader(fragmentShader);
return program;
}
- (void)compileShader:(GLuint *)shader file:(NSString *)file type:(GLint)type
{
//获取字符串
NSString *str = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
//转化为C字符串
const GLchar *source = (GLchar *)[str UTF8String];
//创建着色器
*shader = glCreateShader(type);
//将文件中的字符串载入到shader中
glShaderSource(*shader, 1, &source, NULL);
//编译shader生成程序
glCompileShader(*shader);
}
第四部:render方法中
//2.编译链接两个着色器
NSString *verFile = [[NSBundle mainBundle]pathForResource:@"shader.vsh" ofType:nil];
NSString *fragFile = [[NSBundle mainBundle]pathForResource:@"shader.fsh" ofType:nil];
//将两个着色器文件传入链接成program
self.myProgram = [self loadProgramVerFile:verFile fragFile:fragFile];
//链接program(有可能失败,因为你的shader文件代码可能写错)
glLinkProgram(self.myProgram);
//获取连接状态
GLint linkStatus;
glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkStatus);
if (linkStatus == GL_FALSE) {
GLchar message[1000];
//获取连接错误信息
glGetProgramInfoLog(self.myProgram, sizeof(message), NULL, message);
NSString *log = [[NSString alloc]initWithUTF8String:message];
NSLog(@"链接失败:%@",log);
return;
}
//开始使用program
glUseProgram(self.myProgram);
第五部,开始将顶点信息传入到着色器中,
//将顶点数据从内存复制到现存
GLuint buffer;
glGenBuffers(1, &buffer);
glBindBuffer(GL_ARRAY_BUFFER, buffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertice), vertice, GL_STATIC_DRAW);
//获取顶点着色器中的 顶点属性通道 attribute vec3 position;传入的变量名和shader文件的名称一样
GLint position = glGetAttribLocation(self.myProgram, "position");
//开启position属性传输通道,默认是关闭的
glEnableVertexAttribArray(position);
//将顶点数据传入到shader中
glVertexAttribPointer(position, 6, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, NULL);
//纹理坐标的传入和顶点一样
GLint textureCoord = glGetAttribLocation(self.myProgram, "textureCoord");
glEnableVertexAttribArray(textureCoord);
glVertexAttribPointer(position, 6, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (GLfloat *)NULL+3);
//设置纹理内容
[self setUpTexture];
//获取着色器
GLint colorMap = glGetUniformLocation(self.myProgram, "colorMap");
//第一个纹理,在设置纹理时也设置为0
glUniform1f(colorMap, 0);
//开始绘制图像 1.三角形 2.从0开始 3.6个顶点
glDrawArrays(GL_TRIANGLES, 0, 6);
//将绘制好的内容呈现到屏幕上来
[self.myContext presentRenderbuffer:GL_RENDERBUFFER];
第五步:在家纹理
- (void)setUpTexture
{
//图片解压缩,变成位图,具体可以网上搜,本文不讲解
CGImageRef imageRef = [UIImage imageNamed:@"girl"].CGImage;
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
GLubyte *datas = calloc(width*height*4, sizeof(GLubyte));
//将图片转化为位图
CGContextRef contextRef = CGBitmapContextCreate(datas, width, height, 8, width*4, CGImageGetColorSpace(imageRef), kCGImageAlphaPremultipliedLast);
CGRect rect = CGRectMake(0, 0, width, height);
CGContextDrawImage(contextRef, rect, imageRef);
//绑定纹理,将其设置成第0个纹理,与glUniform1f(colorMap, 0);对应
glBindTexture(GL_TEXTURE0, 0);
float w = width;
float h = height;
//设置纹理格式
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//加载纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, datas);
free(contextRef);
free(datas);
}