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

    上午: # 1 开启子进程 #target #args # if __name__ == '__main__' #start() # 2.其它的方法: #方法: #terminate() #is_al ...

  2. adb安装apk

    1.    安装配置 1.1安装包 下载adb.zip,解压至本机 1.2环境配置 将adb安装路径加入path中 2.    安装apk 使用数据线将Android手机与电脑连接,打开手机usb调试 ...

  3. 偏差(Bias)和方差(Variance)——机器学习中的模型选择zz

    模型性能的度量 在监督学习中,已知样本 ,要求拟合出一个模型(函数),其预测值与样本实际值的误差最小. 考虑到样本数据其实是采样,并不是真实值本身,假设真实模型(函数)是,则采样值,其中代表噪音,其均 ...

  4. Qt打包发布exe

    1.首先以 release 方式编译源代码,然后将生成的a. exe 程序放到一个单独的文件夹中. 2.再从开始菜单打开 Qt 命令行工具. 3.在命令行中,进入到第一步中a. exe 程序所在的文件 ...

  5. python 文件与数据格式化

    https://www.cnblogs.com/li-zhi-qiang/p/9269453.html       文件和数据格式化 https://www.cnblogs.com/li-zhi-qi ...

  6. usb2.0、usb3.0、usb3.1、type-c 接口含义与区别

    简单说: usb3.0 比2.0的传输速率快,充电快,能向下兼容2.0 usb3.1 通常是指 usb3.1 gen2,比3.0的传输速率更快.充电更快,同兼容 type-c 通常是指 usb3.1的 ...

  7. 关于数据库连接时URL的问题

    最近在写一个简单的增删改查的代码时,遇到保存的中文都会变成问号(?),由于刚开始只是一些数据的保存,所以认为之后只要对数据库的编码进行修改即可,但是后来要对数据进行查找的时候发现根本查找不到, 当时用 ...

  8. c语言小程序以及java生成注释文档方法

    c语言小程序:sizeof和strlen() sizeof运算符以字节为单位给出数据的大小,strlen()函数以字符为单位给出字符串的长度,字符和字节不是一回事. char类型用于存储字母和标点符号 ...

  9. PHP单一文件入口框架简析

    <?php /** * PHP单一文件框架设计简析 * 1.MVC架构实现 * 2.URL路由原理 */ //URL路由原理 /** * 路由作用 * 获取url中的c和a变量,执行c类对应的方 ...

  10. go网络编程应用

    网络编程基础(聊天室) 服务端 package main import ( "fmt" "net" "strings" "os&q ...