LearnOpengl-Textures

  1. 纹理坐标
  2. 纹理环绕方式
  3. 纹理过滤
  4. 多级渐远纹理(Mipmap)
  5. stb_image.h
    1. 加载图片
  6. 生成纹理
  7. 应用纹理
  8. 纹理单元
  9. 图片翻转问题

纹理坐标

  • 纹理坐标中,左下角位(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);