1)纹理坐标
纹理坐标范围0-1,左下角(0,0),右上角(0,1),使用纹理坐标获取纹理颜色叫做采样。
2)纹理环绕方式
如果纹理坐标超过0-1,OpenGL提供了几种环绕方式:
可以用glTexParameter函数来设置:
-
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);
3)纹理过滤
当纹理和物体相差很大时,会涉及到纹理缩放,通过以下代码来设置缩放选项:
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
4)多级渐远纹理
可使用glGenerateMipmaps创建多级渐远纹理。
在渲染中切换多级渐远纹理级别(Level)时,OpenGL在两个不同级别的多级渐远纹理层之间会产生不真实的生硬边界。就像普通的纹理过滤一样,切换多级渐远纹理级别时你也可以在两个不同多级渐远纹理级别之间使用NEAREST和LINEAR过滤。为了指定不同多级渐远纹理级别之间的过滤方式,你可以使用下面四个选项中的一个代替原有的过滤方式:
一个常见的错误是,将放大过滤的选项设置为多级渐远纹理过滤选项之一。这样没有任何效果,因为多级渐远纹理主要是使用在纹理被缩小的情况下的。
5)加载纹理
图片格式多种多样,可以用stb_image.h(下载)库来加载图片。加入以下两行代码,预处理器会修改头文件,让其只包含相关的函数定义源码。
-
#define STB_IMAGE_IMPLEMENTATION
-
#include "stb_image.h"
使用stbi_load加载图片:
-
int width, height, nrChannels;
-
unsigned char
*
data
= stbi_load(
"container.jpg",
&width,
&height,
&nrChannels,
0);
6)生成纹理
创建并绑定纹理:
-
unsigned int texture;
-
glGenTextures(
1,
&texture);
-
glBindTexture(GL_TEXTURE_
2D, texture);
用图片图片数据生成纹理:
-
glTexImage
2D(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,也就是基本级别。
-
第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有
RGB
值,因此我们也把纹理储存为RGB
值。 -
第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
-
下个参数应该总是被设为
0
(历史遗留的问题)。 -
第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为
char
(byte)数组,我们将会传入对应值。 -
最后一个参数是真正的图像数据。
生成一个纹理的过程:
-
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)
-
{
-
glTexImage
2D(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);
7)应用纹理
-
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
/
/ 左上
-
};
-
-
glVertexAttribPointer(
2,
2, GL_FLOAT, GL_
FALSE,
8
* sizeof(float), (void
*)(
6
* sizeof(float)));
-
glEnableVertexAttribArray(
2);
顶点着色器:
-
#version
330 core
-
layout (
location
=
0)
in vec
3 aPos;
-
layout (
location
=
1)
in vec
3 aColor;
-
layout (
location
=
2)
in vec
2 aTexCoord;
-
-
out vec
3 ourColor;
-
out vec
2 TexCoord;
-
-
void main()
-
{
-
gl_Position
= vec
4(aPos,
1.0);
-
ourColor
= aColor;
-
TexCoord
= aTexCoord;
-
}
片段着色器:
-
#version
330 core
-
out vec
4 FragColor;
-
-
in vec
3 ourColor;
-
in vec
2 TexCoord;
-
-
uniform sampler
2D ourTexture;
-
-
void main()
-
{
-
FragColor
= texture(ourTexture, TexCoord);
-
}
8)纹理单元
使用glUniform1i,我们可以给纹理采样器分配一个位置值,这样的话我们能够在一个片段着色器中设置多个纹理。一个纹理的位置值通常称为一个纹理单元(Texture Unit)。一个纹理的默认纹理单元是0,它是默认的激活纹理单元。其它的纹理单元使用时要激活。
-
glActiveTexture(GL_TEXTURE
0);
/
/ 在绑定纹理之前先激活纹理单元
-
glBindTexture(GL_TEXTURE_
2D, texture
1);
-
glActiveTexture(GL_TEXTURE
1);
-
glBindTexture(GL_TEXTURE_
2D, texture
2);
-
-
glBindVertexArray(VAO);
-
glDrawElements(GL_TRIANGLES,
6, GL_UNSIGNED_INT,
0);
片段着色器:
-
#version
330 core
-
...
-
-
uniform sampler
2D texture
1;
-
uniform sampler
2D texture
2;
-
-
void main()
-
{
-
FragColor
= mix(texture(texture
1, TexCoord), texture(texture
2, TexCoord),
0.2);
-
}
通过glUniform1i设置纹理单元:
-
glUniform
1i(glGetUniformLocation(ourShader.ID,
"texture1"),
0);
/
/ 手动设置
-
glUniform
1i(glGetUniformLocation(ourShader.ID,
"texture2"),
1);
/
/ 手动设置
图片可能会上下颠倒,这是因为OpenGL要求y轴0.0
坐标是在图片的底部的,但是图片的y轴0.0
坐标通常在顶部。用一下代码可以反转图片:
stbi_set_flip_vertically_on_load(true);
9)源码示例
-
int ma
in()
-
{
-
-
-
/
/ build
and compile our shader zprogram
-
/
/ ------------------------------------
-
Shader ourShader(
"4.2.texture.vs",
"4.2.texture.fs");
-
-
/
/
set
up vertex
data (
and buffer(s))
and configure vertex attributes
-
/
/ ------------------------------------------------------------------
-
float vertices[]
= {
-
/
/ positions
/
/ colors
/
/ texture coords
-
0.5f,
0.5f,
0.0f,
1.0f,
0.0f,
0.0f,
1.0f,
1.0f,
/
/
top
right
-
0.5f, -
0.5f,
0.0f,
0.0f,
1.0f,
0.0f,
1.0f,
0.0f,
/
/
bottom
right
-
-
0.5f, -
0.5f,
0.0f,
0.0f,
0.0f,
1.0f,
0.0f,
0.0f,
/
/
bottom
left
-
-
0.5f,
0.5f,
0.0f,
1.0f,
1.0f,
0.0f,
0.0f,
1.0f
/
/
top
left
-
};
-
unsigned int indices[]
= {
-
0,
1,
3,
/
/
first triangle
-
1,
2,
3
/
/ second triangle
-
};
-
unsigned int VBO, VAO, EBO;
-
glGenVertexArrays(
1,
&VAO);
-
glGenBuffers(
1,
&VBO);
-
glGenBuffers(
1,
&EBO);
-
-
glBindVertexArray(VAO);
-
-
glBindBuffer(GL_ARRAY_BUFFER, VBO);
-
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
-
-
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
-
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
-
-
/
/ position attribute
-
glVertexAttribPointer(
0,
3, GL_FLOAT, GL_
FALSE,
8
* sizeof(float), (void
*)
0);
-
glEnableVertexAttribArray(
0);
-
/
/ color attribute
-
glVertexAttribPointer(
1,
3, GL_FLOAT, GL_
FALSE,
8
* sizeof(float), (void
*)(
3
* sizeof(float)));
-
glEnableVertexAttribArray(
1);
-
/
/ texture coord attribute
-
glVertexAttribPointer(
2,
2, GL_FLOAT, GL_
FALSE,
8
* sizeof(float), (void
*)(
6
* sizeof(float)));
-
glEnableVertexAttribArray(
2);
-
-
-
/
/ load
and create a texture
-
/
/ -------------------------
-
unsigned int texture
1, texture
2;
-
/
/ texture
1
-
/
/ ---------
-
glGenTextures(
1,
&texture
1);
-
glBindTexture(GL_TEXTURE_
2D, texture
1);
-
/
/
set the texture wrapping parameters
-
glTexParameteri(GL_TEXTURE_
2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
/
/
set texture wrapping
to GL_REPEAT (
default wrapping
method)
-
glTexParameteri(GL_TEXTURE_
2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-
/
/
set texture filtering parameters
-
glTexParameteri(GL_TEXTURE_
2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
glTexParameteri(GL_TEXTURE_
2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
/
/ load image, create texture
and
generate mipmaps
-
int width, height, nrChannels;
-
stbi_
set_flip_vertically_
on_load(
true);
/
/ tell stb_image.h
to flip loaded texture
's on the y-axis.
-
// The FileSystem::getPath(...) is part of the GitHub repository so we can find files on any IDE/platform; replace it with your own image path.
-
unsigned char *data = stbi_load(FileSystem::getPath("resources/textures/container.jpg").c_str(), &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);
-
// texture 2
-
// ---------
-
glGenTextures(1, &texture2);
-
glBindTexture(GL_TEXTURE_2D, texture2);
-
// set the texture wrapping parameters
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture wrapping to GL_REPEAT (default wrapping method)
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
-
// set texture filtering parameters
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
-
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-
// load image, create texture and generate mipmaps
-
data = stbi_load(FileSystem::getPath("resources/textures/awesomeface.png").c_str(), &width, &height, &nrChannels, 0);
-
if (data)
-
{
-
// note that the awesomeface.png has transparency and thus an alpha channel, so make sure to tell OpenGL the data type is of GL_RGBA
-
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 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);
-
-
// tell opengl for each sampler to which texture unit it belongs to (only has to be done once)
-
// -------------------------------------------------------------------------------------------
-
ourShader.use(); // don't forget
to activate
/
use the shader
before setting uniforms!
-
/
/ either
set it manually like so:
-
glUniform
1i(glGetUniformLocation(ourShader.ID,
"texture1"),
0);
-
/
/
or
set it via the texture
class
-
ourShader.setInt(
"texture2",
1);
-
-
-
-
/
/ render loop
-
/
/ -----------
-
while (
1)
-
{
-
-
/
/ render
-
/
/ ------
-
glClearColor(
0.2f,
0.3f,
0.3f,
1.0f);
-
glClear(GL_COLOR_BUFFER_
BIT);
-
-
/
/ bind textures
on
corresponding texture units
-
glActiveTexture(GL_TEXTURE
0);
-
glBindTexture(GL_TEXTURE_
2D, texture
1);
-
glActiveTexture(GL_TEXTURE
1);
-
glBindTexture(GL_TEXTURE_
2D, texture
2);
-
-
/
/ render container
-
ourShader.
use();
-
glBindVertexArray(VAO);
-
glDrawElements(GL_TRIANGLES,
6, GL_UNSIGNED_INT,
0);
-
-
}
-
-
/
/
optional: de-allocate
all resources once they
've outlived their purpose:
-
// ------------------------------------------------------------------------
-
glDeleteVertexArrays(1, &VAO);
-
glDeleteBuffers(1, &VBO);
-
glDeleteBuffers(1, &EBO);
-
-
return 0;
-
}
-