opelgl(四)纹理1

如果想让图形看起来真实一点,那我们就必须有足够多的顶点来指定足够多的颜色,这会产生许多额外的开销,于是产生了一种叫做纹理的图形,可以用他来添加物体的细节,你可以想象纹理是一张会有砖块的纸并且可以无缝的折叠贴合到你的3D的房子上,这样你的房子看起来就像有砖墙外表了。 这里写图片描述
为了能够把纹理映射到三角形上,我们需要指定三角形的每个顶点对应的纹理的哪个部分,这样每个顶点就会关联一个纹理坐标,我们为三角形指定了3个纹理坐标点。如上图所示,我们希望三角形的左下角对应纹理的左下角,因此我们把三角形左下角顶点的纹理坐标设置为(0, 0);三角形的上顶点对应于图片的上中位置所以我们把它的纹理坐标设置为(0.5, 1.0);同理右下方的顶点设置为(1, 0)。我们只要给顶点着色器传递这三个纹理坐标就行了,接下来它们会被传片段着色器中,它会为每个片段进行纹理坐标的插值。
1纹理环绕方式

  • GL_REPEAT 对纹理的默认行为,重复纹理图形,
  • GL_MIRRORED_REPEAT 和GL_REPEAT一样,但每次重复图片都是镜像放置的
  • GL_CLAMP_TO_EDGE 纹理坐标会被约束在0和1之间,超出的部分会重复纹理坐标的边缘,产生一种被拉伸的效果。
  • GL_CLAMP_TO_BORDER 超出的坐标为用户指定的颜色
    以上的这些选项都可以用glTexParameter*函数对每个坐标轴单独设置参数,(s,t,r分别对应下,x,y,z)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WARP_S,GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WARP_T,GL_MIRRORED_REPEAT);

如果选择GL_CLAMP_TO_BORDED选项,还需要指定一个边缘的颜色,此时需要使用fv后缀形式

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

2纹理过滤
两种最重要的
GL_NEAREST 和GL_LINEAR
第一种是openGL的默认处理方式,会选择图像最中心最接近纹理坐标的那个像素,
第二种就是综合坐标附近的纹理像素并且计算出一个插值。
当进行放大(Magnify)和缩小(Minify)操作的时候可以设置纹理过滤选项,这段代码设置于纹理选项很像

glTexparameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);//缩小用线性法
glTexpatameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);放大就用邻近法

3多级渐远纹理
想象一下,假设我们有一个包含着上千物体的大房间,每个物体上都有纹理。有些物体会很远,但其纹理会拥有与近处物体同样高的分辨率。由于远处的物体可能只产生很少的片段,OpenGL从高分辨率纹理中为这些片段获取正确的颜色值就很困难,因为它需要对一个跨过纹理很大部分的片段只拾取一个纹理颜色。在小物体上这会产生不真实的感觉,更不用说对它们使用高分辨率纹理浪费内存的问题了。
openGL使用一种叫做多级渐远纹理(Mipmap)的概念解决这个问题,
这里写图片描述

openGL会根据图像距离观察者的距离来选择合适的多级渐远纹理,而且他的性能十分优秀,在创建完一个纹理对象之后,调用glGenerateMipmap函数,这样openGL会承担接下来的所有工作。
OpenGL在两个多级渐远纹理级别之间会产生不真实的生硬的边界,此时,我们可以使用Nearest和Linear两种方式对其进行过滤,可以使用以下四个选项代替原有的过滤方式。

过滤方式 描述
- GL_NEAREST_MIPMAP_NEAREST 使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样
- GL_LINEAR_MIPMAP_NEAREST 使用最邻近的多级渐远纹理级别,并使用线性插值进行采样
- GL_NEAREST_MIPMAP_LINEAR 在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样
- GL_LINEAR_MIPMAP_LINEAR 在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样 。
如同纹理过滤一样,可以用glTexParameteri将过滤方式设置为前面提到的四种方式之一

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
//常见的错误就是为放大选项设置多级纹理过滤,这样没有任何效果,纹理放大不会使用多级渐远纹理

4stb_image.h
使用stb_image.h来加载大部分流行的文件格式,并且很简单的整合到你的工程中去。
可以从这里下载
5生成纹理,
与着色器等OpenGL对象一样,纹理也是通过ID来创建的

unsigned int texture;
glGenTexture(GL_TEXTURE_2D,texture);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width,height,0,GL_RGB,GL_UNSIGNED_BYTE,data);
glGenerateMipmap(GL_TEXTURE_2D);
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;
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    // 左上
};

增加了纹理坐标之后,我们将顶点属性告诉OpenGL,

...
glVertexAttribPointer(2,2,GL_FLOAT,GL_FALSE,8*sizeof(float),(void*)(6*sizeof(float)));
glEnableVertexAttribArray(2);

接着修改顶点着色器

#version 330 core
layout(location=0) in vec3 aPos;
layout(location=1) in vec3 aColor;
layout(location=2) in vec3 aTexCoord;

out vec3 ourColor;
out vec2 TexCoord;
void main()
{
    gl_position =vec4(aPos,1.0);
    ourColor =aColor;
    TexCoord=aTexCoord;
}

GLSL有一个供纹理对象使用的内建数据类型,叫做采样器(Sampler),它以纹理类型作为后缀,比如sampler1D、sampler3D,或在我们的例子中的sampler2D。

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D ourtexture;
void main()
{
    FragColor =texture(ourtexture,TexCoord);
}

在绘画前绑定纹理,
glBindTexture(GL_TEXTURE_2D,texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);

猜你喜欢

转载自blog.csdn.net/shine10076/article/details/82691572