首页 > 其他分享 >OpenGL:纹理

OpenGL:纹理

时间:2024-06-07 19:55:27浏览次数:21  
标签:OpenGL glTexParameteri TEXTURE 纹理 2D 坐标 GL

我们已经了解到,我们可以为每个顶点添加颜色来增加图形的细节,从而创建出有趣的图像。但是,如果想让图形看起来更真实,我们就必须有足够多的顶点,从而指定足够多的颜色。这将会产生很多额外开销,因为每个模型都会需求更多的顶点,每个顶点又需求一个颜色属性。

艺术家和程序员更喜欢使用纹理(Texture)。纹理是一个2D图片(甚至也有1D和3D的纹理),它可以用来添加物体的细节;你可以想象纹理是一张绘有砖块的纸,无缝折叠贴合到你的3D的房子上,这样你的房子看起来就像有砖墙外表了。因为我们可以在一张图片上插入非常多的细节,这样就可以让物体非常精细而不用指定额外的顶点。

除了图像以外,纹理也可以被用来储存大量的数据,这些数据可以发送到着色器上,但是这不是我们现在的主题。

纹理坐标

为了将纹理映射至顶点上,每个顶点实际上都会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样(采集片段颜色)。

如果使用的是2D纹理,那么纹理坐标在x轴和y轴上,其范围为(0,1).(0,0)通常指代纹理的起始坐标(左下角),(1,1)代表着纹理的终止坐标(右上角)。

将纹理坐标传入片段着色器中,片段着色器会通过插值的方式进行采样。

纹理环绕方式

既然坐标是(0,0)到(1,1),那么将纹理坐标设置在范围之外会发生什么呢?OpenGL默认的行为是重复这个纹理图像(我们基本上忽略浮点纹理坐标的整数部分),但OpenGL提供了更多的选择:

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

前面提到的每个选项都可以使用glTexParameter*函数对单独的一个坐标轴设置(s、t(如果是使用3D纹理那么还有一个r)它们和x、y、z是等价的):

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选项,我们还需要指定一个边缘的颜色。这需要使用glTexParameter函数的fv后缀形式,用GL_TEXTURE_BORDER_COLOR作为它的选项,并且传递一个float数组作为边缘的颜色值:

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

纹理过滤

纹理坐标不依赖于分辨率(Resolution),它可以是任意浮点值,所以OpenGL需要知道怎样将纹理像素(Texture Pixel,也叫Texel)映射到纹理坐标。当你有一个很大的物体但是纹理的分辨率很低的时候这就变得很重要了。你可能已经猜到了,OpenGL也有对于纹理过滤(Texture Filtering)的选项。纹理过滤有很多个选项,但是现在我们只讨论最重要的两种:GL_NEARESTGL_LINEAR

GL_NEAREST即选择中心点最接近纹理坐标的像素,GL_LINEAR则根据周围像素中心点距离纹理坐标的远近进行加权求和。其表现分别如下:

当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理过滤的选项,比如你可以在纹理被缩小的时候使用邻近过滤,被放大时使用线性过滤。我们需要使用glTexParameter*函数为放大和缩小指定过滤方式。这段代码看起来会和纹理环绕方式的设置很相似:

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

纹理单元

纹理单元的主要目的是让我们在着色器中可以使用多个纹理。通过把纹理单元赋值给采样器,我们可以一次绑定多个纹理,只要我们首先激活对应的纹理单元。一般来说windous上的现代显卡有32个纹理单元:GL_TEXTURE0GL_TEXTURE32。其中GL_TEXTURE3可以写为GL_TEXTURE0 + 3

我们可以使用glActiveTexture激活纹理单元,再绑定纹理,这样就能将纹理绑定至对应的纹理单元上:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);

在片段着色器需要使用uniform设置对应纹理单元的序号(通常是一个int类型,从0到32),texture()函数是根据纹理单元和纹理坐标采样纹理用的:

#version 330 core
...

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
    FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}

加载与创建纹理

纹理坐标可以写入vbo中,通过顶点属性设置,然后在顶点着色器中输入显存以及传输至片段着色器中。(这里不再演示)

生成一个纹理的代码应该看起来如下:

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(1);
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D); // 可选
}
else
{
    std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);

标签:OpenGL,glTexParameteri,TEXTURE,纹理,2D,坐标,GL
From: https://www.cnblogs.com/warren-j1an/p/18237710

相关文章

  • ActiViz中的纹理映射
    文章目录1.纹理映射基础2.加载与处理体积数据3.配置纹理映射4.实现体积渲染5.优化与高级技术6.环境与光照7.集成到.NET应用8.总结1.纹理映射基础什么是纹理映射纹理映射是一种图形渲染技术,它将二维图像(纹理)贴合到三维模型的表面上,以此来增加模型......
  • 【Linux系统查看显卡支持的OpenGL版本】
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录一、安装依赖项二、Linux系统查看显卡支持的OpenGL版本一、安装依赖项sudoapt-getupdate&&sudoapt-getinstallmesa-utils二、Linux系统查看显卡支持的OpenGL版本glxinfo|grepO......
  • 可扩展的触摸屏按钮纹理
    如何在Godot中使`TouchScreenButton`可展开,从而使其边缘不会拉伸?使用普通按钮时,我们可以编辑主题,并在`Style.normal`下添加`StyleBoxTexture`以防止边缘拉伸。我如何使用`TouchScreenButton`实现这一功能?TouchScreenButton纹理可以像ninepachrect一样在不拉伸边......
  • matlab 2024a 在只有核显的机器上opengl报错的问题 X11GLXDrawableFactory
    com.jogamp.opengl.GLException:X11GLXDrawableFactory-CouldnotinitializesharedresourcesforX11GraphicsDevice[type.x11,connection:0,unitID0,handle0x0,ownerfalse,ResourceToolkitLock[obj0x37e10dfd,isOwnerfalse,<4dd74231,25b2e611>[c......
  • OpenGL:三角形的诞生
    先放一个图形渲染管线的每个阶段的抽象展示。要注意蓝色部分代表的是我们可以注入自定义的着色器的部分:顶点缓冲对象(VBO)所有的顶点数据(位置、纹理、法线等)都需要存储在GPU上,OpenGL通过顶点缓冲对象管理这个内存。其声明和绑定的代码函数如下:unsignedintvbo;glGenBuffers(1,......
  • OpenGL:一个黑框的诞生
    OpenGL并不是API,它不包含任何代码,仅仅只是一个关于输入输出的标准。关于OpenGL的具体实现则是由各个开发商负责。OpenGL是以状态机的方式进行控制,我们通过改变一些上下文变量来改变OpenGL状态,从而告诉OpenGL如何去绘图。一旦我们改变了OpenGL的状态为绘制线段,下一个绘制命令就会......
  • 纹理:基本知识
    uv集与纹理图层的对应uv集纹理图层uv集纹理图层0色彩映射表1凹凸贴图2脏蚀贴图3镜面贴图4不透明贴图5法线贴图6自发光贴图7遮挡贴图8粗糙度贴图9金属度贴图如果不存在uv集,则为默认值对于渲染和导出,每个定义的纹理图层均需要与u......
  • 【OpenGL高级】实例化多物体绘制
      一、说明  对于海量而相近物体渲染,如果用buffer直接渲染,其庞大的存储数据将能把整个系统资源耗光。而可行的,用少量模式数据去生成临时显示的数据这样用少量局部变量就能在循环内部完成,本篇将阐述如何去实现。二、实例化渲染  计算机图形学中的实例化是指使用......
  • 纹理是怎样显示在模型上的
    看完games101的第8和9章,就会很好理解这些内容。三维世界中的模型是由点组成三角形,进而组成复杂的模型。每个面都有不同的颜色(像素),可以用纹理来贴上去,最后在形成在屏幕上。立方体例子如何把2d的图片包到3d的模型上面?把3d物品展开成平面(展UV),和小时候做包装纸那样Blender零......
  • 使QOpenGLWidget的背景透明
    方法其实Qt帮助文档已经说了,如下图。大意是如果你想让QOpenGLWidget下方的子Widget可见只需要设置Qt::WA_AlwaysStackOnTop属性就行了;如果QOpenGLWidget下方没有子Widget,只是想创建一个半透明的窗口,那么对顶层窗口(主窗口)使用传统的Qt::WA_TranslucentBackground属性就可以实现了。......