OpenGL-04-01纹理
OpenGL-04-01纹理
原文:https://learnopengl-cn.github.io/01%20Getting%20started/06%20Textures/#_5
本文只是对原文的个人总结与简化
在程序最前端加上这两行代码
#define STB_IMAGE_IMPLEMENTATION
#include
接下来我们创建纹理对象,为了方便解释,我们创建两个纹理对象
创建纹理
创建第一个纹理对象
和之前的OpenGL对象一样,纹理也是使用ID引用的
创建纹理对象
// load and create a texture1// -------------------------//创建纹理IDunsigned 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 parametersglTexParameteri(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 parametersglTexParameteri(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 mipmapsint 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// -------------------------//创建纹理IDunsigned 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 parametersglTexParameteri(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 parametersglTexParameteri(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 attributeglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// color attributeglVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// texture1 coord attributeglVertexAttribPointer(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 classourShader.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 unitsglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D,texture1);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D,texture2);
如果你只有一个纹理,就不用激活了,因为纹理单元GL_TEXTURE0是默认激活的。也不用告诉OpenGL纹理采样器是属于哪个纹理单元的,因为就只有一个。
最后渲染他们即可
// render containerourShader.use();glBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
完整代码
#include
#include
#include
#define STB_IMAGE_IMPLEMENTATION
#include #include 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 coords0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // top right0.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 triangle1, 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 attributeglVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// color attributeglVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// texture1 coord attributeglVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));glEnableVertexAttribArray(2);// load and create a texture1// -------------------------//创建纹理IDunsigned 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 parametersglTexParameteri(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 parametersglTexParameteri(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 mipmapsint 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// -------------------------//创建纹理IDunsigned 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 parametersglTexParameteri(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 parametersglTexParameteri(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 classourShader.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 unitsglActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D,texture1);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D,texture2);// render containerourShader.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 parametersglTexParameteri(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 parametersglTexParameteri(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 mipmapsint 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 parametersglTexParameteri(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 parametersglTexParameteri(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 mipmapsdata = 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_RGBAglTexImage2D(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);
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
