003-OpenGL ES之纹理

什么是纹理

纹理是一个用来保存图像的颜色色素值得OpenGL ES缓存, 纹理的作用是为了使我们渲染的几何图像更加的逼真, 通常来说就是使用一张图片贴在我们需要渲染的几何图形上.

当一个图像初始化一个纹理缓存之后, 在这个图像中的每个像素就变成了纹素(texel), 纹素也是用来保存颜色数据的.

纹理坐标

纹理坐标是一个命名为S和T的2D轴, 在一个纹理坐标中, 无论纹素有多少个, 在纹理的尺寸永远是S轴上从0.0到1 .0, 在T轴上从0.0到1.0. 纹理坐标是一个相对坐标的概念.

例如, 从一个1像素高, 64像素宽的图像初始化来的纹理会沿着T轴有一个纹素, 沿着S轴有64个纹素.

屏幕快照 2017-07-04 上午10.03.31.png

转换几何形状数据为帧缓存中的颜色像素的渲染步骤叫做点阵化(rasterizing), 每个颜色像素叫做(fragment). 当OpenGL ES没有使用纹理时, GPU会根据包含该片元的对象的顶点的颜色来计算每个片元的颜色, 当设置了纹理之后, GPU会根据当前绑定的纹理缓存中的纹素来计算每个片元的颜色.

其实纹理就是要把一张图片贴在我们绘制的几何图像上, 我们要制定贴在哪个位置, 这时就需要用到OpenGL ES坐标与纹理坐标之间的转化.

我的理解是纹理坐标是一个相对的坐标, 是相对与OpenGL ES的坐标

屏幕快照 2017-07-05 下午10.46.15.png

OpenGL ES之绘制三角形(一)一节中, 我们绘制三角形需要指定三角形的顶点位置, 同样, 我们绘制纹理, 也需要指定纹素的位置

typedef struct {
    GLKVector3  positionCoords;
    GLKVector2  textureCoords;
}
SceneVertex;

在原先的结构体中新增一个类型GLKVector2, 用于存储纹理的坐标数据

static const SceneVertex vertices[] =
{
    {{-0.5f, -0.5f, 0.0f}, {0.0f, 0.0f}}, 
    {{ 0.5f, -0.5f, 0.0f}, {1.0f, 0.0f}}, 
    {{-0.5f,  0.5f, 0.0f}, {0.0f, 1.0f}},
};

顶点坐标{-0.5f, -0.5f, 0.0f}映射到纹理坐标就变为{0.0f, 0.0f}, 顶点左下角的坐标对应纹理的左下角, 然后依次向S轴和T轴延伸到1.

接下来就是开始渲染纹理

   // Setup texture
   CGImageRef imageRef =  [[UIImage imageNamed:@"leaves.gif"] CGImage];
      
   GLKTextureInfo *textureInfo = [GLKTextureLoader 
      textureWithCGImage:imageRef 
      options:nil 
      error:NULL];
   
   self.baseEffect.texture2d0.name = textureInfo.name;
   self.baseEffect.texture2d0.target = textureInfo.target;

textureWithCGImage方法, 把CGImageRef转成GLKTextureInfo类型的纹理缓存, GLKTextureInfo类保存了纹理缓存的信息, target属性指定被配置的纹理缓存的类型, name是纹理缓存的一个标识符.

至此纹理就被成功的渲染出来了.

混合

我们都知道三原色是红, 黄, 蓝, 通过这三种颜色可以调配出其他的颜色。
混合就是把两种颜色通过某种特定的方式混在一起。实现特殊的效果。就是透过一个物体去看另外一个物体的模式, 如果遮挡物是透明的, Alpha为0, 则我们可以透过遮挡物去看到被遮挡的物体, 就像玻璃一样, 你可以透过玻璃去看到玻璃里面的东西; 如果遮挡物不是透明的, Alpha为1, 你就看不到被遮挡的物体, 就像你不能透过一堵墙去看墙另一侧的物体.

要使用OpenGL的混合功能,只需要调用:glEnable(GL_BLEND)即可。
要关闭OpenGL的混合功能,只需要调用:glDisable(GL_BLEND)即可。
注意:只有在RGBA模式下,才可以使用混合功能,颜色索引模式下是无法使用混合功能的。

混合需要把原来的颜色和将要画上去的颜色找出来,经过某种方式处理后得到一种新的颜色。这里把将要画上去的颜色称为“源颜色”,把原来的颜色称为“目标颜色”。

OpenGL 会把源颜色和目标颜色各自取出,并乘以一个系数(源颜色乘以的系数称为“源因子”,目标颜色乘以的系数称为“目标因子”),然后进项数学运算, 这样就得到了新的颜色。

源因子和目标因子是可以通过glBlendFunc函数来进行设置的。glBlendFunc有两个参数,前者表示源因子,后者表示目标因子。这两个参数可以是多种值,下面介绍比较常用的几种。

GL_ZERO: 表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
GL_ONE: 表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。

举例来说:

  1. 如果设置了glBlendFunc(GL_ONE, GL_ZERO);,则表示完全使用源颜色,完全不使用目标颜色,因此画面效果和不使用混合的时候一致(当然效率可能会低一点点)。如果没有设置源因子和目标因子,则默认情况就是这样的设置。
  2. 如果设置了glBlendFunc(GL_ZERO, GL_ONE);,则表示完全不使用源颜色,因此无论你想画什么,最后都不会被画上去了。(但这并不是说这样设置就没有用,有些时候可能有特殊用途)
  3. 如 果设置了glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减 小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
  4. 如果设置了glBlendFunc(GL_ONE, GL_ONE);,则表示完全使用源颜色和目标颜色,最终的颜色实际上就是两种颜色的简单相加。例如红色(1, 0, 0)和绿色(0, 1, 0)相加得到(1, 1, 0),结果为黄色。

注意:
所 谓源颜色和目标颜色,是跟绘制的顺序有关的。假如先绘制了一个红色的物体,再在其上绘制绿色的物体。则绿色是源颜色,红色是目标颜色。如果顺序反过来,则 红色就是源颜色,绿色才是目标颜色。在绘制时,应该注意顺序,使得绘制的源颜色与设置的源因子对应,目标颜色与设置的目标因子对应。不要被混乱的顺序搞晕 了。

在上面的代码中我们已经完成了单个纹理的实现, 现在我们再添加一个纹理, 来实现多重混合

设置坐标, 两个三角形, 组成一个矩形

static const SceneVertex vertices[] = 
{
//第一个三角形
   {{-1.0f, -0.5f, 0.0f}, {0.0f, 0.0f}},  // first triangle
   {{ 1.0f, -0.5f, 0.0f}, {1.0f, 0.0f}},
   {{-1.0f,  0.5f, 0.0f}, {0.0f, 1.0f}},
//第二个三角形
   {{ 1.0f, -0.5f, 0.0f}, {1.0f, 0.0f}},  // second triangle
   {{-1.0f,  0.5f, 0.0f}, {0.0f, 1.0f}},
   {{ 1.0f,  0.5f, 0.0f}, {1.0f, 1.0f}},
};

前面说过OpenGL ES 采用的是世界坐标系, 世界坐标系以屏幕中心为原点(0, 0, 0)。你面对屏幕,你的右边是x正轴,上面是y正轴,屏幕指向你的为z正轴。长度单位这样来定: 窗口范围按此单位恰好是(-1,-1)到(1,1)。

通过此坐标系渲染之后将是一个矩形, 但是实际上是有两个三角形组成的矩形

第一个三角形
第二个三角形
   //纹理 texture0
   CGImageRef imageRef0 = 
      [[UIImage imageNamed:@"leaves.gif"] CGImage];
      
   self.textureInfo0 = [GLKTextureLoader 
      textureWithCGImage:imageRef0 
      options:[NSDictionary dictionaryWithObjectsAndKeys:
         [NSNumber numberWithBool:YES], 
         GLKTextureLoaderOriginBottomLeft, nil] 
      error:NULL];
      
   // 纹理 texture1
   CGImageRef imageRef1 = 
      [[UIImage imageNamed:@"beetle.png"] CGImage];
      
   self.textureInfo1 = [GLKTextureLoader 
      textureWithCGImage:imageRef1 
      options:[NSDictionary dictionaryWithObjectsAndKeys:
         [NSNumber numberWithBool:YES], 
         GLKTextureLoaderOriginBottomLeft, nil] 
      error:NULL];
//GLKTextureLoaderOriginBottomLeft 设置成YES
  //开启混合
   glEnable(GL_BLEND);
//设置源因子与目标因子的关系
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

我们现在设置了两个纹理, 所以当我们绘制的时候也需要分别进行绘制, 此处注意, 纹理显示的先后顺序是根据绘制的先后顺讯决定的. 先绘制的在下面, 后绘制的在上面, 这个和UIView的层级结构是一样的.
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect方法里进行绘制, 这些方法也可以写在设置设置纹理的时候

   self.baseEffect.texture2d0.name = self.textureInfo0.name;
   self.baseEffect.texture2d0.target = self.textureInfo0.target;
      
   self.baseEffect.texture2d0.name = self.textureInfo1.name;
   self.baseEffect.texture2d0.target = self.textureInfo1.target;

混合就是在绘制时,不是直接把新的颜色覆盖在原来旧的颜色上,而是将新的颜色与旧的颜色经过一定的运算,从而产生新的颜色。新的颜色称为源颜色,原来旧的颜色称为目标颜色。传统意义上的混合,是将源颜色乘以源因子,目标颜色乘以目标因子,然后相加。

源 因子和目标因子是可以设置的。源因子和目标因子设置的不同直接导致混合结果的不同。将源颜色的alpha值作为源因子,用1.0减去源颜色alpha值作 为目标因子,是一种常用的方式。这时候,源颜色的alpha值相当于“不透明度”的作用。利用这一特点可以绘制出一些半透明的物体。

在进行混合时,绘制的顺序十分重要。因为在绘制时,正要绘制上去的是源颜色,原来存在的是目标颜色,因此先绘制的物体就成为目标颜色,后来绘制的则成为源颜色。绘制的顺序要考虑清楚,将目标颜色和设置的目标因子相对应,源颜色和设置的源因子相对应。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容