GLKViewController关于更新方法的补充
GLKViewController补充点, 如果被子类化且实现了updata, 则该方法每60FPS的频率调用
案例 -- OpenGL ES GLKit加载立方体图形
思路导图
- 这次我们的VC并没有继承什么的, 只是声明了一个GLKView属性, 在这个图层上面实现我们要完成的效果
-
- 这里的数据是由一个结构体提供的, 结构体里面分别为
-
-
- 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
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);