OpenGL ES之纹理(三)

作者: 冰三尺 | 来源:发表于2017-07-04 10:48 被阅读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值相当于“不透明度”的作用。利用这一特点可以绘制出一些半透明的物体。

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

    相关文章

      网友评论

        本文标题:OpenGL ES之纹理(三)

        本文链接:https://www.haomeiwen.com/subject/ueuthxtx.html