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

    在网站开发过程中,有些时候我们需要了解MySQL的服务器状态信息,譬如当前MySQL启动后的运行时间,当前MySQL的客户端会话连接数,当前MySQL服务器执行的慢查询数,当前MySQL执行了多少SE ...

  2. pandas库的学习笔记

    Environment pandas 0.21.0 python 3.6 jupyter notebook 开始 习惯上,我们导入如下: import pandas as pd import nump ...

  3. jq 下拉框获取选中自定义属性值

    // 下拉框发送改变后 获取选择的信息 <div class="form-group"> <label class="col-sm-3 control- ...

  4. idea类里面编译报错,快速定位快捷键设置

    settings---->keyMap------->Main menu----------->在搜索框里输入error,找到Next Highlighted Error 和Prev ...

  5. laravel 对接 UCenter 基础

    说明:1,运行环境  laravel 5.3 php7+nginx+mysql 2,使用安装包  https://github.com/goodspb/laravel5-ucenter  上面有对接方 ...

  6. cURL error 60: SSL certificate problem...

    php在curl的时候报错 cURL error 60: SSL certificate problem: unable to get local issuer certificate (see ht ...

  7. 可遇不可求的Question之INSERT … ON DUPLICATE KEY UPDATE 语法篇

    MySQL 自4.1版以后开始支持INSERT … ON DUPLICATE KEY UPDATE语法,使得原本需要执行3条SQL语句(SELECT,INSERT,UPDATE),缩减为1条语句即可完 ...

  8. vue solt 属性浅析

    solt 也就是插槽,通俗的讲也是和div,span,p等类似的html标签,只是solt是子组件中的html标签.它显示不显示完全是由父组件决定的, 但是显示的位置是由子组件自己决定的.插槽主要分为 ...

  9. 解析分享链接在微信内转发防封API接口的实现原理

    域名被微信封了怎么办?相信这是很多做微信的朋友的疑惑,本人也是做防封的,特此写一篇文章,写给域名被微信封的.被秒封的朋友来看.简单个大家讲一下防封原理和实现方式. 域名拦截因素 我们先来了解一下域名为 ...

  10. WSGI and Paste学习笔记

    The Problem Lots of web frameworks Zope, Quixote, Webware, SkunkWeb and Twisted Web etc Applications ...