OpenGL的"绑定"功能背后的概念

man*_*479 42 opengl

我正在从本教程学习OpenGL .我的问题是关于一般的规范,而不是关于特定的功能或主题.查看以下代码时:

glGenBuffers(1, &positionBufferObject);
glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
Run Code Online (Sandbox Code Playgroud)

我对在设置缓冲区数据之前和之后调用绑定函数的效用感到困惑.由于我对OpenGL和计算机图形学一般缺乏经验,这对我来说似乎是多余的.

手册页说:

glBindBuffer允许您创建或使用命名缓冲区对象.与目标设定为GL_ARRAY_BUFFER,GL_ELEMENT_ARRAY_BUFFER,GL_PIXEL_PACK_BUFFER或GL_PIXEL_UNPACK_BUFFER主叫glBindBuffer和缓冲设置为新的缓冲对象的名称绑定缓存对象名称的目标.当缓冲区对象绑定到目标时,该目标的先前绑定将自动中断.

将某些东西"绑定"到"目标"的概念/效用究竟是什么?

and*_*oke 30

opengl中的命令不是孤立存在的.他们假设存在上下文.想到这一点的一种方法是在后台隐藏一个opengl对象,并且函数是该对象上的方法.

因此,当你调用一个函数时,它的作用取决于参数,当然,还取决于opengl的内部状态 - 在上下文/对象上.

bind非常清楚,它表示"将其设置为当前X".然后函数修改"当前X"(例如,X可能是缓冲区).[update:]正如你所说,正在设置的东西(对象中的属性,或"数据成员")是第一个绑定的参数.如此GL_ARRAY_BUFFER命名您正在设置的特定事物.

并回答问题的第二部分 - 将其设置为0只是清除值,这样您就不会意外地在其他地方进行计划外更改.

  • 谢谢,如果我理解你的答案,我可以将GL_ARRAY_BUFFER视为上下文的"数据成员",我将positionBufferObject指定为数组vertexPositions的句柄.正确吗? (5认同)
  • 可能`有状态'是一个可以在这里说的话 (2认同)

hon*_*ann 26

OpenGL技术令人难以置信的不透明和令人困惑.我知道!多年来我一直在使用基于OpenGL的3D引擎(关闭和开启).在我的情况下,问题的一部分是,我编写引擎来隐藏底层的3D API(OpenGL),所以一旦我开始工作,我再也看不到OpenGL代码了.

但这里有一种技术可以帮助我的大脑理解"OpenGL方式".我认为这种思考方式是真的(但不是整个故事).

想想硬件图形/ GPU卡.它们具有在硬件中实现的某些功能.例如,GPU可能仅能够一次更新(写入)一个纹理.尽管如此,GPU必须在GPU内部包含许多纹理,因为CPU内存和GPU内存之间的传输速度非常慢.

那么OpenGL API的作用就是创建"活动纹理"的概念.然后,当我们调用OpenGL API函数将图像复制到纹理时,我们必须这样做:

1:  generate a texture and assign its identifier to an unsigned integer variable.
2:  bind the texture to the GL_TEXTURE bind point (or some such bind point).
3:  specify the size and format of the texture bound to GL_TEXTURE target.
4:  copy some image we want on the texture to the GL_TEXTURE target.
Run Code Online (Sandbox Code Playgroud)

如果我们想在另一个纹理上绘制图像,我们必须重复相同的过程.

当我们最终准备好在显示器上渲染某些东西时,我们需要我们的代码来创建我们创建的一个或多个纹理并复制图像,以便我们的片段着色器可以访问它们.

事实证明,片段着色器可以通过访问多个"纹理单元"(每个纹理单元一个纹理)一次访问多个纹理.因此,我们的代码必须将我们想要的纹理绑定到我们的片段着色器期望它们绑定到的纹理单元.

所以我们必须做这样的事情:

glActiveTexture (GL_TEXTURE0);
glBindTexture (GL_TEXTURE_2D, mytexture0);

glActiveTexture (GL_TEXTURE1);
glBindTexture (GL_TEXTURE_2D, mytexture1);

glActiveTexture (GL_TEXTURE2);
glBindTexture (GL_TEXTURE_2D, mytexture2);

glActiveTexture (GL_TEXTURE3);
glBindTexture (GL_TEXTURE_2D, mytexture3);
Run Code Online (Sandbox Code Playgroud)

现在,我必须说我喜欢OpenGL有很多原因,但这种方法让我疯狂.那是因为我多年来写的所有软件都是这样的:

error = glSetTexture (GL_TEXTURE0, GL_TEXTURE_2D, mytexture0);
error = glSetTexture (GL_TEXTURE1, GL_TEXTURE_2D, mytexture1);
error = glSetTexture (GL_TEXTURE2, GL_TEXTURE_2D, mytexture2);
error = glSetTexture (GL_TEXTURE3, GL_TEXTURE_2D, mytexture3);
Run Code Online (Sandbox Code Playgroud)

八脉.无需一遍又一遍地设置所有这些状态.只需指定要附加纹理的纹理单元,加上纹理类型以指示如何访问纹理,以及我想要附加到纹理单元的纹理的ID.

我也不需要将纹理绑定为活动纹理来将图像复制到它,我只需要给出我想要复制的纹理的ID.为什么需要绑定?

好吧,有一种方法迫使OpenGL以疯狂的方式构建.因为硬件做了一些事情,而软件驱动程序做了其他事情,并且因为做什么是变量(取决于GPU卡),他们需要一些方法来控制复杂性.他们的解决方案基本上只对每种实体/对象只有一个绑定点,并要求我们在调用操作它们的函数之前将我们的实体绑定到那些绑定点.作为第二个目的,绑定实体使GPU可用,以及我们在GPU中执行的各种着色器.


至少这就是我如何将"OpenGL方式"直接放在脑海中.坦率地说,如果有人真的,真的,真的理解OpenGL的所有原因(并且必须是)结构,我希望他们发布自己的回复.我相信这是一个重要的问题和话题,如果根本没有描述,理论基础很少,更不用说我的小脑子可以理解的方式了.

  • 实际上,我会澄清一句话:***1:生成纹理并将其标识符分配给无符号整数变量.***虽然标识符来自名称池(在OpenGL 3.0+中这个池)对于每种类型的对象都是唯一的,在旧版本中它不必是),直到第一次绑定对象时它才真正不是实际纹理.Nitpicky,我知道,但是GL充满了这样的怪癖:P另外,`glSetTexture(...)`类型的语法确实以`GL_EXT_direct_state_access`的形式存在,但扩展似乎是'EXT`中的permastuck状态 :( (2认同)

Nic*_*las 15

简介:什么是OpenGL?

像结构一样的复杂聚合永远不会直接暴露在OpenGL中.任何此类构造都隐藏在API后面.这样可以更轻松地将OpenGL API暴露给非C语言,而无需复杂的转换层.

在C++中,如果你想要一个包含整数,浮点数和字符串的对象,你可以创建它并像这样访问它:

struct Object
{
    int count;
    float opacity;
    char *name;
};

//Create the storage for the object.
Object newObject;

//Put data into the object.
newObject.count = 5;
newObject.opacity = 0.4f;
newObject.name = "Some String";
Run Code Online (Sandbox Code Playgroud)

在OpenGL中,您将使用看起来更像这样的API:

//Create the storage for the object
GLuint objectName;
glGenObject(1, &objectName);

//Put data into the object.
glBindObject(GL_MODIFY, objectName);
glObjectParameteri(GL_MODIFY, GL_OBJECT_COUNT, 5);
glObjectParameterf(GL_MODIFY, GL_OBJECT_OPACITY, 0.4f);
glObjectParameters(GL_MODIFY, GL_OBJECT_NAME, "Some String");
Run Code Online (Sandbox Code Playgroud)

当然,这些都不是实际的OpenGL命令.这只是这种对象的接口看起来像的一个例子.

OpenGL拥有所有OpenGL对象的存储.因此,用户只能通过引用访问对象.几乎所有OpenGL对象都由无符号整数(GLuint)引用.对象由glGen*形式的函数创建,其中*是对象的类型.第一个参数是要创建的对象数,第二个参数是接收新创建的对象名的GLuint*数组.

要修改大多数对象,必须先将它们绑定到上下文.许多对象可以绑定到上下文中的不同位置; 这允许以不同方式使用相同的对象.这些不同的位置称为目标; 所有对象都有一个有效目标列表,有些只有一个.在上面的示例中,虚构目标"GL_MODIFY"是objectName绑定的位置.

这是大多数OpenGL对象的工作方式,缓冲区对象是"大多数OpenGL对象".

如果这还不够好,本教程将在第1章:关注数据后再次介绍它:

void InitializeVertexBuffer()
{
    glGenBuffers(1, &positionBufferObject);

    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Run Code Online (Sandbox Code Playgroud)

第一行创建缓冲区对象,将句柄存储在全局变量positionBufferObject中.虽然该对象现在存在,但它还没有任何内存.那是因为我们没有为此对象分配任何内容.

glBindBuffer函数将新创建的缓冲区对象绑定到GL_ARRAY_BUFFER绑定目标.正如在介绍中所提到的,OpenGL中的对象通常必须绑定到上下文才能使它们做任何事情,缓冲对象也不例外.

glBufferData函数执行两个操作.它为当前绑定到GL_ARRAY_BUFFER的缓冲区分配内存,这是我们刚创建和绑定的缓冲区.我们已经有了一些顶点数据; 问题是它在我们的记忆中而不是OpenGL的记忆中.sizeof(vertexPositions)使用C++编译器来确定vertexPositions数组的字节大小.然后,我们将此大小传递给glBufferData,作为为此缓冲区对象分配的内存大小.因此,我们分配足够的GPU内存来存储我们的顶点数据.

glBufferData执行的另一个操作是将数据从我们的内存数组复制到缓冲区对象中.第三个参数控制它.如果此值不为NULL,则在这种情况下,glBufferData会将指针引用的数据复制到缓冲区对象中.在此函数调用之后,缓冲区对象存储了vertexPositions存储的确切内容.

第四个参数是我们将在未来的教程中看到的内容.

第二个绑定缓冲区调用只是清理.通过将缓冲区对象0绑定到GL_ARRAY_BUFFER,我们使先前绑定到该目标的缓冲区对象从中解除绑定.在这种情况下,零工作很像NULL指针.这不是绝对必要的,因为任何后来绑定到这个目标将简单地解除已经存在的东西.但除非您对渲染有非常严格的控制,否则解绑绑定的对象通常是个好主意.

  • 是的我已经读过了.我问这个问题的唯一原因是因为我不清楚.特别是像"glBindBuffer函数将新创建的缓冲区对象绑定到GL_ARRAY_BUFFER绑定目标"这样的句子.实际上意味着 (8认同)
  • +1和-1,用于获取良好信息和获取信息. (4认同)
  • ||"他们必须首先绑定到上下文"< - 什么是'绑定'?||"上下文中的不同位置"< - 位置,目标?||我明白,进入更多细节会产生反作用,但有些类比不会受到伤害.||(如果你是作者,感谢你写这篇文章,这很棒.虽然我在构建它时遇到了很多麻烦.) (3认同)
  • :facepalm:我知道.我不理解它在(上下文,目标,位置)等环境中的含义.我不了解其他人,但在启动OpenGL之后,不直观的术语是毁灭性的. (3认同)
  • -1.如果他很难理解教程中的内容,那么将其粘贴到此处而不进行任何重新解释也不会有太大帮助. (3认同)

Ern*_*ill 5

将缓冲区绑定到目标就像设置全局变量一样.然后,后续函数调用对该全局数据进行操作.在OpenGL的情况下,所有"全局变量"一起形成GL上下文.事实上,所有GL函数都从该上下文中读取或以某种方式对其进行修改.

glGenBuffers()电话是有点像malloc(),分配缓冲区; 我们设定一个全球指向它glBindBuffer(); 我们调用一个在global(glBufferData())上运行的函数,然后我们将全局设置为NULLso ,这样它就不会再次无意中在该缓冲区上运行glBindBuffer().

  • @ manasij7479:glGenBuffers没有分配内存,它会生成一个句柄来识别缓冲区,稍后使用glBindBuffer进行绑定 (5认同)
  • @ manasij7479:...此外,GL缓冲区需要驻留在GPU端,因此在CPU端分配的数据"没有用",只需要传输源. (2认同)