在设计物体表面时,很多时候我们不满足于一种颜色或者几种简单颜色,我们希望是丰富多彩的图案,或者说我们提供给它的图片。这样一个顶点一个顶点的去指定那是行不通了,我们不可能把所有顶点用数字去表达出来,必须用一种新的方式去设置颜色。这就是纹理,像用一张画去贴在物体的表面一样,这样就不用指定太多的点,只需要设置“边界”就可以了。我们把这种行为叫做映射。 我们不可能随便映射,我们必须告诉程序三个东西:1.纹理图片剪裁多少(边界坐标位置)2.纹理图片对应的3d面的边界(顶点坐标位置)3将纹理图片的坐标与3d面的坐标一一对应。 (一)纹理图片设定:

和标准化坐标一样,纹理的坐标也是从0——1,不过这回没有负值了,不管图片有多么大,我们都将它们认为是在0-1的图片
坐标系中,其中左下顶点是原点。我们需要三个点构成一个面,所以我们找三个顶点作为表示边界的数据。

float texCoords[] = {
0.0f, 0.0f, // 左下角
1.0f, 0.0f, // 右下角
0.5f, 1.0f // 上中
};

(二)处理坐标超出(0,1)设定,纹理环绕方式

      不同于openGL的标准化坐标,在超出(-1,1)时候,直接不显示,当纹理坐标超出(0,1)的时候,纹理处理会将图片进行变
化,使图片将贴合的那个面完全包含,OpenGL默认的行为是重复这个纹理图像(我们基本上忽略浮点纹理坐标的整数部分)。
    接下来介绍一个函数,它用来设置种情况,它要放在你创建了一个纹理对象,并且绑定到当前上下文的后面,保证它对这个纹理对象起作用。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
第一个参数是表示2d渲染,第二个参数表示这是对那个坐标轴进行的(可以猜测这样可以有好多的组合情况发生),
最后一个参数需要我们传递一个环绕方式,这里设置的是GL_MIRRORED_REPEAT,和GL_REPEAT一样,但每次重复图片是镜像放置的。
(大多数情况都是这样,但有时候我们还需要指定填充颜色,这个我们一般用这个函数的变形函数,没办法c语言不支持重载)
(三)处理放大,缩小情况,纹理过滤   我们对于物体不可避免要应对放大和缩小的情况。首先我们屏幕的像素点数量是不可能改变的,也就是说,有的时候当你缩小物体的时候
你需要用更小的像素点数量去承载图片,放大的时候你需要用更多的像素点去表现物体,这就是纹理过滤的问题。此外还有一个情况就是当一
个分辨率很高的物体,在很远的地方。我们只能用很少的像素点去表现它。这一系列问题都是这个纹理过滤的原由 这里需要三个属性作为分析。1.纹理像素 2.纹理顶点 3.屏幕像素
首先纹理像素,就是一张图片不断放大后,能发现它是由一个一个点组成的,这就是纹理像素,它由拍摄这张图片的仪器决定,照片大小不变
它不变。
之后是纹理顶点,这个从(0,1)的绝对坐标组,不受分辨率影响,所以所以OpenGL需要知道怎样将纹理像素(Texture Pixel,也叫Texel,
译注1)映射到纹理坐标。
最后是屏幕像素,OpenGL根据纹理顶点坐标,查找纹理图片上的像素,再根据纹理像素判断分析,提取出一个颜色值,放置到屏幕像素上。
纹理过滤主要就是考虑如何分析判断。目前提供两种过滤GL_NEAREST(颗粒状的图案),GL_LINEAR(更平滑的图案)。


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

(四)处理物体远近情况,多级渐远纹理

   接下来我们考虑之前说的一个问题,当一个纹理分辨率很高的物体,在很远的地方,我们只能用很少的屏幕像素点去表示它。
OpenGL从高分辨率纹理中为这些片段获取正确的颜色值就很困难,因为它需要对一个跨过纹理很大部分的片段只拾取一个纹理颜色。在小物
体上这会产生不真实的感觉,对它们使用高分辨率纹理浪费内存的问题。这就是用多级渐远纹理的原因。
这是一种用“空间换时间”的手段。多级渐远纹理背后的理念很简单:距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,
即最适合物体的距离的那个。

注意每个二分之一的图片它们的纹理像素只有之前的四分之一多。这一步必须在加载图片成功之后再执行。
这个负责函数是glGenerateMipmaps(),此外也可以为这种缩小或者放大,指定纹理过滤形式。
glGenerateMipmap(GL_TEXTURE_2D);
这个参数跟VBO,VAO类似,代表了绑定在这个属性上的纹理对象。

(五)加载与创建纹理:

  接下来我们要把存储在文件中的图片转化成二进制流,让OpenGL识别,由于图片格式有很多种,我们要写很多读取函数去读,
这些函数我们当然不必自己去写,引用一个开源的支持多种流行格式的图像加载库就好了。stb_image.h库是我们用的。
我们引用一个stbi_load函数去加载图片文件好了。
int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
第一个参数接受一个图像文件的位置,剩下三个是宽度、高度和颜色通道的个数,最后一个填0.暂时不管。

(六)生成纹理:

使用前面载入的图片数据生成一个纹理   
接下来就是生成纹理了,首先我们需要一个纹理对象作为处理对象,毕竟前面我们一直在做图片的工作,那些属性的设置都需要一个
对象去承载它们。
//ID引用生成对象
unsigned int texture;
glGenTextures(1, &texture);
//绑定2d纹理目标(属性),之前的哪些纹理过滤也是这个时候用
glBindTexture(GL_TEXTURE_2D, texture);
//使用前面载入的图片数据生成一个纹理,用的生成函数为glTexImage2D,用处就是根据数据生成纹理
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
//第一个参数指定了纹理目标(Target)。第二个参数为纹理指定多级渐远纹理的级别。这里我们填0,也就是基本级别(不自动生成)。
//第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值,因此我们也把纹理储存为RGB值
//第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
//下个参数应该总是被设为0(历史遗留的问题)。
//第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
//最后一个参数是真正的图像数据。
生成纹理和多纹渐进纹理之后,我们就不需要图片数据了,这时释放它们,不要占用内存了 stbi_image_free(data);

(七)将纹理对象传给着色器,显示到屏幕上:

     严格来讲之前我们只是去处理了图片数据和纹理对象,如果我们想看到纹理的样子,我们就必须用着色器去显示。
我们要把纹理对象传递到着色器内部,然后告诉它如何去显示到3d图形的每个点上。
顶点着色器:
#version 330 core layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord; void main() {
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
} 把数据(纹理数据坐标)读进来,传到片段着色器,此外我们这个时候要从程序CPU把纹理对象传入着色器了,这个最好用之前的uniform
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main() {
FragColor = texture(ourTexture, TexCoord);
}
texture函数来采样纹理的颜色,它第一个参数是纹理采样器,第二个参数是对应的纹理坐标。
texture函数会使用之前设置的纹理参数对相应的颜色值进行采样。这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。 那怎么把纹理对象赋值给着色器呢,这个不用咱们去做,咱们只需要绑定纹理对象,绑定VAO,就可调用绘制函数了,纹理对象会自动传入的。
glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

LearnOpenGL学习笔记(五)——纹理的更多相关文章

  1. webgl学习笔记五-纹理

    写在前面 建议先阅读下前面我的三篇文章. webgl学习笔记一-绘图单点 webgl学习笔记二-绘图多点 webgl学习笔记三-平移旋转缩放 术语 : 纹理 :图像 图形装配区域 :顶点着色器顶点坐标 ...

  2. C#可扩展编程之MEF学习笔记(五):MEF高级进阶

    好久没有写博客了,今天抽空继续写MEF系列的文章.有园友提出这种系列的文章要做个目录,看起来方便,所以就抽空做了一个,放到每篇文章的最后. 前面四篇讲了MEF的基础知识,学完了前四篇,MEF中比较常用 ...

  3. (转)Qt Model/View 学习笔记 (五)——View 类

    Qt Model/View 学习笔记 (五) View 类 概念 在model/view架构中,view从model中获得数据项然后显示给用户.数据显示的方式不必与model提供的表示方式相同,可以与 ...

  4. java之jvm学习笔记五(实践写自己的类装载器)

    java之jvm学习笔记五(实践写自己的类装载器) 课程源码:http://download.csdn.net/detail/yfqnihao/4866501 前面第三和第四节我们一直在强调一句话,类 ...

  5. Learning ROS for Robotics Programming Second Edition学习笔记(五) indigo computer vision

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS for Robotics Pr ...

  6. Typescript 学习笔记五:类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  7. ES6学习笔记<五> Module的操作——import、export、as

    import export 这两个家伙对应的就是es6自己的 module功能. 我们之前写的Javascript一直都没有模块化的体系,无法将一个庞大的js工程拆分成一个个功能相对独立但相互依赖的小 ...

  8. muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor

    目录 muduo网络库学习笔记(五) 链接器Connector与监听器Acceptor Connector 系统函数connect 处理非阻塞connect的步骤: Connetor时序图 Accep ...

  9. python3.4学习笔记(五) IDLE显示行号问题,插件安装和其他开发工具介绍

    python3.4学习笔记(五) IDLE显示行号问题,插件安装和其他开发工具介绍 IDLE默认不能显示行号,使用ALT+G 跳到对应行号,在右下角有显示光标所在行.列.pycharm免费社区版.Su ...

  10. Go语言学习笔记五: 条件语句

    Go语言学习笔记五: 条件语句 if语句 if 布尔表达式 { /* 在布尔表达式为 true 时执行 */ } 竟然没有括号,和python很像.但是有大括号,与python又不一样. 例子: pa ...

随机推荐

  1. 别人的Linux私房菜(20)启动流程、模块管理与Loader

    系统启动时,首先加载BIOS,通过BOIS读取COMS的硬件信息,进行自我检测,取得第一个可启动的设备(多个根据设置有关). 读取并执行设备内的MBR启动引导程序,引导程序调用boot sector中 ...

  2. temp--贵州银行

    -------住宿----泊乐酒店----8905----与朱聿一起住 2018年  1月3日晚 1月4日晚  1月5日晚 1月6日晚  1月7日晚 1月8日晚  1月9日晚 已结清! ======= ...

  3. 解决jenkins shell执行sonar-scanner提示命令存在的问题

    通过jenkins的以下三个方式去执行sonar-scanner,抛如下错误. Send files or execute commands over SSH before the build sta ...

  4. redis操作(String,Hash,List,Set,其他操作)

    一.String操作 String操作,redis中的String在在内存中按照一个name对应一个value来存储.如图: set(name,value,ex=None,px=None,nx=Fal ...

  5. 解决 Files 的值"<<<<<<< HEAD"无效。路径中具有非法字符

    通常我们使用版本控制后会出现诸如此类的问题,此时从vs工具找错误和调试是无法找到问题的,也不影响项目的运行,但是有错误就是得解决.原因是版本控制导致文件的路径出现问题. 解决 Files 的值&quo ...

  6. 基于ajax提交数据

    昨日回顾: 1 inclusion_tag -干什么用的?生成html的片段(动态,传参数,传数据) -app下新建一个模块,templatetags -创建一个py文件(mytag.py) -fro ...

  7. Mac下git配置

    1.下载git 2.配置key macdeMacBook-Pro:~ mac$ cd ~/.ssh macdeMacBook-Pro:.ssh mac$ ssh-keygen -t rsa -C &q ...

  8. pom.xml文件模板、application文件模板、configuration逆向生成文件、

    pom: <?xml version="1.0" encoding="UTF-8"?><project xmlns="http:// ...

  9. G++与C++的区别

    C++是一门计算机编程语言,G++不是语言,是一款编译器中编译C++程序的命令而已. 不同的编译器,会对代码做出一些不同的优化 比如说: a++;  和 ++a; 如果从标准C的角度去理解.a++这个 ...

  10. 使用Consul 实现 MagicOnion(GRpc) 服务注册和发现

    1.下载打开Consul 笔者是windows下面开发的(也可以使用Docker). 官网下载windows的Consul https://www.consul.io/ 使用cmd窗口打开,输入con ...