纹理坐标
纹理坐标中,左下角位(0,0),右上角位(1,1)
使用纹理坐标获取纹理颜色叫做采样(Sampling)
纹理环绕方式
纹理过滤
产生原因:纹理坐标可以取任意浮点值,所以纹理元素与纹理坐标无法一一对应
纹理元素(Texel)
而纹素是纹理图片空间的基本单元,可以看成是纹理的组成“像素”
- 纹理过滤(Texture Filtering)
一个像素一般不会正好对应于一个纹元(texel)。所以像素的颜色无法直接得到,需要经过一定的运算,这个过程就是纹理过滤。
参考网址:https://zhuanlan.zhihu.com/p/91208143
- 有两种常见的纹理过滤的方式:GL_NEAREST和GL_LINEAR
邻近过滤:选择中心点最接近纹理坐标的那个像素
线性过滤:基于纹理坐标附近的纹理像素,计算出一个插值。一个纹理像素的中心距离纹理坐标越近,那么这个纹理像素的颜色对最终的样本颜色的贡献越大。
具体项目可见https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/
- 如何设置过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
GL_TEXTURE_MAG_FILTTER:纹理元素的数量 < 图元像素的数量。比如texture为256*256,图元为512*512。相当于纹理元素被放大,一个纹理元素的颜色决定多个图元像素的颜色
GL_TEXTURE_MIN_FILTER 纹理元素的数量 > 图元像素的数量。比如texture为512*512,图元为256*256。相当于纹理被缩小,一个图元像素的颜色由多个纹理像素决定。
多级渐远纹理(Mipmap)
产生原因:我们有一个包含着上千物体的大房间,每个物体上都有纹理。有些物体会很远,但其纹理会拥有与近处物体同样高的分辨率。由于远处的物体可能只产生很少的片段(像素),OpenGL从高分辨率纹理中为这些片段(像素)获取正确的颜色值就很困难,因为它需要对一个跨过纹理很大部分的片段(像素)只拾取一个纹理颜色。在小物体上这会产生不真实的感觉,更不用说对它们使用高分辨率纹理浪费内存的问题了。
多级渐远纹理:简单来说就是一系列的纹理图像,后一个纹理图像是前一个的二分之一
距观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个。由于距离远,解析度不高也不会被用户注意到。同时,多级渐远纹理另一加分之处是它的性能非常好。
Mipmap的例子如下
可以在GL_TEXTURE_MIN_FILTER中使用多级渐远纹理过滤选项,但是在GL_TEXTURE_MAG_FILTER中无法使用,纹理放大不会使用多级渐远纹理,为放大过滤设置多级渐远纹理的选项会产生一个GL_INVALID_ENUM错误代码。
stb_image.h
加载图片
int width, height, nrChannels;
//地址、宽度、高度、颜色通道的个数、期望通道数(得到的data中的数据的通道数目)
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
生成纹理
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 为当前绑定的纹理对象设置环绕、过滤方式
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);
// 加载并生成纹理
int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
//用于生成2D纹理
// 参数一:设置纹理目标,一般为GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
// 参数二:Mipmap级别,手动设置每个的话这里设0
// 参数三:纹理存储格式
// 参数四:宽度
// 参数五:长度
// 0
// 参数六:image的的存储格式
// 参数七:图像数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
//为当前绑定的纹理自动生成所有需要的多级渐远纹理。
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
//释放图像的内存
stbi_image_free(data);
应用纹理
- 在定点数组中添加纹理坐标
- 在VS中声明纹理坐标属性
- 利用glVertexAttribPointer给定点数组中的纹理坐标解释,并启用该定点属性
- 在PS中获取VS中的纹理坐标,声明sampler2D变量
- glActiveTexture激活对应的纹理单元
- 利用texture(ourTexture, TexCoord)使用之前设置的纹理参数对相应的颜色值进行采样
纹理单元
- uniform sampler2D MySampler存放纹理的位置称为纹理单元。
- opengl有GL_TEXTURE0 - GL_TEXTURE15总共16个纹理单元。
- 使用方式如下
// 设置着色器采样器使用哪个纹理单元
ourShader.use(); // 别忘记在激活着色器前先设置uniform!
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); // 手动设置
ourShader.setInt("texture2", 1); // 或者使用着色器类设置
//激活纹理单元
glActiveTexture(GL_TEXTURE0);
//绑定时会自动使用当前激活的纹理单元
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
- 纹理单元GL_TEXTURE0默认总是被激活,所以我们在前面的例子里当我们使用glBindTexture的时候,无需激活任何纹理单元。
图片翻转问题
- 纹理可能上下颠倒,这是因为OpenGL要求y轴0.0坐标是在图片的底部的,但是图片的y轴0.0坐标通常在顶部。
//解决方法
stbi_set_flip_vertically_on_load(true);