首页 > 其他分享 >OpenGL入门——多个纹理

OpenGL入门——多个纹理

时间:2023-09-25 22:13:45浏览次数:42  
标签:1.0 入门 OpenGL 0.0 TEXTURE 纹理 2D GL

上一节OpenGL入门——纹理 - 一只小瓶子 - 博客园 (cnblogs.com)中介绍了怎么使用纹理,实际使用过程中可能会用到多个纹理。跟顶点属性一样(顶点对象可以有多个属性,每个属性都有一个位置值(layout)),纹理采样器也有一个位置值(纹理单元),OpenGL中至少有16个纹理单元,从GL_TEXTURE0到GL_TEXTURE15,可以通过GL_TEXTURE0+8的方式获得GL_TEXTURE8(循环中很有用)。

 

多个纹理采样器在片段着色器中的使用如下,声明多个采样器

//fragment shader source
#version 330 core
in vec3 vertexColor;    //顶点颜色
in vec2 textureCoord;    //顶点对应纹理坐标

out vec4 fragColor;        //像素的最终颜色

uniform sampler2D ourTexture0;    //纹理采样器,通过源码中绑定纹理glBindTexture赋值
uniform sampler2D ourTexture1;    

void main()
{
    //fragColor = texture(ourTexture1, textureCoord);//赋值为ourTexture1
    //fragColor = texture(ourTexture0, textureCoord)*vec4(vertexColor, 1.0);//纹理与颜色混合
    //如果第三个值是0.0,它会返回第一个输入;如果是1.0,会返回第二个输入值。0.2会返回80%的第一个输入颜色和20%的第二个输入颜色
    fragColor = mix(texture(ourTexture0, textureCoord), texture(ourTexture1, textureCoord), 0.2);//混合纹理
}

 

怎么将采样器和纹理对应起来呢?片段着色器中的采样器类型是uniform,说明是在程序中定义赋值。

    shader.run();//设置uniform值之前必须激活程序
    //设置纹理采样器对应哪个纹理单元,如果只有一个纹理单元无须设置,默认为0单元
    shader.setUniformInt("ourTexture0", 0);
    shader.setUniformInt("ourTexture1", 1);

为什么上一节我们没有在程序中设置这个纹理单元呢?因为当只有一个纹理的时候默认纹理单元是0,它是默认的激活纹理单元,所以上一节我们没有给它分配一个位置值。

 

最后在绘制前绑定所需的纹理即可,记住绑定前应先激活对应纹理单元

//绘制前,激活纹理单元0和1,如果只有一个纹理单元默认自动激活
        glActiveTexture(GL_TEXTURE0);//先激活对应的纹理单元
        glBindTexture(GL_TEXTURE_2D, texture0);//再绑定对应的纹理
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture1);

 

 

完整示例

int texture_triangle()
{
    ///初始化窗口
    GLFWwindow* window = init_window();

    ///定义着色器
    CShader shader("texture_triangle.vs", "texture_triangle.fs");

    ///定义顶点对象
    float vertices[] = {
        // 顶点坐标X,Y,Z      // 顶点颜色R,G,B    // 纹理坐标S,T
         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  // 左上
    };
    unsigned int indices[] = {
        0, 1, 3, // 第一个三角形
        1, 2, 3  // 第二个三角形
    };

    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);

    // 顶点坐标属性
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);
    // 顶点颜色属性
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
    glEnableVertexAttribArray(1);
    // 纹理坐标属性
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
    glEnableVertexAttribArray(2);

    //创建纹理,和之前生成的OpenGL对象一样,纹理也是使用ID引用的
    unsigned int texture0;
    glGenTextures(1, &texture0);//生成纹理的数量1,然后把它们储存在第二个参数的unsigned int数组中

    glBindTexture(GL_TEXTURE_2D, texture0);//绑定纹理

    //为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//X轴环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);//Y轴环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//被缩小时的过滤选项
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//被放大时的过滤选项

    //加载图像前进行翻转,因为图像的原点在左上角,OpenGL的原点在左下角
    stbi_set_flip_vertically_on_load(true);

    //加载纹理图像0
    int width, height, nrChannels;
    unsigned char* data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
    if (data)
    {
        //生成纹理,当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像
        /*
        第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
        第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
        第三个参数告诉OpenGL我们希望把纹理储存为何种格式。我们的图像只有RGB值,因此我们也把纹理储存为RGB值。
        第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
        下个参数应该总是被设为0(历史遗留的问题)。
        第七第八个参数定义了源图的格式和数据类型。我们使用RGB值加载这个图像,并把它们储存为char(byte)数组,我们将会传入对应值。
        最后一个参数是真正的图像数据。
        */
        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);

    //创建纹理
    unsigned int texture1;
    glGenTextures(1, &texture1);//生成纹理的数量1,然后把它们储存在第二个参数的unsigned int数组中

    glBindTexture(GL_TEXTURE_2D, texture1);//绑定纹理

    //为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);//X轴环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);//Y轴环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//被缩小时的过滤选项
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);//被放大时的过滤选项

    //加载纹理图像1
    data = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
    if (data)
    {
        //生成纹理,当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 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);

    shader.run();//设置uniform值之前必须激活程序
    //设置纹理采样器对应哪个纹理单元,如果只有一个纹理单元无须设置,默认为0单元
    shader.setUniformInt("ourTexture0", 0);
    shader.setUniformInt("ourTexture1", 1);

    while (!glfwWindowShouldClose(window))
    {
        processInput(window);


        //清空屏幕
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        //绘制前,激活纹理单元0和1,如果只有一个纹理单元默认自动激活
        glActiveTexture(GL_TEXTURE0);//先激活对应的纹理单元
        glBindTexture(GL_TEXTURE_2D, texture0);//再绑定对应的纹理
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture1);

        ///绘制物体
        shader.run();

        glBindVertexArray(VAO);
        //使用VEO绘制
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);//绘制图元为三角形,绘制顶点数量6,索引类型uint,偏移量0

        glfwSwapBuffers(window);//交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲)
        glfwPollEvents();//检查有没有触发什么事件
    }

    //释放资源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);

    glfwTerminate();//释放/删除之前的分配的所有资源
    return 0;
}

 

运行效果

 附上纹理图像下载地址container.jpg (512×512) (learnopengl-cn.github.io) 和awesomeface.png (512×512) (learnopengl-cn.github.io)

 

标签:1.0,入门,OpenGL,0.0,TEXTURE,纹理,2D,GL
From: https://www.cnblogs.com/ping-code/p/17728967.html

相关文章

  • Learn Git in 30 days—— 第 24 天:使用 GitHub 远端仓库 - 入门篇
    写的非常好的一个Git系列文章,强烈推荐原文链接:https://github.com/doggy8088/Learn-Git-in-30-days/tree/master/zh-cn GitHub是目前全世界最多人采用的Git线上管理平台,他包含了完整的Git远端仓库实作,还有完整的议题追踪机制与报表,更有成千上万的开源码项目都在GitHub......
  • Redis之Lua语言入门
    前言Redis通过lua脚本来支持多条语句的原子性。Linux下安装#下载压缩包curl-R-Ohttp://www.lua.org/ftp/lua-5.4.3.tar.gz#解压tar-zxvflua-5.4.3.tar.gz#进入解压目录cdlua-5.4.3#编译makelinuxtest#安装,此步骤也可以省略makeinstall运行my......
  • docker基础命令快速入门
    docker快速入门Docker是一个虚拟环境容器,可以将你的开发环境、代码、配置文件等一并打包到这个容器中,并发布和应用到任意平台中。Docker的三个概念镜像Docker镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的配置参数。......
  • 推荐源哥和川川的新书:《Pyhton网络爬虫从入门到实战》
    ❤️作者主页:小虚竹❤️作者简介:大家好,我是小虚竹。2022年度博客之星评选TOP10......
  • 【Python入门教程】Python实现猜数字小游戏
    ​    今天跟大家分享一下很久之前自己做的一款猜数字小游戏,基本的循环判断语句即可实现,可以用来当练手或者消磨时间用。    大家在编代码的时候最重要就是先理清逻辑思路,例如应该套几层循环、分几个模块等等。然后在编码时可以先随意一点,变量名、函数等可以先......
  • 入门篇-其之四-字符串String的简单使用
    什么是字符串?在Java编程语言中,字符串用于表示文本数据。字符串(String)属于引用数据类型,根据String的源码,其头部使用class进行修饰,属于类,即引用数据类型。字符串的表示字符串使用双引号""表示,在双引号中你可以写任意字符。和前面定义并初始化基本数据类型的变量一样,定义最简单......
  • 安卓逆向 -- 入门Smali语言
    在Android应用程序逆向工程和安全研究中,了解Smali语言是非常重要的。Smali是一种用于Android应用程序的反汇编和反编译的语言,允许您分析、修改和定制应用程序的行为。本博客将带您深入了解Smali语言的基础知识和技术,同时提供一些实际的代码案例。一、什么是Smali语言?Smali是一种基......
  • 3、深度学习入门之数值微分P94、P95、P96、P97、P98
    $f'(x)=\lim_{{\Deltax\to0}}\frac{f(x0+\Deltax)-f(x0)}{\Deltax}$$f(x0+\Deltax)-f(x0)=\Deltay$$f'(x) =\lim_{{\Deltax\to0}}\frac{\Deltay}{\Deltax}$$f'(x)$就是导数导数粗糙的理解为就是在某点的切线斜率可导意味着在某点处它的导数存在 ......
  • 深度学习入门——卷积神经网络CNN基本原理+实战
    beginning今天给小伙伴们介绍一个高级的分类方法——卷积神经网络CNN,并学习用CNN实现图像的分类。作为深度学习的基础,CNN可太重要了呐,在图像分类、目标检测、目标跟踪、语义分割、实例分割等领域随处可见它的身影。废话不多说啦,如果你也对CNN感兴趣的话,赶紧跟我一起愉快的看下去叭......
  • 深度学习入门——卷积神经网络CNN基本原理+实战
    beginning今天给小伙伴们介绍一个高级的分类方法——卷积神经网络CNN,并学习用CNN实现图像的分类。作为深度学习的基础,CNN可太重要了呐,在图像分类、目标检测、目标跟踪、语义分割、实例分割等领域随处可见它的身影。废话不多说啦,如果你也对CNN感兴趣的话,赶紧跟我一起愉快的看下去叭......