什么是纹理
纹理是一个用来保存图像的颜色色素值得OpenGL ES缓存, 纹理的作用是为了使我们渲染的几何图像更加的逼真, 通常来说就是使用一张图片贴在我们需要渲染的几何图形上.
当一个图像初始化一个纹理缓存之后, 在这个图像中的每个像素就变成了纹素(texel), 纹素也是用来保存颜色数据的.
纹理坐标
纹理坐标是一个命名为S和T的2D轴, 在一个纹理坐标中, 无论纹素有多少个, 在纹理的尺寸永远是S轴上从0.0到1 .0, 在T轴上从0.0到1.0. 纹理坐标是一个相对坐标的概念.
例如, 从一个1像素高, 64像素宽的图像初始化来的纹理会沿着T轴有一个纹素, 沿着S轴有64个纹素.
转换几何形状数据为帧缓存中的颜色像素的渲染步骤叫做点阵化(rasterizing), 每个颜色像素叫做(fragment). 当OpenGL ES没有使用纹理时, GPU会根据包含该片元的对象的顶点的颜色来计算每个片元的颜色, 当设置了纹理之后, GPU会根据当前绑定的纹理缓存中的纹素来计算每个片元的颜色.
其实纹理就是要把一张图片贴在我们绘制的几何图像上, 我们要制定贴在哪个位置, 这时就需要用到OpenGL ES坐标与纹理坐标之间的转化.
我的理解是纹理坐标是一个相对的坐标, 是相对与OpenGL ES的坐标
在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值相当于“不透明度”的作用。利用这一特点可以绘制出一些半透明的物体。
在进行混合时,绘制的顺序十分重要。因为在绘制时,正要绘制上去的是源颜色,原来存在的是目标颜色,因此先绘制的物体就成为目标颜色,后来绘制的则成为源颜色。绘制的顺序要考虑清楚,将目标颜色和设置的目标因子相对应,源颜色和设置的源因子相对应。