ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

C-4:对正方体加载纹理

2022-01-02 16:02:50  阅读:222  来源: 互联网

标签:正方体 1.0 0.0 0.5 TEXTURE 纹理 GL 加载


C-4. 对正方体加载纹理


作业要求:
1、使用三种纹理过滤方式加载纹理(线性采样、mipmap 和最近点采样);
2、加载纹理的图片可以自己设定;
3、各个面的纹理不同;
4、鼠标或者键盘控制纹理过滤方式的切换。

初始化OpenGL

同之前C-2和C-3的报告。

准备数据

建立顶点数组对象。同时注册顶点的位置属性和纹理坐标属性。属性的序号值指明了数据的存放位置,以在着色器中通过对应位置找到位置和纹理坐标数据。
顶点的position属性满足题设的立方体要求。在后面渲染时使用glDrawArrays(GL_TRIANGLES, 0, 36)来绘制每个三角形,即不同的三角形面片之间不复用顶点数据。

	float vertices[] = {
         // positions          // texture coords
        -0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  1.0f, 0.0f,

        0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
        0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
        
        -0.5f, -0.5f,  0.5f, 0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, 0.0f, 0.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f, 0.0f, 1.0f,
         0.5f, -0.5f, -0.5f, 1.0f, 0.0f,
         0.5f, -0.5f,  0.5f, 1.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };

    // first, configure the pyramid's VAO, VBO, EBO
    unsigned int VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    // position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // texture attribute
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);

    glBindVertexArray(0);//unbind

纹理图片准备

根据作者本人喜好,采用了如下的6张贴图。

图1
图2
图3
图4
图5
图6

使用stb_image.h库加载纹理。通过glTexParameteri设置纹理的wrapfilter参数。wrap参数选择GL_REPEAT使得纹理坐标超出[0, 1]范围时按周期性重复采样,另外有GL_CLAMP_TO_EDGE选项使超出范围的采样延拓到边缘。初始化加载纹理时filter参数都先采用GL_LINEAR的过滤方式。具体根据用户输入而切换的控制放在渲染循环中。stbi_load读入图片的数据后,根据图片的分量通道数通过glTexImage2D生成纹理。

	GLuint textures[6];
    const char * textPaths[] = {
        "../resources/gantayipao.jpg",
        "../resources/jingrui.jpg",
        "../resources/kaipao.jpg",
        "../resources/kongerlengzi.jpg",
        "../resources/tiancai.jpg",
        "../resources/yidalipao.jpg"
    };
    // glGenTextures(6, textures);
    for (int i = 0; i < 6; i++) {//初始化加载纹理时都采用GL_LINEAR的过滤方式
        glGenTextures(1, &textures[i]);
        glBindTexture(GL_TEXTURE_2D, textures[i]);
        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);
        stbi_set_flip_vertically_on_load(true);
        int width, height, nrChannels;
        unsigned char *data = stbi_load(textPaths[i], &width, &height, &nrChannels, 0);
        if (data) {
            GLenum format;
            if (nrChannels == 1)
                format = GL_RED;
            else if (nrChannels == 3)
                format = GL_RGB;
            else if (nrChannels == 4)
                format = GL_RGBA;

            glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
            glGenerateMipmap(GL_TEXTURE_2D);
            stbi_image_free(data);
        } else {
            std::cout << "Texture failed to load at path: " << textPaths[i] << std::endl;
            stbi_image_free(data);
        }
    }

建立着色器

由于使用纹理贴图,不涉及任何光照,因此顶点着色器和片段着色器都非常简单。只需要在顶点着色器中读入纹理坐标并传送给片段着色器即可。

顶点着色器

#version 460 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * model * vec4(aPos, 1.0);
	TexCoord = vec2(aTexCoord.x, aTexCoord.y);
}

片段着色器

#version 460 core
out vec4 FragColor;

in vec2 TexCoord;

// texture samplers
uniform sampler2D texture;

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

纹理过滤方式的切换

通过在process_input函数中接受键盘输入的信号而改变变量filter_mode的值,

void processInput(GLFWwindow *window) {
    ... ...
    if (glfwGetKey(window, GLFW_KEY_0) == GLFW_PRESS)
        filter_mode = LINEAR;
    else if (glfwGetKey(window, GLFW_KEY_1) == GLFW_PRESS)
        filter_mode = MIPMAP;
    else if (glfwGetKey(window, GLFW_KEY_2) == GLFW_PRESS)
        filter_mode = NEAREAST;
}

进而在渲染循环中,对每张纹理图修改filter参数。需要注意的是,利用glTexParameteri修改时需要先用glBindTexture绑定到GL_TEXTURE_2D纹理的位置上。需要注意的是,LearnOpenGL指出:

一个常见的错误是,将放大过滤的选项设置为多级渐远纹理过滤选项之一。这样没有任何效果,因为多级渐远纹理主要是使用在纹理被缩小的情况下的:纹理放大不会使用多级渐远纹理,为放大过滤设置多级渐远纹理的选项会产生一个GL_INVALID_ENUM错误代码。

因此选择MIPMAP选项时,只将缩小过滤设置为多级,而放大过滤仍采用GL_LINEAR

	switch (filter_mode)
    {
    case LINEAR:
    for (int i = 0; i < 6; i++) {
        glBindTexture(GL_TEXTURE_2D, textures[i]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }
    break;
	case NEAREAST:
	    for (int i = 0; i < 6; i++) {
	        glBindTexture(GL_TEXTURE_2D, textures[i]);
	        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	    }
	    break;
	case MIPMAP:
	    for (int i = 0; i < 6; i++) {
	        glBindTexture(GL_TEXTURE_2D, textures[i]);
	        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
	        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	    }
	    break;
    default:
        std::cout << "invalid filter_mode of " << filter_mode << std::endl;
        break;
    }

渲染纹理

在渲染循环中,除了需要设定着色器的三个坐标变换的矩阵,还要通过setInt("texture", 0)设定片段着色器中的sampler2D值:uniform sampler2D texture,这个数需要与后续激活的纹理单元号保持一致glActiveTexture(GL_TEXTURE0)(不显式激活时默认为0),片段着色器才能正确地采样到纹理。

ourShader.use();
ourShader.setInt("texture", 0);
ourShader.setMat4("model", glm::mat4(1.0f));
ourShader.setMat4("view", camera.GetViewMatrix());
ourShader.setMat4("projection", glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f));

glBindVertexArray(VAO);
//依次画两个三角形(六个顶点)
for (int i = 0; i < 6; i++) {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, textures[i]);// bind corresponding texture
    glDrawArrays(GL_TRIANGLES, 6*i, 6);
}

效果图

整体效果图如下所示。

图1
图2

在放大到超出原有图片分辨率的情况下,观察不同纹理过滤方式的效果,如下图所示。

  • 线性采样
    linear

  • mipmap
    mipmap

  • 最近点采样
    nearest

标签:正方体,1.0,0.0,0.5,TEXTURE,纹理,GL,加载
来源: https://blog.csdn.net/weixin_43614211/article/details/121398669

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有