OpenGL教学 | 3.了解纹理,给图形添加纹理

本文介绍了OpenGL中纹理的相关知识,包括纹理的环绕方式如GL_REPEAT、GL_MIRRORED_REPEAT和GL_CLAMP_TO_EDGE,过滤方式如GL_NEAREST和GL_LINEAR,以及多级渐远纹理(Mipmap)的作用和使用。此外,还讲解了如何加载与创建纹理,以及如何应用和混合多个纹理。
摘要由CSDN通过智能技术生成

本篇将学习OpenGL中关于纹理的各种知识。纹理就是图片,用于给模型添加细节。

个人博客食用体验更佳:文章链接

纹理环绕方式

如果我们把纹理坐标设置在范围之外会发生什么?OpenGL默认的行为是重复这个纹理图像。下面更多的选择:

环绕方式描述
GL_REPEAT对纹理的默认行为。重复纹理图像。
GL_MIRRORED_REPEAT和GL_REPEAT一样,但每次重复图片是镜像放置的。
GL_CLAMP_TO_EDGE纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
GL_CLAMP_TO_BORDER超出的坐标为用户指定的边缘颜色。

设置坐标轴环绕方式(s、t(如果是使用3D纹理那么还有一个r))

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);

如果选择GL_CLAMP_TO_BORDER选项,需要指定一个边缘的颜色。

float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

纹理过滤

如何将纹理像素(Texture Pixel)映射到纹理坐标,让分辨率清晰

纹理过滤有很多个选项,但是现在我们只讨论最重要的两种:

GL_NEAREST

邻近过滤 (Nearest Neighbor Filtering):

选择中心点最接近纹理坐标的那个像素

img

GL_LINEAR

线性过滤 (linear Filtering):

基于纹理坐标附近的纹理像素,计算出一个插值 (心距离纹理坐标越近,贡献越大)

img

效果对比

img

我们可以在纹理被缩小的时候使用邻近过滤,被放大时使用线性过滤。方法:

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

多级渐远纹理

(Mipmap):一系列的纹理图像,后一个纹理图像是前一个的二分之一。

功能:提升真实性、提升性能。

img

过滤方式描述
GL_NEAREST_MIPMAP_NEAREST使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样
GL_LINEAR_MIPMAP_NEAREST使用最邻近的多级渐远纹理级别,并使用线性插值进行采样
GL_NEAREST_MIPMAP_LINEAR在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样
GL_LINEAR_MIPMAP_LINEAR在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样

设置方法:

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

加载与创建纹理

将图像加载到应用中,需要自己写一个图像加载器,或者使用支持多种格式的图像加载库。

stb_image.h

stb_image.h下载链接

将头文件加入你的工程,并在源文件中输入以下代码引入:

#define STB_IMAGE_IMPLEMENTATION //让头文件只包含相关的函数定义源码
#include "stb_image.h"

接下来,我们用stb_image加载图片

int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);

加载图片前我们为了防止图片颠倒输出,我们可以输入

stbi_set_flip_vertically_on_load(true); //OpenGL会自动将图片颠倒,所以我们要上下翻转

生成纹理

创建纹理对象

unsigned int texture;
glGenTextures(1, &texture);

生成纹理

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
  • 第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
  • 第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
  • 第三个参数:纹理储存格式。(RGB\RGBA)
  • 第四个和第五个参数设置最终的纹理的宽度和高度。
  • 下个参数应该总是被设为0(历史遗留的问题)。
  • 第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
  • 最后一个参数:图像数据。

最后还要释放图像内存

stbi_image_free(data);

生成纹理的整个过程

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;
stbi_set_flip_vertically_on_load(true);//OpenGL会自动将图片颠倒,所以我们要上下翻转
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
    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);

应用纹理

接下来我们来应用纹理,绘制一个带我的世界草块纹理的矩形:草块图片

float vertices[] = {
//     ---- 位置 ----       ---- 颜色 ----     - 纹理坐标 -
	0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // 右上
    0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // 右下
    -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // 左下
    -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // 左上
};
unsigned int indices[] = {
    0, 1, 3, // first triangle
    1, 2, 3  // second triangle
};

在循环中绘制

glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

我们的草块就出来了!

我们再在草块上加上顶点颜色:修改片段着色器

源代码:纹理应用源码

FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);

纹理单元

纹理单元(Texture Unit):纹理的位置值

绑定前激活纹理单元:

glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);

我们来做个笑脸草块:

先修改一下片段着色器:

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
    //mix输出两个texture的混合值
}

再设置纹理单元

ourShader.use();
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);

glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

就可以看到我们的笑脸草块了

源代码:混合纹理源码

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值