09 - OpenGL学习之基本纹理

前言

前面文章中,我们通过为顶点添加颜色,来创建有趣的图形,但是现实世界中的物体(例如砖墙,草坪等等)表面是有很多细节的,如果我们想要让图形看起来更加真实,必须要有足够的顶点,从而能指定足够多的颜色,描述图形的细节。但是绘制一个立方体的时候,我们就需要绘制六个面,已经感觉很繁琐,更不用说为每个面绘制不同的细节。
所以接下来我们介绍让图形看起来更加真实的技术------纹理。
纹理类型包含以下几种:2D纹理,立方图纹理,3D纹理,2D纹理数组,1D纹理,下面我们介绍最基本的2D纹理,来帮助我们了解纹理这个概念。

下面我们在正方形上加载一个纹理。


IMG_6718.PNG

片段着色器:

#version 300 es 

precision mediump float;

in vec2 oTextCoord;

uniform sampler2D  texture1;
 
out vec4 fragColor;

void main() {
    
    fragColor = texture(texture1,oTextCoord);
    
}

片段着色器中,我们用sampler2D 这个类表示纹理,texture函数加载纹理像素,需要传入两个对象:纹理对象,纹理坐标。
这里我们说明下纹理坐标的概念:
这里我们使用的是2D纹理,纹理坐标在X轴和Y轴,范围在0~1之间,纹理坐标标明从纹理图像的哪个部分采样,通过使用纹理坐标把纹理图像映射到屏幕的过程叫做纹理映射也叫纹理贴图。


2D纹理坐标

代码如下:

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    [super glkView:view drawInRect:rect];

    _esContext.width = view.drawableWidth;
    _esContext.height = view.drawableHeight;
    
    GLfloat factor =  (view.frame.size.width / view.frame.size.height) * 0.5;
    
    
    //(x,y,z) (s,t)
    float position[] = {
        -0.5,  factor, 0.0,  0.0, 0.0,
        -0.5, -factor, 0.0,  0.0, 1.0,
         0.5, -factor, 0.0,  1.0, 1.0,
         0.5,  factor, 0.0,  1.0, 0.0,
    };
    
    
    //开启深度测试,为了确定绘制的时候哪一个面绘制在上面
    glClear(GL_COLOR_BUFFER_BIT);
    
    GLuint vboIndex = 0;
    glGenBuffers(1, &vboIndex);
    glBindBuffer(GL_ARRAY_BUFFER, vboIndex);
    glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
    
    GLuint positionIndex = glGetAttribLocation(_esContext.program, "vPosition");
    GLuint textCoordIndex = glGetAttribLocation(_esContext.program, "textCoord");
    
    GLuint textureLocation = glGetUniformLocation(_esContext.program, "texture1");
    
    glEnableVertexAttribArray(positionIndex);
    glEnableVertexAttribArray(textCoordIndex);
    
    GLuint offset = 3 * sizeof(float);
    glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, 5  * sizeof(float), NULL);
    glVertexAttribPointer(textCoordIndex, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void *)offset);
    
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, self.baseTexture.name);
    glUniform1i(textureLocation, 0);
    _esContext.drawFunc(&_esContext);
    
    //关闭顶点属性
    glDisableVertexAttribArray(positionIndex);
    glDisableVertexAttribArray(textCoordIndex);
    glDeleteBuffers(1, &vboIndex);
    
    
}

代码中注意以下两点:
1.这里我们设置顶点坐标的时候用上了屏幕宽高比,这是因为手机屏幕宽高不一样,如果按照0.5的比例来渲染的话,图像会拉伸,所以需要乘以这个宽高比。

 glActiveTexture(GL_TEXTURE0);
  glBindTexture(GL_TEXTURE_2D, self.baseTexture.name);
  glUniform1i(textureLocation, 0);

这里的baseTexture是通过GLkit库提供的GLKTextLoader类加载的纹理对象。得益于这个API,帮我们省去了很多工作,示例代码中注释的部分还含有自己生成纹理对象,并绑定使用的过程。

纹理环绕

上面我们说的纹理坐标范围是0~1如果我们超过了这个范围,怎么办呢,OpenGL ES默认的行为是重复这个纹理图像(但OpenGL ES提供了更多的选择:

#define GL_REPEAT                对纹理的默认行为。重复纹理图像。
#define GL_CLAMP_TO_EDGE        纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
#define GL_MIRRORED_REPEAT       和GL_REPEAT一样,但每次重复图片是镜像放置的。
纹理环绕方式图解

纹理过滤:

纹理坐标不依赖于屏幕分辨率,它可以是任意浮点值,当你有一个很大的物体,但是纹理的分辨率很低的时候,ES需要知道怎样将纹理映射到屏幕,ES这里提供了对于纹理过滤的选项。这里我们介绍最重要的两项:
1.GL_NEARST(邻近过滤),这个是默认的纹理过滤方式,当设置为这个选项时,ES会选择中心点最接近纹理坐标的那个像素。如下图所示

GL_NEARST

2.GL_LINEAR,当设置为这个选项时,它会基于纹理坐标附近的纹理像素,计算一个差值。一个纹理像素的中心距离纹理坐标越近,那么它在插值的计算中,占比越大。如下图所示:


GL_LINEAR

看一下两种方式对最终成像效果的影响:


效果图

我们可以看到GL_NEARST方式的纹理具有颗粒感,可以看清像素,但是GL_LINEAR效果比较真实,过渡比较自然。

mipmap贴图

想象一下,假设我们绘制一段城墙,这个城墙表面是由一个个砖块纹理拼接而成,近处的看起来效果很自然,但是距离很远的时候,由于远处的物体可能只产生很少的片段,OpenGL ES从高分辨率纹理中为这些片段获取正确的颜色值就很困难,因为它需要对一个跨过纹理很大部分的片段只拾取一个纹理颜色,在小物体上会产生不真实的感觉,而且对它们使用高分辨率的纹理也会浪费资源,造成性能问题。
OPenGL ES使用一种叫做多级渐远纹理(Mipmap)的概念来解决这个问题,它简单来说就是一系列的纹理图像,后一个纹理图像是前一个的二分之一。多级渐远纹理背后的理念很简单:距观察者的距离超过一定的阈值,OpenGL ES 会使用不同的多级渐远纹理,即最适合物体的距离的那个。由于距离远,解析度不高也不会被用户注意到。同时,多级渐远纹理另一加分之处是它的性能非常好。

mipmap也有几种过滤方式:

GL_NEAREST_MIPMAP_NEAREST   使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样
GL_LINEAR_MIPMAP_NEAREST    使用最邻近的多级渐远纹理级别,并使用线性插值进行采样
GL_NEAREST_MIPMAP_LINEAR    在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样
GL_LINEAR_MIPMAP_LINEAR 在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样

1.生成和绑定纹理

   GLuint textId = 0;
   glGenTextures(1, &textId);
   glBindTexture(GL_TEXTURE_2D, textId)

2.加载纹理数据

 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 750, 750, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)_imageData.bytes);
  1. 设置纹理过滤方式
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 
 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

4.绑定纹理到着色器

glUniform1i(textureLocation, 0);

使用纹理大致分为上面几步,示例程序中有两种方式,第一种简易方式是借助GLKit.后面一种是实际的操作过程。

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

推荐阅读更多精彩内容

  • 一、前言 图片在屏幕上的显示,最终都是解码成位图,然后进行显示的。 一个图形在帧缓存区中的存储空间,可以根据如下公...
    含笑州阅读 711评论 0 0
  • 纹理(Texture) 纹理(Texture) 是一个2D图像(也有1D和3D纹理存在)用于给一个对象添加细节信息...
    蓬篙人阅读 1,146评论 0 5
  • 基本概念 ** 纹理 **概念:纹理是一个用来保存图像颜色元素值的OpenGL ES缓存。应该尽量使用最小的图像来...
    星空雪雨阅读 1,507评论 0 6
  • 一、了解纹理 在OpenGL中,纹理是一种图形数据,主要用于包装不同的物体。我们来举个例子:装修房子时,各个房间需...
    宇宙那么大丶阅读 471评论 0 0
  • 纹理纹理是一个2D图片,它可以用来添加物体的细节,让物体更加真实。为了能够把纹理映射到三角形上,我们需要制定三角形...
    ccccr阅读 750评论 0 0