OpenGL 技术可能非常不透明和令人困惑。我知道!多年来,我一直在编写基于 OpenGL 的 3D 引擎(断断续续)。就我而言,问题的一部分是,我编写引擎来隐藏底层 3D API (OpenGL),所以一旦我得到一些工作,我就再也看不到 OpenGL 代码了。
但这里有一种技术可以帮助我的大脑理解“OpenGL 方式”。我认为这种思考方式是正确的(但不是全部)。
想想硬件图形/GPU 卡。它们具有在硬件中实现的某些功能。例如,GPU 可能一次只能更新(写入)一个纹理。尽管如此,GPU 必须在 GPU 内部的 RAM 中包含许多纹理,因为 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.
如果我们想在另一个纹理上绘制图像,我们必须重复同样的过程。
当我们最终准备好在显示器上渲染某些东西时,我们需要我们的代码来制作我们创建和复制图像的一个或多个纹理,以便我们的片段着色器可以访问。
事实证明,片段着色器可以通过访问多个“纹理单元”(每个纹理单元一个纹理)一次访问多个纹理。因此,我们的代码必须将我们想要提供的纹理绑定到我们的片段着色器期望它们绑定到的纹理单元。
所以我们必须这样做:
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);
现在,我必须说我喜欢 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);
巴莫。无需一遍又一遍地设置所有这些状态。只需指定要将纹理附加到哪个纹理单元,加上指示如何访问纹理的纹理类型,以及我要附加到纹理单元的纹理 ID。
我也不需要将纹理绑定为活动纹理来复制图像,我只需提供我想要复制到的纹理的 ID。为什么要绑定?
嗯,有一个问题迫使 OpenGL 以疯狂的方式构建。因为硬件做一些事情,软件驱动做其他事情,并且因为在哪里做的事情是一个变量(取决于 GPU 卡),他们需要一些方法来控制复杂性。他们的解决方案本质上是为每种实体/对象只有一个绑定点,并要求我们在调用操作它们的函数之前将我们的实体绑定到这些绑定点。作为第二个目的,绑定实体使它们可用于 GPU,以及我们在 GPU 中执行的各种着色器。
至少这就是我将“OpenGL 方式”保持在脑海中的方式。坦率地说,如果有人真的、真的、真的理解 OpenGL 的所有原因(而且必须如此),我希望他们能发表自己的回复。我相信这是一个重要的问题和话题,其基本原理很少被描述,更不用说以我微弱的大脑可以理解的方式了。