当前位置: 首页 > news >正文

opengl——贴图

原文地址:https://www.haroldserrano.com/blog/a-brief-talk-about-opengl-textures

创建和初始化纹理
贴图在OpenGL中代表了图片,它包含了一些特性。比如,下面的图,一个是没有贴图的一个是有贴图的。
在这里插入图片描述

在OpenGL中,有两种存储方式:
1.缓冲
2.贴图

缓冲是未定义类型的线性结构的数据,可以被视为通常的内存分配。贴图是多维度的数据,比如图片。
在这里插入图片描述

在OpenGL中,属性数据如下:
1.顶点位置
2.法线
3.u-v坐标

这些都存储在OpenGL的缓冲中。相反的,图片数据则是存储在OpenGL的贴图对象中。

在这里插入图片描述

为了存储图片数据到纹理对象,你要按照如下的步骤:
1.创建纹理对象
2.分配纹理内存
3.绑定纹理对象

我们使用如下的函数去创建、绑定、分配内存。
清单1:

//创建纹理对象
glCreateTextures(...);

//分配内存
glTexStorage2D(...);

//绑定它到GL_Texture_2D目标
glBindTexture(...);

上面是程序片段,用来展示如何创建纹理对象、分配存储空间、绑定它到OpenGL上下文。

清单2:

//OpengL中使用名字的类型为GLuint
GLuint texture;

//创建2D的纹理对象
glCreateTextures(GL_TEXTURE_2D,1,&texture);

//定义贴图的使用内存大小
glTextureStorage2D(texture,    //纹理对象
                   1,          //1 mimap 等级
                   GL_RGBA32F, //32位浮点数RGBA数据
                   256,256);   //256 x 256 texels

//绑定它到OpenGL上下文,使用的是GL_TEXTURE_2D作为绑定点
glBindTexture(GL_TEXTURE_2D, texture);

在这个纹理对象绑定之后,你可以加载数据到纹理。数据加载的方式如下:

清单3:

glTexSubImage2D(...)

清单4:

//定义一些数据
float *data=new float[256*256*4];

glTexSubImage2D(texture,  //纹理对象
                0,        //level 0
                0,0,      //offset 0,0
                256,256,  //256 x 256 texels
                GL_RGBA,  //Four channel data
                GL_FLOAT, //Floating point data
                data);    //Pointer to data

纹理对象和类型

在理解OpenGL纹理的时候遇到的第一个问题是,什么是目标。事实证明,目标是非常简单和直接的。一个纹理目标决定了纹理对象的类型。

比如,纹理以2D纹理创建。因此,纹理对象绑定到2D纹理目标,GL_TEXTURE_2D。如果有一个1D的纹理,你就要把它绑定到1D纹理目标,GL_TEXTURE_1D。

由于你要使用多个图片,你会发现经常使用的是GL_TEXTURE_2D类型的。这是因为图片是用2D数据展示的。

从shaders中读取纹理

一旦纹理绑定了,且包含了数据,它就可以被shader使用。在shader中,纹理是作为全局Sampler变量的。
在这里插入图片描述

比如:
清单5:

uniform sampler2D myTexture;

采样器的维度和纹理的维度有关。我们的纹理是2D的,所以要指定为2D的采样器,那就是sampler2D。

如果你的纹理是1D的,采样器就是sampler1D的。如果纹理是3D的,那么采样器就是sampler3D。

纹理坐标

采样器代表了纹理以及采样参数。纹理坐标从0.0到1.0。这个两个坐标在应用纹理到物体所必须的。你已经知道如何在shader中代表纹理了,使用的是sampler。但是怎样在shader中得到纹理坐标呢?

属性数据是通过OpenGL的缓冲传递给GPU的。你加载属性数据。这些属性可以是顶点的位置、法线、纹理坐标。纹理坐标也称为uv坐标。

顶点着色器从顶点属性接收信息。只有顶点着色器可以接收属性数据。细分曲面、几何和片段着色器都不能接收属性数据。如果这些着色器需要数据,你必须从顶点着色器开始向他们传递。

在这里插入图片描述

因此,顶点着色器从顶点属性中接收纹理坐标。然后由顶点着色器向片段着色器传递。一旦这个坐标传递到了片段着色器,OpenGL就可以应用纹理到物体了。

清单6展示纹理坐标作为顶点的属性。在main函数中,这个坐标未经变换直接传递给片段着色器。

清单6:

#version 450 core
//uniform variables
uniform mat4 mvMatrix;
uniform mat4 projMatrix;

//Vertex Attributes for position and UV coordinates
layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texCoordinates;

//output to fragment shader
out TextureCoordinates_Out{
    vec2 texCoordinates;
}textureCoordinates_out;

void main(){
//计算每个顶点的裁剪空间坐标
vec4 position_vertex=mvMatrix*position;

//未修改直接传出
textureCoordinates_out.texCoordinates=texCoordinates;

gl_Position=projMatrix*position_vertex;

}

清单7展示了如何使用texture()函数,它使用纹理坐标采样纹理,然后应用到一个像素。
清单7:

#version 450 core
//Sampler2D declaration
layout(binding = 0) uniform sampler2D textureObject;

//Input from vertex shader
in TextureCoordinates_Out{
    vec2 textureCoordinates;
}textureCoordinates_in;

//Output to framebuffer
out vec4 color;

void main(){
//使用纹理坐标和采样器,应用纹理,得到颜色
color=texture(textureObject,textureCoordinates_in.textureCoordinates;
}

控制纹理数据如何读取
采样器在纹理单元中找到纹理数据并且遍历。纹理单元包含了一个纹理对象和一个采样对象。你已经知道纹理对象是什么了,现在我们来讨论下采样对象吧。

纹理坐标在0.0和1.0之间。OpenGL让我们自己决定对于那些超出了这个范围的坐标怎么办。这叫包围模式。OpenGL同样让我们自己决定对于不是1比1的比例,怎么处理。这个叫做过滤模式。一个采样器存储了包围模式和过滤模式这些参数。

在这里插入图片描述

一个采样器需要纹理对象和采样器对象,他们都要绑定到纹理单元。当这个数据集合是完整的,采样才能能正常应用一个纹理。

在这里插入图片描述

在特殊情况下,你将会创建一个采样器对象、然后把它绑定到纹理单元。但是更多的情况下,你不需要创建一个采样器对象。这是因为纹理对象有一个默认的采样器对象,你可以使用这个默认的。默认的采样器对象有默认的包围/过滤模式参数配置。

为了访问存储在纹理对象中的默认的采样器对象,你可以调用:
清单8:

//accessing the default sampler object in a texture object and setting the sampling parameters
glTexParameter()

在绑定纹理对象到纹理单元之前,你必须激活纹理单元。这个需要调用下面的函数,来激活指定的纹理单元
清单9:

//Activate Texture Unit 0
glActiveTexture(GL_TEXTURE0);

相关文章:

  • learnopengl——Framebuffers
  • learnopengl——纹理
  • [转帖]Application Request Route实现IIS Server Farms集群负载详解
  • learnopengl——三角形
  • songho——OpenGL的帧缓冲
  • while 循环 及 and or not
  • songho——OpenGL的顶点缓冲对象
  • OGL(教程35)——延迟渲染1——代码结构梳理
  • 安装MySQL_安装Navicat_启动数据库服务
  • android studio查看android手机日志
  • Python02(Linux命令)
  • luaforwindws安装与调用方式
  • vs添加链接文件
  • httpclient
  • 继成ScriptableObject的静态类对象竟然没有销毁
  • 【140天】尚学堂高淇Java300集视频精华笔记(86-87)
  • 【347天】每日项目总结系列085(2018.01.18)
  • 【知识碎片】第三方登录弹窗效果
  • C++类的相互关联
  • interface和setter,getter
  • Java反射-动态类加载和重新加载
  • Mithril.js 入门介绍
  • Python学习笔记 字符串拼接
  • Redis 懒删除(lazy free)简史
  • 订阅Forge Viewer所有的事件
  • 对话 CTO〡听神策数据 CTO 曹犟描绘数据分析行业的无限可能
  • 技术攻略】php设计模式(一):简介及创建型模式
  • 将回调地狱按在地上摩擦的Promise
  • 那些被忽略的 JavaScript 数组方法细节
  • 长三角G60科创走廊智能驾驶产业联盟揭牌成立,近80家企业助力智能驾驶行业发展 ...
  • ​七周四次课(5月9日)iptables filter表案例、iptables nat表应用
  • #define、const、typedef的差别
  • #Z0458. 树的中心2
  • (13)[Xamarin.Android] 不同分辨率下的图片使用概论
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (9)目标检测_SSD的原理
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (rabbitmq的高级特性)消息可靠性
  • (附源码)springboot工单管理系统 毕业设计 964158
  • (力扣题库)跳跃游戏II(c++)
  • (排序详解之 堆排序)
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)从零实现3D图像引擎:(8)参数化直线与3D平面函数库
  • .net 4.0 A potentially dangerous Request.Form value was detected from the client 的解决方案
  • .net core开源商城系统源码,支持可视化布局小程序
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • @AliasFor注解
  • @converter 只能用mysql吗_python-MySQLConverter对象没有mysql-connector属性’...
  • [ vulhub漏洞复现篇 ] Hadoop-yarn-RPC 未授权访问漏洞复现
  • [2016.7.test1] T2 偷天换日 [codevs 1163 访问艺术馆(类似)]
  • [20171106]配置客户端连接注意.txt
  • [CISCN2019 华东南赛区]Web11
  • [CTO札记]盛大文学公司名称对联
  • [Docker]六.Docker自动部署nodejs以及golang项目
  • [Docker]三.Docker 部署nginx,以及映射端口,挂载数据卷