具有纹理对象和采样器的多纹理理论 [英] Multitexturing theory with texture objects and samplers

查看:22
本文介绍了具有纹理对象和采样器的多纹理理论的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找不到任何关于如何仅使用纹理对象或纹理对象加采样器对多纹理进行编码的好的理论文章.我只是不知道如何管理 glActiveTexture 函数以及它到底做了什么.

glGenTextures(1, &texture);glActiveTexture(GL_TEXTURE0 + 0);//0 到 GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 之间的数字glBindTexture(GL_TEXTURE_2D,纹理);glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img.getSize().x, img.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.getPixelsPtr());//不在采样器中glGenerateMipmap(GL_TEXTURE_2D);//不在采样器中/* 与纹理相关的值而不是与采样器相关的值(采样器优先于纹理).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_MAG_FILTER, GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);*/glGenSampler(1, &textureSampler);glBindSampler(0, 纹理采样器);glSamplerParameteri(textureSampler, GL_TEXTURE_WRAP_S, GL_REPEAT);glSamplerParameteri(textureSampler, GL_TEXTURE_WRAP_T, GL_REPEAT);glSamplerParameteri(textureSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);glSamplerParameteri(textureSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);glUniform1i(glGetUniformLocation(colorShader->program, "textureSampler"), 0);//0 倒 GL_TEXTURE0

我有点困惑,如果多纹理是将片段代码中的多个采样器链接到多个纹理,还是可能只有一个采样器具有多个纹理?

解决方案

之前肯定已经解释过大部分内容,但让我尝试提供一个概述,希望能够让您更清楚地了解所有不同的部分是如何组合在一起的.我将首先分别解释每个部分,然后解释它们是如何连接的.

纹理目标

这是指不同类型的纹理(2D、3D 等).您可以将多个纹理(每种纹理类型之一)同时绑定到同一个纹理单元.例如之后:

glBindTexture(GL_TEXTURE_2D, texId1);glBindTexture(GL_TEXTURE_3D, texId2);

texId1texId2 都将绑定到同一个纹理单元,这是可能的,因为它们绑定到了不同的目标.>

这个细节有点令人费解和混乱,我不会在这个答案的其余部分考虑它.我建议您始终将不同的纹理绑定到不同的纹理单元.它会让您免于头痛和意外.

纹理对象

纹理对象的名称由 glGenTextures() 创建,它们与 glBindTexture() 绑定,等等.纹理对象拥有:

  • 纹理数据.
  • 定义纹理数据采样方式的状态,例如使用 glTexParameteri() 设置的过滤属性.

它们还包含有关与数据一起指定的纹理格式/类型的信息.

纹理单元

作为当前 OpenGL 状态的一部分,您可以绘制当前绑定的纹理表.我们需要同时绑定多个纹理来支持多纹理.一个纹理单元可以看作是这个状态表中的一个条目.

您使用 glActiveTexture() 指定当前活动的纹理单元.需要对特定纹理单元进行操作的调用将在活动纹理单元上进行操作.例如:

glActiveTexture(GL_TEXTURE3);glBindTexture(GL_TEXTURE_2D, texId);

texId 绑定到纹理单元 3.再次想象绑定纹理表,第 4 个条目(编号从 0 开始)现在指向纹理 texId.

采样对象

这是在 OpenGL 3.3 及更高版本中可用的一种较新的对象.对于大多数用例,您不需要需要这个,即使它们涉及从多个纹理中采样.为了完整起见,我将它们包括在此处,但在您牢牢掌握纹理对象和纹理单元之前,无需担心采样器.

还记得我上面如何解释纹理对象拥有纹理数据,以及定义数据采样方式的状态吗?采样器本质上所做的是将这两个方面解耦.采样器对象包含可以覆盖纹理对象中采样相关状态的状态.

这允许您在同一着色器中使用不同采样参数单个纹理进行采样.假设您想在单个着色器中对同一纹理进行 LINEAR 和 NEAREST 采样.没有采样器对象,如果没有同一纹理的多个副本(数据的多个副本),就无法做到这一点.采样器对象支持这种功能.

纹理视图

这是 OpenGL 4.3 中引入的一项功能.甚至不仅仅是纹理采样器,我只是为了完整性才提到它.

采样器将纹理数据(及其相关格式)与采样参数分离,纹理视图将原始纹理数据与格式分离.它们使使用具有不同格式的相同原始纹理数据成为可能.我怀疑您可以在不使用此功能的情况下走很长的路.

拼凑起来

您最终想要做的是指定着色器应从哪些纹理进行采样.纹理单元是连接着色器和纹理的关键部分.

从着色器的侧面看,着色器知道它从哪些纹理单元采样.这是由采样器统一变量的值给出的.例如,如果MyFirstTexture"是着色器代码中采样器变量的名称,则以下指定该变量与纹理单元3相关联:

GLint loc = glGetUniformLocation(prog, "MyFirstTexture");glUniform1i(loc, 3);

纹理单元和纹理对象之间的关联是通过上面已经显示的代码片段建立的:

glActiveTexture(GL_TEXTURE3);glBindTexture(GL_TEXTURE_2D, texId);

这两部分是将纹理连接到着色器代码中的采样器变量的关键部分.注意uniform变量的值是纹理单元的索引(3),而glActiveTexture()的参数是对应的enum(GL_TEXTURE3).我认为这是不幸的 API 设计,但您必须习惯它.

一旦你理解了这一点,你就会非常清楚如何在着色器中使用多个纹理(又名多纹理"):

  • 您的着色器代码中有多个采样器变量.
  • 您进行 glUniform1i() 调用以将采样器变量的值设置为不同纹理单元的索引.
  • 您将纹理绑定到每个匹配的纹理单元.

使用纹理单元 0 和 1 显示两个纹理:

glUseProgram(prog);GLint loc = glGetUniformLocation(prog, "MyFirstTexture");glUniform1i(loc, 0);loc = glGetUniformLocation(prog, "MySecondTexture");glUniform1i(loc, 1);glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, texId0);glActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, texId1);

另一种看待这个问题的方式是着色器中的采样器变量和纹理对象之间存在一定程度的间接性.着色器与纹理对象没有直接连接.相反,它有一个指向纹理对象表的索引(其中该索引是统一变量的值),而该表又包含指向纹理对象的指针"(其中表条目由 glActiveTexture()/glBindTexture()`.

或者对同一件事的最后一个类比,使用通信术语:您可以将纹理单元视为端口.你告诉着色器从哪个端口读取数据(统一变量的值).然后将纹理插入端口(通过将其绑定到纹理单元).着色器现在将从您插入端口的纹理读取数据.

I couldn't find any good theory articles on how to code multitexturing with either only texture objects or texture objects plus samplers. I just don't know how to manage the glActiveTexture function and what it exactly does.

glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0 + 0); // Number between 0 and GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, img.getSize().x, img.getSize().y, 0, GL_RGBA, GL_UNSIGNED_BYTE, img.getPixelsPtr()); // Not in sampler
glGenerateMipmap(GL_TEXTURE_2D); // Not in sampler

/* Values associated with the texture and not with sampler (sampler has priority over 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_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);*/

glGenSamplers(1, &textureSampler);
glBindSampler(0, textureSampler);
glSamplerParameteri(textureSampler, GL_TEXTURE_WRAP_S, GL_REPEAT);
glSamplerParameteri(textureSampler, GL_TEXTURE_WRAP_T, GL_REPEAT);
glSamplerParameteri(textureSampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glSamplerParameteri(textureSampler, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

glUniform1i(glGetUniformLocation(colorShader->program, "textureSampler"), 0); // 0 pour GL_TEXTURE0

I'm a little bit confused about if multitexturing is about having multiple samplers in the fragment code linked to multiple textures or if it is possible to have only have one sampler with multiple textures?

解决方案

Much of this must have been explained before, but let me try and give an overview that will hopefully make it clearer how all the different pieces fit together. I'll start by explaining each piece separately, and then explain how they are connected.

Texture Target

This refers to the different types of textures (2D, 3D, etc). You can have multiple textures, one of each texture type, bound to the same texture unit at the same time. For example, after:

glBindTexture(GL_TEXTURE_2D, texId1);
glBindTexture(GL_TEXTURE_3D, texId2);

BothtexId1 and texId2 will be bound to the same texture unit, which is possible because they are bound to different targets.

The details of this are somewhat convoluted and confusing, and I won't consider it in the rest of this answer. I would recommend that you always bind different textures to different texture units. It will save you from headaches and surprises.

Texture Object

Names for texture objects are created with glGenTextures(), they are bound with glBindTexture(), etc. Texture objects own:

  • Texture data.
  • State that defines how the texture data is sampled, like filtering attributes set with glTexParameteri().

They also contain information about the texture format/type that was specified together with the data.

Texture Unit

As part of the current OpenGL state, you can picture a table of textures that are currently bound. We need more than a single texture bound at the same time to support multi-texturing. A texture unit can be seen as an entry in this state table.

You use glActiveTexture() to specify the currently active texture units. Calls that need to operate on a specific texture unit will then operate on the active texture unit. For example:

glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, texId);

Will bind texId to texture unit 3. Picturing the table of bound textures again, the 4th entry (numbering starts at 0) now points at the texture texId.

Sampler Object

This is a newer kind of object available in OpenGL 3.3 and later. You will not need this for most use cases, even if they involve sampling from multiple textures. I'm including them here for completeness, but there's no need to worry about samplers until you have a firm grasp of texture objects and texture units.

Remember how I explained above that texture objects own the texture data, as well as state that defines how the data is sampled? What samplers essentially do is decouple these two aspects. The sampler object contains state that can override the sampling related state in the texture object.

What this allows you to do is sample one single texture with different sampling parameters in the same shader. Say you wanted to do LINEAR and NEAREST sampling of the same texture in a single shader. Without sampler objects, you can't do that without having multiple copies of the same texture (with multiple copies of the data). Sampler objects enable this kind of functionality.

Texture View

This is a feature introduced in OpenGL 4.3. Even more than texture samplers, I'm only mentioning it for completeness.

Where samplers decouple the texture data (with its associated format) from the sampling parameters, texture views decouple the raw texture data from the format. They make it possible to use the same raw texture data with different formats. I suspect that you can go a very long way without ever using this feature.

Putting the Pieces Together

What you ultimately want to do is specify which textures a shader should sample from. Texture units are the critical pieces in making the connection between shaders and textures.

Looking at it from the side of the shader, the shader knows which texture units it samples from. This is given by the value of the sampler uniform variables. For example, if "MyFirstTexture" is the name of a sampler variable in the shader code, the following specifies that the variable is associated with texture unit 3:

GLint loc = glGetUniformLocation(prog, "MyFirstTexture");
glUniform1i(loc, 3);

The association between texture unit and a texture object is established with the code fragment that was already shown above:

glActiveTexture(GL_TEXTURE3);
glBindTexture(GL_TEXTURE_2D, texId);

These two pieces are the critical parts in connecting a texture to a sampler variable in your shader code. Note that the value of the uniform variable is the index of the texture unit (3), while the argument of glActiveTexture() is the corresponding enum (GL_TEXTURE3). I would argue that this is unfortunate API design, but you'll just have to get used to it.

Once you understand this, it will hopefully be very obvious how you use multiple textures in your shader (aka "multi-texturing"):

  • You have multiple sampler variables in your shader code.
  • You make the glUniform1i() calls to set the values of the sampler variables to indices of different texture units.
  • You bind a texture to each of the matching texture units.

Showing this for two texture, using texture units 0 and 1:

glUseProgram(prog);

GLint loc = glGetUniformLocation(prog, "MyFirstTexture");
glUniform1i(loc, 0);
loc = glGetUniformLocation(prog, "MySecondTexture");
glUniform1i(loc, 1);

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texId0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texId1);

One other way of looking at this is that there's a level of indirection between samplers variables in shaders, and texture objects. The shader does not have a direct connection to the texture objects. Instead, it has an index into a table of texture objects (where this index is the value of the uniform variable), and this table in turn contains "pointers" to texture objects (where the table entries are populated with glActiveTexture()/glBindTexture()`.

Or one final analogy for the same thing, using communication terminology: You can look at the texture units as ports. You tell the shader which ports to read data from (value of uniform variable). Then you plug a texture into the port (by binding it to the texture unit). The shader will now read data from the texture you plugged into the port.

这篇关于具有纹理对象和采样器的多纹理理论的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆