OpenGL+MetalOpenGL ES图形处理OPenGL ES For IOS

OpenGL ES之纹理(三)

2017-07-04  本文已影响141人  冰三尺

什么是纹理

纹理是一个用来保存图像的颜色色素值得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值相当于“不透明度”的作用。利用这一特点可以绘制出一些半透明的物体。

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

上一篇下一篇

猜你喜欢

热点阅读