OpenGL-04-01纹理
原文:https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/#_5
本文只是对原文的个人总结与简化
在程序最前端加上这两行代码
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
接下来我们创建纹理对象,为了方便解释,我们创建两个纹理对象
创建纹理
创建第一个纹理对象
和之前的OpenGL对象一样,纹理也是使用ID引用的
创建纹理对象
// load and create a texture1
// -------------------------
//创建纹理ID
unsigned int texture1;
//生成纹理对象,并存储在texture中
glGenTextures(1, &texture1);
glGenTextures函数首先需要输入生成纹理的数量,然后把它们储存在第二个参数的unsigned int
数组中(我们的例子中只是单独的一个unsigned int
)
这样就创建了一个纹理,然后我们需要绑定他,绑定之后的纹理指令都会来配置当前绑定的纹理
绑定纹理
//绑定当前的纹理
glBindTexture(GL_TEXTURE_2D,texture1); // all upcoming GL_TEXTURE_2D operations now have effect on this texture1 object
配置纹理
// 为当前绑定的纹理对象设置环绕、过滤方式
// set the texture1 wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture1 wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 为当前绑定的纹理对象设置环绕、过滤方式
// set texture1 filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//多级渐远过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
现在我们已经创建,绑定并配置好了纹理对象
接下来首先需要使用stbi_load
函数加载图片,并获得纹理数据
这个函数首先接受一个图像文件的位置作为输入。接下来它需要三个int
作为它的第二、第三和第四个参数,stb_image.h
将会用图像的宽度、高度和颜色通道的个数填充这三个变量。我们之后生成纹理的时候会用到的图像的宽度和高度的。
// load image, create texture1 and generate mipmaps
int width, height, nrChannels;
// 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("Texture/container.jpeg", &width, &height, &nrChannels, 0);
获得纹理数据后,接下来我们首先判断是否获得了纹理数据,如果获得了,我们可以使用glTexImage2D
函数来生成纹理
函数很长,参数也不少,所以我们一个一个地讲解:
- 第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
- 第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
- 第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有
RGB
值,因此我们也把纹理储存为RGB
值。 - 第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
- 下个参数应该总是被设为
0
(历史遗留的问题)。 - 第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为
char
(byte)数组,我们将会传入对应值。 - 最后一个参数是真正的图像数据。
当调用glTexImage2D
时,当前绑定的纹理对象就会被附加上纹理图像。
然而,目前只有基本级别(Base-level)
的纹理图像被加载了。
如果之前配置纹理过滤的时候,使用了多级渐远纹理,我们必须手动设置所有不同的图像(不断递增第二个参数)。或者,直接在生成纹理之后调用glGenerateMipmap
。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。
这里我设置了多级渐远纹理,就是之前的这行代码
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);//多级渐远过滤
如果不想设置,那就可以更改为GL_NEAREST也就是邻近过滤。那么后面就不用使用glGenerateMipmap
函数了。
如果设置了就要调用这个函数,否则会黑屏
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 texture1" << std::endl;
}
最后记得释放图像内存
//释放图像内存
stbi_image_free(data);
创建第二个纹理对象
// load and create a texture1
// -------------------------
//创建纹理ID
unsigned int texture2;
//生成纹理对象,并存储在texture中
glGenTextures(1, &texture2);
//绑定当前的纹理
glBindTexture(GL_TEXTURE_2D,texture2); // all upcoming GL_TEXTURE_2D operations now have effect on this texture1 object
// 为当前绑定的纹理对象设置环绕、过滤方式
// set the texture1 wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture1 wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 为当前绑定的纹理对象设置环绕、过滤方式
// set texture1 filtering parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// load image, create texture1 and generate mipmaps
// 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.
data = stbi_load("Texture/wall.jpeg", &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 texture1" << 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 // 左上
};
这是顶点着色器,我们可以看到,他有三个位置值变量:分别是顶点位置坐标,颜色值,纹理坐标
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
因此,我们还需要将顶点数据复制到缓存里,这里我们绘制的是矩形,因此采用索引缓存对象,基本操作和之前基本一样
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);
唯一不同的是,这次有三个位置值变量,因此我们要告诉OpenGL正确的数据解析方式
// 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);
// texture1 coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
第一个参数是位置值,三个分别为0,1,2。步长为8个float
类型的长度,偏移量分别为0,3,6。
之前的知识掌握了的话,这些无需多做解释。
最后就是分别启用每个顶点属性。
这是片段着色器
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture sampler
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.5);
}
GLSL内建的mix
函数需要接受两个值作为参数,并对它们根据第三个参数进行线性插值。如果第三个值是0.0
,它会返回第一个输入;如果是1.0
,会返回第二个输入值。0.2
会返回80%
的第一个输入颜色和20%
的第二个输入颜色,即返回两个纹理的混合色。
他还有两个新出现的变量:
uniform sampler2D texture1;
uniform sampler2D texture2;
这是两个纹理采样器,因为我们有两个纹理对象,所以需要两个。
我们使用GLSL内建的texture
函数来采样纹理的颜色,它第一个参数是纹理采样器,第二个参数是对应的纹理坐标。texture
函数会使用之前设置的纹理参数对相应的颜色值进行采样。这个片段着色器的输出就是纹理的(插值)纹理坐标上的(过滤后的)颜色。
texture
函数可以使用采样器分别从他们的纹理单元上采样纹理
这里出现了一个新名词:纹理单元
如果我们要渲染纹理,我们首先要在渲染指令中激活纹理单元,激活纹理单元后,再绑定纹理。
这里我们首先要解决一个问题,那就是,上面这两个采样器如何知道他们的纹理单元分别是谁?
代码如下
ourShader.use(); // don't forget to activate/use the shader before setting uniforms!
// either set it manually like so:
ourShader.setInt("texture1",0);
// or set it via the texture1 class
ourShader.setInt("texture2", 1);
其实我们已经发现了,这两个采样器都有uniform
修饰,说明他们是全局变量,通过函数,glUniform1i
可以告诉这采样器他们属于哪个纹理单元。
glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0);
这里我们已经将上面这行代码封装到了ourShader对象的函数里,这里仅做展示。
这样一来,采样器就知道他们分别属于哪个纹理单元了。
到最后了,最后,我们只需要激活对应的纹理单元,在绑定之前的两个纹理对象到纹理单元上即可
所谓的纹理单元在这里就是:
GL_TEXTURE0
和GL_TEXTURE1
OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0
到GL_TEXTRUE15
。它们都是按顺序定义的,所以我们也可以通过GL_TEXTURE0 + 8的方式获得GL_TEXTURE8,这在当我们需要循环一些纹理单元的时候会很有用。
// bind Texture
// bind textures on corresponding texture1 units
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,texture2);
如果你只有一个纹理,就不用激活了,因为纹理单元GL_TEXTURE0
是默认激活的。也不用告诉OpenGL纹理采样器是属于哪个纹理单元的,因为就只有一个。
最后渲染他们即可
// render container
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
完整代码
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <myShader.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb_image.h>
#include <iostream>
void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
// settings
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
int main()
{
#pragma region glfw
// glfw: initialize and configure
// ------------------------------
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
// --------------------
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
// glad: load all OpenGL function pointers
// ---------------------------------------
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
#pragma endregion
// build and compile our shader zprogram
// ------------------------------------
Shader ourShader("path/to/shaders/shader.vs", "path/to/shaders/shader.fs");
// set up vertex data (and buffer(s)) and configure vertex attributes
// ------------------------------------------------------------------
float vertices[] = {
// positions // colors // texture1 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);
// texture1 coord attribute
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
// load and create a texture1
// -------------------------
//创建纹理ID
unsigned int texture1;
//生成纹理对象,并存储在texture中
glGenTextures(1, &texture1);
//绑定当前的纹理
glBindTexture(GL_TEXTURE_2D,texture1); // all upcoming GL_TEXTURE_2D operations now have effect on this texture1 object
// 为当前绑定的纹理对象设置环绕、过滤方式
// set the texture1 wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture1 wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 为当前绑定的纹理对象设置环绕、过滤方式
// set texture1 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 texture1 and generate mipmaps
int width, height, nrChannels;
// 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("Texture/container.jpeg", &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 texture1" << std::endl;
}
//释放图像内存
stbi_image_free(data);
stbi_set_flip_vertically_on_load(true);
// load and create a texture1
// -------------------------
//创建纹理ID
unsigned int texture2;
//生成纹理对象,并存储在texture中
glGenTextures(1, &texture2);
//绑定当前的纹理
glBindTexture(GL_TEXTURE_2D,texture2); // all upcoming GL_TEXTURE_2D operations now have effect on this texture1 object
// 为当前绑定的纹理对象设置环绕、过滤方式
// set the texture1 wrapping parameters
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); // set texture1 wrapping to GL_REPEAT (default wrapping method)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// 为当前绑定的纹理对象设置环绕、过滤方式
// set texture1 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 texture1 and generate mipmaps
// 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.
data = stbi_load("Texture/awesomeface.png", &width, &height, &nrChannels, 0);
if (data)
{
//生成纹理
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 texture1" << std::endl;
}
//释放图像内存
stbi_image_free(data);
// tell opengl for each sampler to which texture1 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:
ourShader.setInt("texture1",0);
// or set it via the texture1 class
ourShader.setInt("texture2", 1);
// render loop
// -----------
while (!glfwWindowShouldClose(window))
{
// input
// -----
processInput(window);
// render
// ------
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
stbi_set_flip_vertically_on_load(true);
// bind Texture
// bind textures on corresponding texture1 units
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,texture1);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D,texture2);
// render container
ourShader.use();
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)
// -------------------------------------------------------------------------------
glfwSwapBuffers(window);
glfwPollEvents();
}
// optional: de-allocate all resources once they've outlived their purpose:
// ------------------------------------------------------------------------
glDeleteVertexArrays(1, &VAO);
glDeleteBuffers(1, &VBO);
glDeleteBuffers(1, &EBO);
// glfw: terminate, clearing all previously allocated GLFW resources.
// ------------------------------------------------------------------
glfwTerminate();
return 0;
}
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
// ---------------------------------------------------------------------------------------------------------
void processInput(GLFWwindow *window)
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, true);
}
// glfw: whenever the window size changed (by OS or user resize) this callback function executes
// ---------------------------------------------------------------------------------------------
void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{
// make sure the viewport matches the new window dimensions; note that width and
// height will be significantly larger than specified on retina displays.
glViewport(0, 0, width, height);
}
纹理颜色与顶点颜色的混合
这个很简单
之前已经获得了顶点颜色,只不过没用而已
这里可以直接将颜色值转化为vec4类型,并想乘即可。
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture sampler
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.5)*vec4(ourColor,1.0);
}
// load and create a texture
// -------------------------
unsigned int texture1, texture2;
// texture 1
// ---------
glGenTextures(1, &texture1);
glBindTexture(GL_TEXTURE_2D, texture1);
// 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("resources/Texture/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);
// 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("Texture/awesomeface.png", &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);
图像倒转
在倒转的纹理对象创建之前加入这行代码即可
stbi_set_flip_vertically_on_load(true);
对于存在透明图层的图片的特殊处理
在生成纹理的时候,要使用GL_RGBA而不是RGB,否则会出现问题
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);