LearnOpenGL-入门-纹理

news/2024/4/23 16:23:28/文章来源:https://blog.csdn.net/qq_34060370/article/details/129227826

本人刚学OpenGL不久且自学,文中定有代码、术语等错误,欢迎指正

我写的项目地址:https://github.com/liujianjie/LearnOpenGLProject

LearnOpenGL中文官网:https://learnopengl-cn.github.io/

文章目录

  • 纹理
    • 纹理环绕方式
    • 纹理过滤
    • 多级渐远纹理
  • 加载与创建纹理
    • stb_image.h
    • 生成纹理
  • 应用纹理
    • 给顶点数据添加纹理坐标
    • 对应的glsl
    • 绑定纹理和绘画
    • 效果
  • 纹理单元

纹理

  • 简介

    • 若给每个顶点添加颜色来增加图形的细节,会增加开销,所以用纹理。

    • 纹理是一个2D图片,可以认为纹理附在物体表面上,其实是根据当前片段的uv值,采样纹理- 的颜色值作为当前片段的颜色。

  • 纹理坐标

    • 每个顶点就会关联着一个纹理坐标(Texture Coordinate),用来标明该从纹理图像的哪个部分采样

      三角形有三个顶点,对应3个纹理坐标。

    • 使用纹理坐标获取纹理颜色叫做采样(Sampling)

    • 重点理解

      三角形只设置3个顶点的纹理坐标就行了,接下来它们会被传片段着色器中,它会为每个片段进行纹理坐标的插值

      比如下面那条线(0,0)-(1.0)的各个像素点,会变插值成为浮点小数,从而形成小数点的纹理坐标

纹理环绕方式

纹理坐标的范围通常是从(0, 0)到(1, 1),那如果我们把纹理坐标设置在范围之外

环绕方式描述
GL_REPEAT对纹理的默认行为。重复纹理图像。
GL_MIRRORED_REPEAT和GL_REPEAT一样,但每次重复图片是镜像放置的。
GL_CLAMP_TO_EDGE纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。
GL_CLAMP_TO_BORDER超出的坐标为用户指定的边缘颜色。

当纹理坐标超出默认范围时,每个选项都有不同的视觉效果输出

前面提到的每个选项都可以使用glTexParameter*函数对单独的一个坐标轴设置(st(如果是使用3D纹理那么还有一个r)它们和xyz是等价的):

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

纹理过滤

  • 简介

    纹理坐标不依赖于分辨率,它可以是任意浮点值,所以OpenGL需要知道怎样将纹理像素映射到纹理坐标。

    对应前面说的:下面那条线(0,0)-(1.0)的各个像素点,会变插值成为浮点小数,从而形成小数点的纹理坐标

  • 过滤方式

    • GL_NEAREST:邻近过滤,OpenGL会选择中心点最接近纹理坐标的那个像素,左上角那个纹理像素的中心距离纹理坐标最近,所以它会被选择为样本颜色

    • GL_LINEAR:线性过滤,基于纹理坐标附近的纹理像素,计算出一个插值,近似出这些纹理像素之间的颜色。一个纹理像素的中心距离纹理坐标越近,那么这个纹理像素的颜色对最终的样本颜色的贡献越大

  • 对比效果

    • GL_NEARSET:颗粒感
    • GL_LINEAR:更平滑的图案
  • 使用

    • 放大:线性过滤
    • 缩小:邻近过滤
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    

多级渐远纹理

  • 问题引出

    当远处的物体和它的纹理有一样高的分辨率,由于在远处,物体所占的片段较小,OpenGL为这些片段在高分辨率纹理中获得正确的颜色变得困难。所以会不真实,且浪费内存

  • 多级渐远纹理(Mipmap)简介

    它简单来说就是一系列的纹理图像,后一个纹理图像是前一个的二分之一

  • OpenGL提供函数创建Mipmap

    glGenerateMipmaps

  • 过滤方式

    在渲染中切换多级渐远纹理级别(Level)时,OpenGL在两个不同级别的多级渐远纹理层之间会产生不真实的生硬边界

    过滤方式描述
    GL_NEAREST_MIPMAP_NEAREST使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样
    GL_LINEAR_MIPMAP_NEAREST使用最邻近的多级渐远纹理级别,并使用线性插值进行采样
    GL_NEAREST_MIPMAP_LINEAR在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样
    GL_LINEAR_MIPMAP_LINEAR在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    // 放大时,用GL_LINEAR
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    

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

加载与创建纹理

  • 引出

    由于自己写,难,所以使用第三方库stb_image

  • 下载地址

    https://github.com/nothings/stb/blob/master/stb_image.h

stb_image.h

  • 简介

    单头文件图像加载库

  • 使用

    下载好std_image.h后,将它以stb_image.h的名字加入工程中

    新建一个cpp文件,并写入以下代码

    #define STB_IMAGE_IMPLEMENTATION
    #include "stb_image.h"
    

    通过定义STB_IMAGE_IMPLEMENTATION,预处理器会修改头文件,让其只包含相关的函数定义源码,等于是将这个头文件变为一个 .cpp 文件了。

    这个新的cpp文件得放在项目下,不然会报错
    请添加图片描述

  • 具体使用代码

    int width, height, nrChannels;
    unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
    
  • 细节

    stb_image.h能够在图像加载时帮助我们翻转y轴

    stbi_set_flip_vertically_on_load(true);
    

    因为OpenGL要求y轴0.0坐标是在图片的左下角的,但是图片的y轴0.0坐标通常在左上角。

生成纹理

  • 使用代码

    和之前生成的OpenGL对象一样,纹理也是使用ID引用的

    unsigned int texture;
    glGenTextures(1, &texture);
    

    绑定纹理

    glBindTexture(GL_TEXTURE_2D, texture);
    

    使用前面stb_image.h载入的图片数据生成一个纹理

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
    
  • 至此完整代码

    unsigned int texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    // 为当前绑定的纹理对象设置环绕、过滤方式
    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);
    // 加载并生成纹理
    int width, height, nrChannels;
    unsigned char *data = stbi_load("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);
    

应用纹理

给顶点数据添加纹理坐标

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    // 左上
};

告诉OpenGL我们新的顶点格式

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

对应的glsl

#version 330 core
layout (location = 0) in vec3 aPos;// 0 -12
layout (location = 1) in vec3 aColor;// 12-24
layout (location = 2) in vec2 aTexCoord;// 24-32out vec3 ourColor;
out vec2 TexCoord;void main()
{gl_Position = vec4(aPos, 1.0);ourColor = aColor;TexCoord = aTexCoord;
}
#version 330 core
out vec4 FragColor;in vec3 ourColor;
in vec2 TexCoord;uniform sampler2D ourTexture;void main()
{// 只有纹理FragColor = texture(ourTexture, TexCoord);// 纹理和颜色混合// FragColor = texture(texture1, TexCoord) * vec4(ourColor, 1.0);
}
  • texture:来采样纹理的颜色

    第一个参数是纹理采样器,第二个参数是对应的纹理坐标

绑定纹理和绘画

在glDrawElements之前,绑定已加载的纹理到纹理单元0位置,片段着色器的采样器默认位置值为0

glBindTexture(GL_TEXTURE_2D, texture);
glBindVertexArray(VAO);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

效果

  • 单纹理

  • 纹理和颜色混合

纹理单元

  • 引出

    sampler2D变量是uniform,上面却没用glUniform给它赋值。

  • 原因

    上面只有一个纹理,它的默认纹理单元是0,它是默认的激活纹理单元,且glsl的采样器默认位置值为0,所以上面代码没有用glUniform

  • 纹理单元

    • 可以用glUniform1i给纹理采样器分配一个位置值,这样能够在一个片段着色器中设置多个纹理

    • 一个纹理的位置值通常称为一个纹理单元

  • 前提

    激活对应的纹理单元

    glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
    glBindTexture(GL_TEXTURE_2D, texture);
    

    GL_TEXTURE0总是被激活

  • 片段着色器使用两个纹理采样器

    #version 330 core
    ...uniform sampler2D texture1;
    uniform sampler2D texture2;void main()
    {FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
    }
    
    • mix函数说明:
      • 根据第三个参数进行线性插值。
      • 如果第三个值是0.0,它会返回第一个输入;
      • 如果是1.0,会返回第二个输入值。
      • 0.2会返回80%的第一个输入颜色和20%的第二个输入颜色,即返回两个纹理的混合色。

    绑定两个纹理到对应的纹理单元

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture1);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture2);glBindVertexArray(VAO);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    

    glUniform1i设置OpenGL每个着色器采样器属于哪个纹理单元

    ourShader.use(); // 不要忘记在设置uniform变量之前激活着色器程序!
    // 手动设置,采样器texture1的位置值是0
    glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); 
    // 或者使用着色器类设置,采样器texture2的位置值是1
    ourShader.setInt("texture2", 1); 
    // 也是执行:glUniform1i(glGetUniformLocation(ourShader.ID, "texture2"), 1);while(...) 
    {[...]
    }
    
  • 效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_74229.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【java基础】包装类,自动装箱和自动拆箱

文章目录基本介绍包装类自动装箱自动拆箱包装类注意事项包装类比较包装器内容不可变基本介绍 有时&#xff0c;需要将int这样的基本类型转换为对象。所有的基本类型都有一个与之对应的类。 例如&#xff0c;Integer类对应基本类型int。通常&#xff0c;这些类称为包装器&#…

使用file-selector-button美化原生文件上传

前言 你平时见到的上传文件是下面这样的? 还是下面这种美化过的button样式 还是下面这种复杂的上传组件。 <input type="file" >:只要指定的是type类型的input,打开浏览器就是上面第一种原生的浏览器默认的很丑的样式。 下面的两种是我从ElementUI截的图,…

OpenAPI SDK组件介绍

背景 公司成立以来&#xff0c;积累了数以万计的可复用接口。上层的SaaS业务&#xff0c;原则上要复用这些接口开发自己的业务&#xff0c;为了屏蔽调用接口的复杂性&#xff0c;基础服务开发了apisdk组件&#xff0c;定义了一套声明OpenAPI的注解、注解解析器&#xff0c;实例…

scrapy下载图片

&#x1f431; 个人主页&#xff1a;莎萌玩家&#x1f64b;‍♂️ 作者简介&#xff1a;全栈领域新星创作者、专注于全栈各领域技术&#xff0c;共同学习共同进步&#xff0c;一起加油呀&#xff01;&#x1f4ab;系列专栏&#xff1a;网络爬虫、WEB全栈开发&#x1f4e2; 资料…

你应该知道的ChatGPT提示语

ChatGPT 自上线以来&#xff0c;凭借其优异的自然语言理解和输出能力&#xff0c;仅花 5天就成为了活跃用户过百万的现象级产品。而上一个现象级产品 instagram 花了 2 个半月。到目前为止 ChatGPT 在全球累计用户数量已经过亿&#xff0c;相信现在也有很多人在跟 ChatGPT 聊过…

OKR 与 KPI有何异同?各部门OKR实例【小bu】

OKR 与 KPI&#xff0c;如何本土化是关键 近期公司计划对去年实施的绩效考核方案进行优化&#xff0c;公司以往采用 KPI 绩效考核方式&#xff0c;产生了一些争议。一方面&#xff0c;执行期间部分部门一度忽略指标设置的真实目的&#xff0c;导致出现短视思维和行为&#xff1…

TCP协议原理二

文章目录四、滑动窗口二、流量窗口三、拥塞控制四、滑动窗口 前面我们学习了 确认应答&#xff0c;超时重传&#xff0c;连接管理&#xff0c;这些机制都为我们TCP的可靠性提供了保证&#xff0c;当然在保证TCP的可靠性的同时&#xff0c;传输效率也受到了一定的影响&#xff…

05 DC-AC逆变器(DCAC Converter / Inverter)简介

文章目录0、概述逆变原理方波变换阶梯波变换斩控调制方式逆变器分类逆变器波形指标1、方波变换器A 单相单相全桥对称单脉冲调制移相单脉冲调制单相半桥2、方波变换器B 三相180度导通120度导通&#xff08;线、相的关系与180度相反&#xff09;3、阶梯波逆变器独立直流源二极管钳…

BLIP2-图像文本预训练

文章目录摘要解决问题算法模型结构通过frozen图像编码器学习视觉语言表征图像文本对比学习&#xff08;ITC&#xff09;基于图像文本生成&#xff08;ITG&#xff09;图文匹配&#xff08;ITM&#xff09;从大规模语言模型学习视觉到语言生成模型预训练预训练数据预训练图像编码…

Gehpi的网络布局

Gehpi的网络布局1. 力引导布局2. 辅助布局布局是网络可视化中的重要概念&#xff0c;指将点和边通过某种策略进行排布&#xff0c;应尽可能满足以下4个原则&#xff1a; 节点均匀分布在有限的区域内避免边的交叉和弯曲保持边的长度一致整体布局能反映图内在的特性 Gephi的布局…

Vision Transformer学习了什么-WHAT DO VISION TRANSFORMERS LEARN? A VISUAL EXPLORATION

WHAT DO VISION TRANSFORMERS LEARN? A VISUAL EXPLORATION 文章地址 代码地址 摘要 视觉转换器( Vision Transformers&#xff0c;ViTs )正在迅速成为计算机视觉的事实上的架构&#xff0c;但我们对它们为什么工作和学习什么知之甚少。虽然现有研究对卷积神经网络的机制进…

Bunifu.UI.WinForms 6.0.2 Crack

Bunifu.UI.WinForms为 WinForms创建令人惊叹的UI Bunifu.UI.WinForms我们为您提供了现代化的快速用户界面控件。用于 WinForms C# 和 VB.NET 应用程序开发的完美 UI 工具 简单 Bunifu.UI.WinForms没有臃肿的特征。正是您构建令人惊叹的 WinForms 应用程序所需要的。只需拖放然…

JavaSe第3次笔记

1.String str "hello";字符串类型。 2.两个字符串类型相加意思是拼接&#xff0c;类似于c语言里面的strcat函数。 3.整型变成字符串类型: int a 10; String str String. valueOf(a); 4.当字符串和其他类型进行相加的时候&#xff0c;结果就是字符串。(不完全…

MS9132是一款USB 3 0投屏芯片,内部集成USB 3 0 Device控制器、数据收发模块、音视频处理模块

MS9132是一款USB 3.0投屏芯片&#xff0c;内部集成USB 3.0 Device控制器、数据收发模块、音视频处理模块。MS9132可以通过USB 3.0接口将PC、智能手机、平板电脑上的信息显示或扩展到更大尺寸的显示设备&#xff0c;支持HDMI视频接口输出。 主要功能特征 HDMI 1.4b兼容 支持EDI…

RK3568编译Android11和目录讲解

文章目录 前言一、下载android11源码二、环境搭建1.增加交换内存三、编译瑞芯微原厂源码四、目录讲解总结前言 本文记录在Ubuntu18.04中编译Android11,只有编译了源码,后面才能进行驱动的开发,有兴趣的小伙伴可以和我一起学习吧! 提示:以下是本篇文章正文内容,下面案例可…

【华为OD机试模拟题】用 C++ 实现 - 剩余可用字符集 or @分割可用字符集(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 获得完美走位(2023.Q1) 文章目录 最近更新的博客使用说明剩余可用字符集 or @分割可用字符集题目输入输出示例一输入输出说明Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才…

烙铁使用方法

烙铁使用 烙铁是硬件工程师最经常使用的工具之一,一把性能保持良好的烙铁能帮助我们快速进行电路调试。烙铁第一次加热时采用焊锡均匀涂覆在烙铁头上,以便去除包在烙铁头上面的氧化物。在工作中我们需要根据情况选择合适的烙铁头类型,合适的温度进行操作。完成焊接后要在烙铁…

华为OD机试用Python实现 -【贪心的商人 or 最大利润】(2023-Q1 新题)

华为OD机试题 华为OD机试300题大纲贪心的商人 or 最大利润题目描述输入描述输出描述说明示例一输入输出示例二输入输出Python 代码实现华为OD机试300题大纲 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:blog.c…

基于SpringCloud的可靠消息最终一致性01:定理、解决方案和框架

在互联网发展的早期,单体架构是主流的开发模式。因为访问的用户不多,所以整个系统的结构比较简单,就像一口竖井,从上到下,一通到底,如下图所示: 图一:单体应用 随着业务复杂度的不断提升,以及用户需求的不断增加,原来单个的业务系统已经不堪重负了。就好像一个窗口前…

MS9123是一款单芯片USB投屏器,内部集成了USB2 0控制器和数据收发模块、视频DAC和音视频处理模块,MS9123可以通过USB接口显示或者扩展PC、

MS9123是一款单芯片USB投屏器&#xff0c;内部集成了USB2.0控制器和数据收发模块、视频DAC和音视频处理模块&#xff0c;MS9123可以通过USB接口显示或者扩展PC、智能手机、平板电脑的显示信息到更大尺寸的显示设备上&#xff0c;支持CVBS、S-Video视频接口。 主要功能特征 C…