2.OpenGL的渲染结构与iOS离屏渲染

一、渲染结构

OpenGL的渲染结构

OpenGL 渲染架构

Client:是指常见的iOS代码和OpenGL API方法,这部分是在CPU中运行
Server:是指OpenGL底层的渲染等处理,是运行在GPU中的
attribute属性, 直接用于顶点着色器, 不能直接传递到片元着色器, 通过GLSL代码间接传递到片元着色器。
Uniform 值(只是一个通道, 可传矩阵,可传单个值), 可直接传递到片元着色器和顶点着色器。
Vertex Shader: 顶点着色器 (可编程)
**Primitive Assembly **(图元装配 也叫光栅化): 顶点着色器 -> 光栅化(过程开发者是不可干预的) -> Fragment Shader, 只能编程Vertex Shader 和 Fragment Shader。
Fragment Shader:片元着色器(可编程)
TexTure Data:纹理数据 -> 顶点着色器,片元着色器
渲染一个图形: 比如渲染一个正方形 如果是渲染为蓝色 那么可以直接使用颜色去处理, 不用使用纹理, 要填充图片的话, 才需要使用到纹理。

OpenGL 为什么能够跨平台有一个很重要的原因是因为OpenGL是没有自己的窗口系统的, 每个平台需要使用OpenGL需要配合OpenGL集成一个自己的窗口系统。

iOS下的渲染架构

iOS下的渲染架构
iOS下的渲染架构

Core Animation: 本质上可以理解为一个复合引擎,主要职责包含:渲染,构建和实现动画。

二、离屏渲染

老生常谈的离屏渲染,无非就是cornerRadius,masksToBounds = YES, 我们来探究一下引起离屏渲染的真正因素。

先来一段简单的代码。


    UIButton *btn1 = [[UIButton alloc] initWithFrame:CGRectMake(100, 50, 100, 100)];
    btn1.layer.cornerRadius = 50;
    [btn1 setImage:[UIImage imageNamed:@"Popup_new1"] forState:UIControlStateNormal];
    btn1.clipsToBounds = YES;
    [self.view addSubview:btn1];

    UIButton *btn2 = [[UIButton alloc] initWithFrame:CGRectMake(100, 200, 100, 100)];
    btn2.layer.cornerRadius = 50;
    [btn2 setBackgroundColor:UIColor.redColor];
    btn2.clipsToBounds = YES;
    [self.view addSubview:btn2];

    UIImageView *imgView1 = [[UIImageView alloc] initWithFrame:CGRectMake(100, 400, 100, 100)];
    imgView1.image = [UIImage imageNamed: @"Popup_new1"];
    imgView1.layer.cornerRadius = 50;
    imgView1.layer.masksToBounds = YES;
    imgView1.backgroundColor = [UIColor redColor];
    [self.view addSubview:imgView1];

    UIImageView *imgView3 = [[UIImageView alloc] initWithFrame:CGRectMake(100, 550 , 100, 100)];
    imgView3.image = [UIImage imageNamed: @"Popup_new1"];
    imgView3.layer.cornerRadius = 50;
    imgView3.layer.masksToBounds = YES;
    [self.view addSubview:imgView3];

运行结果


image.png

以上现象说明了什么。

设置了cornerRadius和masksToBounds = YES 不一定会造成离屏渲染.

图片正常渲染,没有离屏的时候 流程是 APP -> 帧缓存区 -> 显示到界面上
离屏渲染: APP -> 离屏缓存区 -> 帧缓存区 -> 显示

为什么单独只设置了背景色或者图片的时候不会离屏,一般情况下 GPU渲染都会遵循画家算法,由远至近,一层一层的渲染,渲染完一个张后帧缓存区的数据会清空。

画家算法渲染图片

我们在切圆角的时候, 此时是只有一个图层,渲染好这张图片直接切了显示到界面上就可以了。
如果有多张图层的时候,先渲染好的图层需要等待其他的图层都渲染完成 ,如果不另外保存起来,那么之前的数据都清空了,那么久没有办法去处理圆角了。这个就叫离屏渲染,需要多开辟一个离屏缓存区,用于存放渲染后的数据再统一处理。

比较UIButton 和UIImageView
iOS的button setImage 是添加了一个imgaeView的,所以就有两个图层,所以在切button的圆角时 会造成离屏渲染,而如果只切button中的imageView圆角,是不会的,大家可以自己试一试。

cornerRadius+masksToBounds 只有在设置了content且背景不是透明时,才会出现离屏渲染。

三、iOS下常用圆角处理方案

方案一

imageView.clipsToBounds = YES;
imageVieiw.layer.cornerRadius = 4.0;

方案二

- (UIImage *)roundedCornerImageWithCornerRadius: (CGFloat)cornerRadius {
    CGFloat w = self.size.width;
    CGFloat h = self.size.height;
    CGFloat scale = [UIScreen mainScreen].scale;

    if (cornerRadius < 0) {
        cornerRadius = 0;
    } else if (cornerRadius > MIN(w, h)) {
        cornerRadius = MIN(w, h) / 2.;
    }
    UIImage *image = nil;
    CGRect imageFrame = CGRectMake(0.0, 0.0, w, h);
    UIGraphicsBeginImageContextWithOptions(self.size, NO, scale);
    [[UIBezierPath bezierPathWithRoundedRect:imageFrame cornerRadius:cornerRadius] addClip];
    [self drawInRect:imageFrame];
    image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

方案三

- (void)addMaskToBounds: (CGRect)maskBounds cornerRadius:(CGFloat)cornerRadius {
    CGFloat w = maskBounds.size.width;
    CGFloat h = maskBounds.size.height;
    CGSize size = maskBounds.size;
    CGFloat scale = [UIScreen mainScreen].scale;
    CGRect imageRect = CGRectMake(0, 0, w, h);

    if (cornerRadius < 0) {
        cornerRadius = 0;
    } else if (cornerRadius > MIN(w, h)) {
        cornerRadius = MIN(w, h) / 2.;
    }
    UIImage *image = nil;
    UIGraphicsBeginImageContextWithOptions(self.size, NO, scale);
    [[UIBezierPath bezierPathWithRoundedRect:imageRect cornerRadius:cornerRadius] addClip];
    [image drawInRect:imageRect];
    self.roundImage = UIGraphicsGetImageFromCurrentImageContext();
    self.image = self.roundImage;
    UIGraphicsEndImageContext();
}

常见的触发离屏渲染的几种情况

  1. 使⽤了 mask 的 layer (layer.mask)
  2. 需要进⾏裁剪的 layer (layer.masksToBounds / view.clipsToBounds)
  3. 设置了组透明度为 YES,并且透明度不为 1 的 layer (layer.allowsGroupOpacity/layer.opacity)
  4. 添加了投影的 layer (layer.shadow*)
  5. 采⽤了光栅化的 layer (layer.shouldRasterize)
  6. 绘制了⽂字的 layer (UILabel, CATextLayer, Core Text 等)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351