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值来作为因子。
举例来说:
- 如果设置了glBlendFunc(GL_ONE, GL_ZERO);,则表示完全使用源颜色,完全不使用目标颜色,因此画面效果和不使用混合的时候一致(当然效率可能会低一点点)。如果没有设置源因子和目标因子,则默认情况就是这样的设置。
- 如果设置了glBlendFunc(GL_ZERO, GL_ONE);,则表示完全不使用源颜色,因此无论你想画什么,最后都不会被画上去了。(但这并不是说这样设置就没有用,有些时候可能有特殊用途)
- 如 果设置了glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减 小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
- 如果设置了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值相当于“不透明度”的作用。利用这一特点可以绘制出一些半透明的物体。
在进行混合时,绘制的顺序十分重要。因为在绘制时,正要绘制上去的是源颜色,原来存在的是目标颜色,因此先绘制的物体就成为目标颜色,后来绘制的则成为源颜色。绘制的顺序要考虑清楚,将目标颜色和设置的目标因子相对应,源颜色和设置的源因子相对应。