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_TEXTURE0GL_TEXTURE1

OpenGL至少保证有16个纹理单元供你使用,也就是说你可以激活从GL_TEXTURE0GL_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);


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部