OpenGL ES on iOS --- 2D纹理

OpenGL ES on iOS --- 2D纹理

简介

纹理是用来丰富我们绘制物体细节的,它可以是一张2D图片(除了图像外,纹理也被用来存储大量数据,传递到着色器上),就像贴图一样贴在绘制的物体上.

纹理属性

纹理坐标

为了将纹理映射到绘制的物体上,我们需要指定 某个顶点对应着 纹理的那个位置. 通过纹理坐标,标明顶点从纹理图像那一部分采样,之后在图形的其它片段进行片段插值.

纹理坐标系和顶点坐标系有所不同,顶点坐标系 (0,0)点位于窗口中心. 纹理坐标系 (0,0)点位于 纹理左下角.

顶点坐标系
纹理坐标系

纹理环绕方式

当我们将顶点位置设置到纹理坐标之外时,则需要设置纹理环绕方式 来显示纹理图案

环绕属性 效果
GL_REPEAT 重复纹理图案(默认)
GL_MIRRORED_REPEAT 镜像重复纹理图案
GL_CLAMP_TO_EDGE 将纹理锁定在0~1之间,超出部分重复纹理边缘图案,产生拉伸效果
GL_CLAMP_TO_BORDER 超出部分为用户指定边缘颜色
环绕示意图

纹理环绕函数

glTexParameteri (GLenum target, GLenum pname, GLint param);
参数:

target: 指定纹理目标,若为2D纹理 则为 GL_TEXTURE_2D
pname: 对应的纹理坐标轴(这里 s,t,r 对应 x,y,z) GL_TEXTURE_WRAP_S ,GL_TEXTURE_WRAP_T
param: 环绕方式,填入上面的方式.

对2D纹理时,必须对 s,t坐标轴都进行设置. 若是设置 GL_CLAMP_TO_BORDER 形式,则还需要额外设置 环绕颜色

float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

纹理过滤

纹理坐标是不依赖于 纹理大小和分辨率的, 1就表示纹理的边缘.但是物体和和纹理大小可能不一致,这就造成了对纹理的放大和拉伸.这时如何将纹理像素映射到纹理坐标上,就需要我们设置. 该属性就是 纹理过滤.

纹理过滤有很多种,下面是最重要的两种

邻近过滤

GL_NEAREST 是默认的纹理过滤方式,它会选择距离 纹理坐标最近的像素点作为样本颜色.当纹理被放大时,会有颗粒感


临近过滤

线性过滤

GL_LINEAR, 会基于当前纹理坐标附近的像素点计算一个插值,也就是附近纹理的混合色,离得越近的像素点,颜色贡献越大,当纹理被放大时,会比临近过滤更平滑.


线性过滤
临近过滤和线性过滤比较

过滤函数

我们需要对放大(Magnify)和缩小(Minify)的情况设置过滤效果

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

多级渐远纹理

在3D世界,根据物体的远近不同,物体也存在缩放的效果,要显示不同的分辨率,若都使用相同的分辨率,一是会使物体产生不真实的效果,二是会造成内存的浪费. 在研究3D纹理时再说.~~

纹理代码

首先是设置 上下文,着色器,程序对象...的方法.

context1 = [[EAGLContext alloc] initWithAPI:(kEAGLRenderingAPIOpenGLES3)];
    BOOL isSetCOntextRight = [EAGLContext setCurrentContext:context1];
    if (!isSetCOntextRight) {
        printf("设置Context失败");
    }

    NSString* verStr = [[NSBundle mainBundle] pathForResource:@"Texture2D_Vert.glsl" ofType:nil];
    NSString* fragStr = [[NSBundle mainBundle]pathForResource:@"Texture2D_Frag.glsl" ofType:nil];

    program1 = createGLProgramFromFile(verStr.UTF8String, fragStr.UTF8String);
    glUseProgram(program1);

    //创建,绑定渲染缓存 并分配空间
    glGenRenderbuffers(1, &renderBuf1);
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuf1);
    // 为 color renderbuffer 分配存储空间
    [context1 renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];

    //创建,绑定帧缓存 并分配空间
    glGenFramebuffers(1, &frameBuf1);
    // 设置为当前 framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuf1);
    // 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
                              GL_RENDERBUFFER, renderBuf1);


    glClearColor(1.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0, 0, self.frame.size.width, self.frame.size.height);

然后是 绘制物体的代码

float verData[] = {
        // 位置              颜色                 纹理坐标
        0.5f,0.5f,0.0f,     1.0f,0.0f,0.0f,     1.0f,1.0f,
        0.5f,-0.5f,0.0f,    0.0f,1.0f,0.0f,     1.0f,0.0f,
        -0.5f,-0.5f,0.0f,   0.0f,0.0f,1.0f,     0.0f,0.0f,
        -0.5f,0.5f,0.0f,    1.0f,1.0f,0.0f,     0.0f,1.0f,
    };
    unsigned int indices[] = {
        0,1,3,
        1,2,3
    };




    glGenVertexArrays(1, &VAO1);
    glGenBuffers(1, &VBO1);
    glGenBuffers(1, &EBO1);

    glBindVertexArray(VAO1);
    glBindBuffer(GL_ARRAY_BUFFER, VBO1);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO1);

    glBufferData(GL_ARRAY_BUFFER, sizeof(verData), verData, GL_STATIC_DRAW);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)0);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(3*sizeof(float)));
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(6*sizeof(float)));

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glEnableVertexAttribArray(2);

纹理设置~~

stbi_image 一个非常流行的单头文件图像加载库 stbi_image

    //因为使用 stbi 函数导入的图片会颠倒,所以需要将其摆正
    stbi_set_flip_vertically_on_load(true);
    
    NSString* imPath = [[NSBundle mainBundle] pathForResource:@"wall.jpg" ofType:nil];
    int width,height,nrChannels;

    //加载图片
    unsigned char * imdata = stbi_load(imPath.UTF8String, &width, &height, &nrChannels, 0);

    //创建 纹理
    unsigned int texture;
    glGenTextures(1, &texture);

    //激活纹理单元0
    glActiveTexture(GL_TEXTURE0);
    //绑定纹理
    glBindTexture(GL_TEXTURE_2D, texture);
    //将图像传入纹理
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imdata);

    //glGenerateMipmap(GL_TEXTURE_2D);

    //设置纹理环绕和纹理过滤
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    //将不用的图像释放
    stbi_image_free(imdata);

    //将纹理作为统一变量传入显存
    glUniform1i(glGetUniformLocation(program1, "outTexture"), 0);


    //这里PNG格式 通过stbi_load 拉入时 会导致产生 BGRA格式(我也不大清楚原因,懂的朋友 告告我 先O(∩_∩)O谢谢了) ,这时 图片作为纹理显示时色彩会出错.所以将其转换一蛤~
    NSString* imPath1 = [[NSBundle mainBundle] pathForResource:@"face.png" ofType:nil];
    int width1,height1,nrChannels1;
    unsigned char * imdata1 = stbi_load(imPath1.UTF8String, &width1, &height1, &nrChannels1, STBI_rgb_alpha);
    for (int i = 0; i<width1*height1; i++ ) {
        char tR = imdata1[i*4+2];
        imdata1[i*4+2] = imdata1[i*4];
        imdata1[i*4] = tR;
    }

    unsigned int texture1;

    glGenTextures(1, &texture1);
    glActiveTexture(GL_TEXTURE1);       //必须先写这个再绑定
    glBindTexture(GL_TEXTURE_2D, texture1);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width1, height1, 0, GL_RGBA, GL_UNSIGNED_BYTE, imdata1);
    glGenerateMipmap(GL_TEXTURE_2D);
    stbi_image_free(imdata1);
    
    glUniform1i(glGetUniformLocation(program1, "outTexture1"), 1);

    //绘制显示
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    [context1 presentRenderbuffer:GL_RENDERBUFFER];

片段着色器代码

#version 300 es

precision mediump float;


in vec2 outTexCoord;

uniform sampler2D outTexture;
uniform sampler2D outTexture1;

in vec3 outColor;

out vec4 FragColor;

void main()
{
FragColor = mix(texture(outTexture,outTexCoord),texture(outTexture1,outTexCoord),0.2);
}

GLSL内建的mix函数需要接受两个值作为参数,并对它们根据第三个参数进行线性插值。如果第三个值是0.0,它会返回第一个输入;如果是1.0,会返回第二个输入值。0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。

函数补充说明

glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);

target 纹理目标 设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理
level 为纹理指定多级渐远纹理的级别,0表示基本级别
internalformat 希望将纹理存储为何等格式
width height 图像宽高
border 设置为0 说是历史遗留问题
format 源图格式 type 数据格式
pixels 图像数据

纹理单元

在代码中 使用 glUniform1i方法进行纹理传递 是因为 纹理单元 这个概念.
使用glUniform1i可以为纹理采样器分配一个位置值,通过把纹理单元赋值给采样器,就可以一次绑定多个纹理.

OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0到GL_TEXTRUE15。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。

需要注意的是 在绑定纹理时 先要激活纹理单元才可以

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