0

我发现的大多数教程、指南和书籍都与 OpenGL 相关,解释了如何绘制三角形和初始化 OpenGL。没关系。但是当他们试图解释它时,他们只是列出了一堆函数和参数,例如:

glClear()
glClearColor()
glBegin()
glEnd()
...

由于我不太擅长通过记忆学习事物,所以我总是需要回答“我们为什么要这样做?” 所以我会写那一堆函数,因为我记得我必须在做其他事情之前设置某些事情等等,而不是因为教程告诉我。

glBegin()在开始用and绘制一些东西之前,请有人向我解释一下我必须向 OpenGL 定义什么(只有纯 OpenGL,我使用 SFML 作为背景库,但这并不重要)glEnd()

示例答案:

您必须首先告诉 OpenGL 需要什么颜色来清除屏幕。因为在我们开始绘制当前帧之前,每一帧都需要被前一帧清除...

4

2 回答 2

9

首先你应该知道,OpenGL 是一个状态机。这意味着,除了创建 OpenGL 上下文(由 SFML 完成)之外,没有初始化之类的东西!

因为我不太擅长通过记忆来学习东西,

这个不错……</p>

我总是需要回答“我们为什么要这样做?”

这太棒了!

在开始用 glBegin() 和 glEnd() 绘制东西之前,请有人向我解释一下我必须对 OpenGL 定义什么(只有纯 OpenGL,我使用 SFML 作为背景库,但这并不重要)?

正如我已经说过的:OpenGL 是一个状态机。这基本上意味着,您可以执行两种调用:设置状态和执行操作。

例如glClearColor,设置一个状态变量,即清除颜色的状态变量,该值用于在glClear设置GL_COLOR_BUFFER_BIT标志的调用时清除活动帧缓冲区颜色。glClearDepth深度值(GL_DEPTH_BUFFER_BITflag to glClear)存在类似的函数。

glBeginglEnd属于OpenGL的即时模式,已被弃用。所以学习它们没有什么理由。您应该改用顶点数组,最好通过顶点缓冲区对象。

但这里是这样:glBegin将 OpenGL 设置为现在应该绘制几何图形的状态,该几何图形被选为glBegin. GL_TRIANGLES例如意味着,OpenGL 现在将每 3 次调用解释glVertex为形成一个三角形。glEnd告诉 OpenGL 你已经完成了批三角形。在 glBegin...glEnd 块中,某些状态更改是不允许的。其中包括与转换几何和生成图片有关的所有内容,包括矩阵、着色器、纹理等。

一种常见的误解是,OpenGL 已初始化。这是由于具有initGL功能或类似功能的教程编写得不好。在开始渲染场景时从头开始设置所有状态是一个很好的做法。但是由于单个帧可能包含多个场景(想想 HUD 或分屏游戏),这种情况会在一个场景中发生多次。


更新:

那么如何画三角形呢?好吧,这很简单。首先,您需要几何数据。例如这个:

GLfloat triangle[] = {
    -1, 0, 0,
    +1, 0, 0,
     0, 1, 0
};

在渲染函数中,我们告诉 OpenGL 下一次调用 glDrawArrays 或 glDrawElements 应该从那里获取数据(为了简单起见,我将在这里使用 OpenGL-2 函数):

glVertexPointer(3, /* there are three scalars per vertex element */
                GL_FLOAT, /* element scalars are float */
                0, /* elements are tightly packed (could as well be sizeof(GLfloat)*3 */
                trignale /* and there you find the data */ );
/* Note that glVertexPointer does not make a copy of the data! 
   If using a VBO the data is copied when calling glBufferData. */

/* this switches OpenGL into a state that it will
   actually access data at the place we pointed it
   to with glVertexPointer */
glEnableClientState(GL_VERTEX_ARRAY);

/* glDrawArrays takes data from the supplied arrays and draws them
   as if they were submitted sequentially in a for loop to immediate
   mode functions. Has some valid applications. Better use index
   based drawing for models with a lot of shared vertices. */
glDrawArrays(Gl_TRIANGLE, /* draw triangles */
             0, /* start at index 0 */
             3, /* process 3 elements (of 3 scalars each) */ );

我还没有包括设置转换和视口映射。

视口定义了易于投影和标准化的几何图形如何放置在窗口中。此状态使用 设置glViewport(pos_left, pos_bottom, width, height)

今天的转换发生在顶点着色器中,本质上顶点着色器是一个用特殊语言(GLSL)编写的小程序,它获取顶点属性并计算结果顶点的剪辑空间位置。通常的方法是模拟固定函数管道,这是一个两阶段的过程:首先将几何体转换到视图空间(一些计算,如照明在这个空间中更容易),然后将其投影到剪辑空间,这是一种渲染器的镜头。在固定函数管道中,有两个转换矩阵:Modelview 和 Projection。您将它们设置为所需结果所需的任何值。在只有三角形的情况下,我们保留模型视图标识并在任一维度上使用从 -1 到 1 的正交投影。

glMatrixMode(GL_PROJECTION);
/* the following function multiplies onto what's already on the stack,
   so reset it to identity */
glLoadIdentity(); 

/* our clip volume is defined by 6 orthogonal planes with normals X,Y,Z
   and ditance 1 from origin into each direction */
glOrtho(-1, 1, -1, 1, -1, 1); 

glMatrixMode(GL_MODELVIEW);
/* now a identity matrix is loaded onto the modelview */
glLoadIdentity();

设置好转换后,我们现在可以绘制上述三角形:

draw_triangle();

最后我们需要告诉 OpenGL 我们已经完成了发送命令,它应该完成它的渲染。

if(singlebuffered)
    glFinish();

然而大多数时候你的窗口是双缓冲的,所以你需要交换它以使事情变得更生动。由于没有完成交换就没有意义,因此交换意味着完成

else
    SwapBuffers();
于 2012-08-05T09:24:55.073 回答
0

您正在使用 API 设置和更改 OpenGL 状态机。

您实际上并没有直接对 GPU 进行编程,而是在您的应用程序和 GPU 之间使用一种媒介来完成您想要做的任何事情。

之所以像这样并且与 CPU 和内存的工作方式不同,是因为 openGL 旨在运行在与操作系统/系统无关的硬件上,因此您的代码可以在任何操作系统上运行并在任何硬件上运行不仅仅是您的编程对象。

因此,因此,您需要学习使用他们的预设代码,以确保无论您尝试做什么,它都能够在合理范围内的所有系统/操作系统/硬件上运行。

例如,如果您要在 Windows 8.1 上使用特定显卡(比如 AMD)创建应用程序,您仍然希望您的应用程序能够在 Andoird/iOS/Linux/其他 Windows 系统/其他硬件(gpus)上运行,例如 Nvidia .

因此,为什么 Khronos 在创建 API 时,尽可能使其独立于系统/硬件,以便它可以在任何东西上运行并成为每个人的标准。

这是我们必须为此付出的代价,我们必须学习他们的 API,而不是学习如何直接写入 gpu 内存并直接利用 GPU 处理信息/数据。

虽然随着 Vulkan 的引入,它在发布时可能会有所不同(也来自 khronos),我们将了解它是如何工作的。

于 2015-07-03T00:58:35.163 回答