GLKViewController关于更新方法的补充
GLKViewController补充点, 如果被子类化且实现了updata, 则该方法每60FPS的频率调用
image.png
案例 -- OpenGL ES GLKit加载立方体图形
思路导图
image.png
- 这次我们的VC并没有继承什么的, 只是声明了一个GLKView属性, 在这个图层上面实现我们要完成的效果
2.这里的数据是由一个结构体提供的, 结构体里面分别为
2.GLKVector3 -> 三维向量 -> 是一个联合体(共用体)
3. OpenGL ES 相关配置
//1.创建context EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3]; //设置当前context [EAGLContext setCurrentContext:context]; //2.创建GLKView并设置代理 CGRect frame = CGRectMake(0, 100, self.view.frame.size.width, self.view.frame.size.width); self.glkView = [[GLKView alloc] initWithFrame:frame context:context]; self.glkView.backgroundColor = [UIColor clearColor]; self.glkView.delegate = self; //3.使用深度缓存 self.glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24; //默认是(0, 1),这里用于翻转 z 轴,使正方形朝屏幕外 //glDepthRangef(1, 0); //4.将GLKView 添加self.view 上 [self.view addSubview:self.glkView]; //5.获取纹理图片 NSString *imagePath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"kunkun.jpg"]; UIImage *image = [UIImage imageWithContentsOfFile:imagePath]; //6.设置纹理参数 NSDictionary *options = @{GLKTextureLoaderOriginBottomLeft : @(YES)}; GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithCGImage:[image CGImage] options:options error:NULL]; //7.使用baseEffect self.baseEffect = [[GLKBaseEffect alloc] init]; self.baseEffect.texture2d0.name = textureInfo.name; self.baseEffect.texture2d0.target = textureInfo.target; }
4. 顶点设置
{ /* 解释一下: 这里我们不复用顶点,使用每 3 个点画一个三角形的方式,需要 12 个三角形,则需要 36 个顶点 以下的数据用来绘制以(0,0,0)为中心,边长为 1 的立方体 */ //8. 开辟顶点数据空间(数据结构SenceVertex 大小 * 顶点个数kCoordCount) self.vertices = malloc(sizeof(CCVertex) * kCoordCount); // 前面 self.vertices[0] = (CCVertex){{-0.5, 0.5, 0.5}, {0, 1}}; self.vertices[1] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 0}}; self.vertices[2] = (CCVertex){{0.5, 0.5, 0.5}, {1, 1}}; self.vertices[3] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 0}}; self.vertices[4] = (CCVertex){{0.5, 0.5, 0.5}, {1, 1}}; self.vertices[5] = (CCVertex){{0.5, -0.5, 0.5}, {1, 0}}; // 上面 self.vertices[6] = (CCVertex){{0.5, 0.5, 0.5}, {1, 1}}; self.vertices[7] = (CCVertex){{-0.5, 0.5, 0.5}, {0, 1}}; self.vertices[8] = (CCVertex){{0.5, 0.5, -0.5}, {1, 0}}; self.vertices[9] = (CCVertex){{-0.5, 0.5, 0.5}, {0, 1}}; self.vertices[10] = (CCVertex){{0.5, 0.5, -0.5}, {1, 0}}; self.vertices[11] = (CCVertex){{-0.5, 0.5, -0.5}, {0, 0}}; // 下面 self.vertices[12] = (CCVertex){{0.5, -0.5, 0.5}, {1, 1}}; self.vertices[13] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 1}}; self.vertices[14] = (CCVertex){{0.5, -0.5, -0.5}, {1, 0}}; self.vertices[15] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 1}}; self.vertices[16] = (CCVertex){{0.5, -0.5, -0.5}, {1, 0}}; self.vertices[17] = (CCVertex){{-0.5, -0.5, -0.5}, {0, 0}}; // 左面 self.vertices[18] = (CCVertex){{-0.5, 0.5, 0.5}, {1, 1}}; self.vertices[19] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 1}}; self.vertices[20] = (CCVertex){{-0.5, 0.5, -0.5}, {1, 0}}; self.vertices[21] = (CCVertex){{-0.5, -0.5, 0.5}, {0, 1}}; self.vertices[22] = (CCVertex){{-0.5, 0.5, -0.5}, {1, 0}}; self.vertices[23] = (CCVertex){{-0.5, -0.5, -0.5}, {0, 0}}; // 右面 self.vertices[24] = (CCVertex){{0.5, 0.5, 0.5}, {1, 1}}; self.vertices[25] = (CCVertex){{0.5, -0.5, 0.5}, {0, 1}}; self.vertices[26] = (CCVertex){{0.5, 0.5, -0.5}, {1, 0}}; self.vertices[27] = (CCVertex){{0.5, -0.5, 0.5}, {0, 1}}; self.vertices[28] = (CCVertex){{0.5, 0.5, -0.5}, {1, 0}}; self.vertices[29] = (CCVertex){{0.5, -0.5, -0.5}, {0, 0}}; // 后面 self.vertices[30] = (CCVertex){{-0.5, 0.5, -0.5}, {0, 1}}; self.vertices[31] = (CCVertex){{-0.5, -0.5, -0.5}, {0, 0}}; self.vertices[32] = (CCVertex){{0.5, 0.5, -0.5}, {1, 1}}; self.vertices[33] = (CCVertex){{-0.5, -0.5, -0.5}, {0, 0}}; self.vertices[34] = (CCVertex){{0.5, 0.5, -0.5}, {1, 1}}; self.vertices[35] = (CCVertex){{0.5, -0.5, -0.5}, {1, 0}}; //顶点数组 VAO //开辟缓存区 VBO glGenBuffers(1, &_vertexBuffer); glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer); GLsizeiptr bufferSizeBytes = sizeof(CCVertex) * kCoordCount; glBufferData(GL_ARRAY_BUFFER, bufferSizeBytes, self.vertices, GL_STATIC_DRAW); //顶点数据 glEnableVertexAttribArray(GLKVertexAttribPosition); glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(CCVertex), NULL + offsetof(CCVertex, positionCoord)); //纹理数据 glEnableVertexAttribArray(GLKVertexAttribTexCoord0); glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(CCVertex), NULL + offsetof(CCVertex, textureCoord)); }
5. 添加CADisplayLink
-(void) addCADisplayLink{ //CADisplayLink 类似定时器,提供一个周期性调用.属于QuartzCore.framework中. //具体可以参考该博客 https://www.cnblogs.com/panyangjun/p/4421904.html self.angle = 0; self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)]; [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; } - (void)update { //1.计算旋转度数 self.angle = (self.angle + 5) % 360; //2.修改baseEffect.transform.modelviewMatrix self.baseEffect.transform.modelviewMatrix = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(self.angle), 0.3, 1, -0.7); //3.重新渲染 [self.glkView display]; }
5. 释放C语法创建的对象
- (void)dealloc { if ([EAGLContext currentContext] == self.glkView.context) { [EAGLContext setCurrentContext:nil]; } if (_vertices) { free(_vertices); _vertices = nil; } if (_vertexBuffer) { glDeleteBuffers(1, &_vertexBuffer); _vertexBuffer = 0; } //displayLink 失效 [self.displayLink invalidate]; }
案例 -- 使用CoreAnimation(核心动画)加载立方体图形
1. 属性声明
@interface CCViewController () @property (weak, nonatomic) IBOutlet UIView *containerView; @property (strong, nonatomic) IBOutlet UIView *view0; @property (strong, nonatomic) IBOutlet UIView *view1; @property (strong, nonatomic) IBOutlet UIView *view2; @property (strong, nonatomic) IBOutlet UIView *view3; @property (strong, nonatomic) IBOutlet UIView *view4; @property (strong, nonatomic) IBOutlet UIView *view5; @property (nonatomic, strong) NSArray *faces; @property (nonatomic, strong) CADisplayLink *displayLink; @property (nonatomic, assign) NSInteger angle; @end
2. 大概思路
[super viewDidLoad]; //添加面 [self addCFaces]; //添加CADisplayLink [self addCADisplayLink]; }
3. 添加视图
-(void)addCFaces { self.faces = @[_view0,_view1,_view2,_view3,_view4,_view5]; //父View的layer图层 CATransform3D perspective = CATransform3DIdentity; perspective.m34 = -1.0 / 500.0; perspective = CATransform3DRotate(perspective, -M_PI_4, 1, 0, 0); perspective = CATransform3DRotate(perspective, -M_PI_4, 0, 1, 0); self.containerView.layer.sublayerTransform = perspective; //add cube face 1 CATransform3D transform = CATransform3DMakeTranslation(0, 0, 100); [self addFace:0 withTransform:transform]; //add cube face 2 transform = CATransform3DMakeTranslation(100, 0, 0); transform = CATransform3DRotate(transform, M_PI_2, 0, 1, 0); [self addFace:1 withTransform:transform]; //add cube face 3 transform = CATransform3DMakeTranslation(0, -100, 0); transform = CATransform3DRotate(transform, M_PI_2, 1, 0, 0); [self addFace:2 withTransform:transform]; //add cube face 4 transform = CATransform3DMakeTranslation(0, 100, 0); transform = CATransform3DRotate(transform, -M_PI_2, 1, 0, 0); [self addFace:3 withTransform:transform]; //add cube face 5 transform = CATransform3DMakeTranslation(-100, 0, 0); transform = CATransform3DRotate(transform, -M_PI_2, 0, 1, 0); [self addFace:4 withTransform:transform]; //add cube face 6 transform = CATransform3DMakeTranslation(0, 0, -100); transform = CATransform3DRotate(transform, M_PI, 0, 1, 0); [self addFace:5 withTransform:transform]; } - (void)addFace:(NSInteger)index withTransform:(CATransform3D)transform { //获取face视图并将其添加到容器中 UIView *face = self.faces[index]; [self.containerView addSubview:face]; //将face视图放在容器的中心 CGSize containerSize = self.containerView.bounds.size; face.center = CGPointMake(containerSize.width / 2.0, containerSize.height / 2.0); //添加transform face.layer.transform = transform; }
4. 定时旋转一个角度
-(void) addCADisplayLink{ self.angle = 0; self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(update)]; [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes]; } - (void)update { //1.计算旋转度数 self.angle = (self.angle + 5) % 360; float deg = self.angle * (M_PI / 180); CATransform3D temp = CATransform3DIdentity; temp = CATransform3DRotate(temp, deg, 0.3, 1, 0.7); self.containerView.layer.sublayerTransform = temp; }
GLSL初探
前言: 有关使用GLSL自己去编写顶点着色器和片元着色器, 因为没有编译器支持提示, 所以很容易写错, 本篇文章介绍自定义编写的代码, 以及相应的使用和语法知识.
在自定义的着色器里面尽量不加中文注释, 可能会导致编译失败, 以下根据两端代码来进行分析:
顶点着色器
//顶点坐标 attribute vec4 position; //纹理坐标 attribute vec2 textCoordinate; //纹理坐标 varying lowp vec2 varyTextCoord; void main() { //通过varying 修饰varyTextCoord, 将纹理坐标传递到片源着色器 varyTextCoord = textCoordinate; //gl_Position作为顶点着色器的内置函数, 要对他进行赋值, 顶点着色器才会起作用. gl_Position = position; }
varyTextCoord 用varying 修饰, 这种写法作为顶点跟片源着色器之间传值使用的, 片源着色器要想接收到该值, 在片元着色器中声明的属性 必须跟在顶点中声明的完全一样.
片元着色器
//纹理坐标 varying lowp vec2 varyTextCoord; //纹理采样器(获取对应的纹理ID) uniform sampler2D colorMap; void main() { //texture2D(纹理采样器, 纹理坐标), 获取对应坐标纹素 //gl_FragColor GLSL 内建变量(赋值像素点颜色值) //纹理颜色添加对应像素点上 //gl_FragColor 内建变量. GLSL语言已经提前定义好的变量, 有相应特殊函数. //内建函数. GLSL语言提前封装好的相关函数. //读取纹素, vec4 texture2D(纹理colorMap, 纹理坐标varyTextCoord); rgba lowp vec4 temp = texture2D(colorMap, varyTextCoord); gl_FragColor = temp; }
顶点/片元常用后缀
最终我们在GLSL代码中需要的是一个GLuint myPrograme的对象.
有关着色器文件的命名, 顶点着色器尽量用.vsh, 片元着色器尽量用.fsh.
- vsh/fsh -> program -> GPU
- vsh -> verterx shader
- fsh -> fragment shader
- glsl -> shader -> 自己根据代码去区分
- 顶点的话: gl_Position
- 片元 -> gl_FragColor
以下增加vsh/fsh -> program的代码
加载shader/编译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); }
program的使用 以及编译
//3.加载shader self.myPrograme = [self loadShaders:vertFile Withfrag:fragFile]; //4.链接 glLinkProgram(self.myPrograme); GLint linkStatus; //获取链接状态 glGetProgramiv(self.myPrograme, GL_LINK_STATUS, &linkStatus); if (linkStatus == GL_FALSE) { GLchar message[512]; glGetProgramInfoLog(self.myPrograme, sizeof(message), 0, &message[0]); NSString *messageString = [NSString stringWithUTF8String:message]; NSLog(@"Program Link Error:%@",messageString); return; } NSLog(@"Program Link Success!"); //5.使用program glUseProgram(self.myPrograme);
最后请看怎么访问着色器中的属性, 因为着色器最后合并生成了programe, 所以具体查看一下代码
//8.将顶点数据通过myPrograme中的传递到顶点着色程序的position //1.glGetAttribLocation,用来获取vertex attribute的入口的. //2.告诉OpenGL ES,通过glEnableVertexAttribArray, //3.最后数据是通过glVertexAttribPointer传递过去的。 //(1)注意:第二参数字符串必须和shaderv.vsh中的输入变量:position保持一致 GLuint position = glGetAttribLocation(self.myPrograme, "position"); //(2).设置合适的格式从buffer里面读取数据 glEnableVertexAttribArray(position); //(3).设置读取方式 //参数1:index,顶点数据的索引 //参数2:size,每个顶点属性的组件数量,1,2,3,或者4.默认初始值是4. //参数3:type,数据中的每个组件的类型,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默认初始值为GL_FLOAT //参数4:normalized,固定点数据值是否应该归一化,或者直接转换为固定值。(GL_FALSE) //参数5:stride,连续顶点属性之间的偏移量,默认为0; //参数6:指定一个指针,指向数组中的第一个顶点属性的第一个组件。默认为0 glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL); //9.----处理纹理数据------- //(1).glGetAttribLocation,用来获取vertex attribute的入口的. //注意:第二参数字符串必须和shaderv.vsh中的输入变量:textCoordinate保持一致 GLuint textCoor = glGetAttribLocation(self.myPrograme, "textCoordinate"); //(2).设置合适的格式从buffer里面读取数据 glEnableVertexAttribArray(textCoor); //(3).设置读取方式 //参数1:index,顶点数据的索引 //参数2:size,每个顶点属性的组件数量,1,2,3,或者4.默认初始值是4. //参数3:type,数据中的每个组件的类型,常用的有GL_FLOAT,GL_BYTE,GL_SHORT。默认初始值为GL_FLOAT //参数4:normalized,固定点数据值是否应该归一化,或者直接转换为固定值。(GL_FALSE) //参数5:stride,连续顶点属性之间的偏移量,默认为0; //参数6:指定一个指针,指向数组中的第一个顶点属性的第一个组件。默认为0 glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*5, (float *)NULL + 3);
OpenGL ES 错误处理
image.png