GLKit详解 (下) 与 OpenGL GLSL 初探 (3) (11)

简介: GLKViewController关于更新方法的补充

GLKViewController关于更新方法的补充


GLKViewController补充点, 如果被子类化且实现了updata, 则该方法每60FPS的频率调用


微信图片_20220508234119.jpg

image.png


案例 -- OpenGL ES GLKit加载立方体图形


思路导图


微信图片_20220508234124.jpg

image.png


  1. 这次我们的VC并没有继承什么的, 只是声明了一个GLKView属性, 在这个图层上面实现我们要完成的效果

屏幕截图 2022-05-08 235242.png


       2.这里的数据是由一个结构体提供的, 结构体里面分别为

屏幕截图 2022-05-08 235313.png


2.GLKVector3 -> 三维向量 -> 是一个联合体(共用体)

屏幕截图 2022-05-08 235458.png


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);


OpenGL ES 错误处理


微信图片_20220508234145.jpg

image.png




目录
相关文章
|
索引
OpenGL ES 案例07:GLSL使用索引绘图 + 纹理颜色混合
OpenGL ES 案例07:GLSL使用索引绘图 + 纹理颜色混合
336 0
OpenGL ES 案例07:GLSL使用索引绘图 + 纹理颜色混合
|
存储 缓存 API
OpenGL ES 案例04:GLSL加载图片
OpenGL ES 案例04:GLSL加载图片
274 0
OpenGL ES 案例04:GLSL加载图片
四、 OpenGL ES GLSL图片倒置的翻转解决方案(6种)
OpenGL ES GLSL图片倒置的翻转解决方案(6种)
705 0
四、 OpenGL ES GLSL图片倒置的翻转解决方案(6种)
|
存储 缓存 API
三、OpenGL ES GLSL语言 & 自定义着色器常用API
OpenGL ES GLSL语言 & 自定义着色器常用API
314 0
三、OpenGL ES GLSL语言 & 自定义着色器常用API
|
存储 缓存 并行计算
使用计算着色器(Compute Shader)模拟粒子效果【OpenGL】【GLSL】
使用计算着色器(Compute Shader)模拟粒子效果【OpenGL】【GLSL】
900 0
使用计算着色器(Compute Shader)模拟粒子效果【OpenGL】【GLSL】
|
6月前
|
XML 小程序 Java
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
【Android App】三维投影OpenGL ES的讲解及着色器实现(附源码和演示 超详细)
126 0
|
缓存 C++
Opengl ES之FBO
Opengl ES连载系列
153 0
|
存储 编解码 算法
Opengl ES之LUT滤镜(上)
Opengl ES之连载系列
463 0
|
数据安全/隐私保护 开发者
OpenGL ES 多目标渲染(MRT)
Opengl ES连载系列
324 0
|
数据安全/隐私保护 索引
Opengl ES之纹理数组
Opengl ES连载系列
260 0