索引缓冲区和错误调试
2023-08-21 19:39:43来源:哔哩哔哩

我们今天来了解一下索引缓冲区,首先要知道在图形编程中GPU的绘制是基于三角形的,如果我们想要绘制一个正方形,对GPU来说就是绘制两个三角形,这意味着GPU在画一个正方形的时候,把相同的顶点位置多次存储在GPU内存中,这无疑是一种浪费,特别是在考虑绘制3D模型时,有大量的三角形互相连接,而且在实际渲染过程中,顶点可能不仅仅包含位置信息,还包括法线、纹理、颜色等信息,这将产生大量的额外的顶点信息,这样一次又一次的复制这么多信息构成的顶点缓冲区,将造成特别严重的浪费,如果只有一个简单的索引就会更快、更高效,所以我们引入了索引缓冲区,它允许我们反复利用现有的顶点。


(资料图)

我们首先创建索引缓冲区需要的数据,这些数据在CPU上,我们会将它们发送到GPU,然后告诉OpenGL使用它们进行渲染,这个过程跟我们创建顶点缓冲区差不多,区别在于,我们需要将target绑定为GL_ELEMENT_ARRAY_BUFFER。

glGenBuffers

错误:

如果n为负数,则生成GL_INVALID_VALUE。

glBindBuffer

该函数将缓冲区绑定到指定的绑定点。target需要设置为可接受的符号常量之一,将Buffer设置为缓冲区的名称,如果不存在名为buffer的缓冲区对象,则会创建一个同名的缓冲区。当一个缓冲区绑定到一个目标时,该目标以前的绑定会自动中断。

缓冲区对象名是无符号整数。零值是保留的,目标没有默认的缓冲区对象,当buffer设置为零会解除绑定的所有缓冲区,并恢复该目标的客户端内存使用(如果该目标支持的话)。

错误:

如果target不是允许的值之一,则生成GL_INVALID_ENUM。 

如果buffer不是先前调用glGenBuffers返回的名称,则生成GL_INVALID_VALUE

glBufferData

glBufferData和glNamedBufferData都是创建一个缓冲区对象的数据存储,对于glBufferData,是当前绑定到target的缓冲区对象;对于glNamedBufferData,是buffer中与ID相关联的缓冲区对象。

创建新存储时,预先存在的数据存储都将被删除,然后以指定的字节大小和用途创建新的数据存储。如果数据不为空,则使用来自该指针的数据初始化数据存储。在初始状态下,新的数据存储没有被映射,它有一个空映射指针,它的映射访问是GL_READ_WRITE。

usage是对GL实现的一个指示,说明该如何访问缓冲区对象的数据存储。这使得GL实现能够做出更加智能的决策,从而显著影响缓冲区对象的性能。使用可以分为两部分:第一,访问的频率(修改和使用),第二,访问的性质。

访问频率可以是以下之一:

STREAM 数据存储内容将被修改一次,使用次数不多。 

STATIC 数据存储内容将被修改一次,使用次数较多。 

DYNAMIC 数据存储内容将被反复修改,并且多次使用。 

访问的性质可能是以下之一: 

DRAW 数据存储内容由应用程序修改,并用作GL绘图和图像规范命令的源。

READ 数据存储内容通过从GL读取数据来修改,并在应用程序查询时用于返回该数据。

COPY 通过从GL读取数据来修改数据存储内容,并将其用作GL绘图和图像规范命令的源。

然后在循环中调用glDrawElements,而不是glDrawArrays。

运行就可以得到一个正方形了。

所以,我们创建了一个索引缓冲区,然后用4行代码将其发送给显卡,最后使用glDrawElements函数绘制这些数据,这里要注意,如果glDrawElements参数中GL_UNSIGNED_INT不小心写成GL_INT,程序不会报错,但窗口只会显示一片黑,像这样的无效枚举的情况很容易发生,排查起来也十分麻烦,因为你可能没有正确设置顶点数据,也许索引缓冲区设置不正确,也可能着色器没运行。这里有两种主要的方法来检查在OpenGL中的错误,一个是glGetError函数,还有一个是glDebugMessageCallback函数,今天我们只讨论glGetError函数。

glGetError

每个可检测的错误都被分配了一个数字代码和符号名称。当错误发生时,错误标志被设置为适当的错误代码值。调用glGetError时,它在返回错误代码并将标志重置为GL_NO_ERROR之前,不会记录其他错误。如果glGetError返回GL_NO_ERROR,则自上次调用glGetError以来,或者自GL初始化以来,没有可检测到的错误。

为了允许分布式实现,可能有几个错误标志。如果任何单个错误标志记录了一个错误,则返回该标志的值,并且在调用glGetError时将该标志重置为GL_NO_ERROR。如果多个标志记录了错误,glGetError返回并清除任意错误标志值。因此,如果要重置所有错误标志,则应始终在循环中调用glGetError,直到它返回GL_NO_ERROR。

最初,所有错误标志都设置为GL_NO_ERROR。 

目前定义了以下错误:

GL_NO_ERROR 没有记录任何错误。这个符号常量的值为0。 

GL_INVALID_ENUM 为枚举参数指定了不可接受的值。违规命令被忽略,除了设置错误标志之外,没有其他副作用。 

GL_INVALID_VALUE 数值参数超出范围。违规命令被忽略,除了设置错误标志之外,没有其他副作用。 

GL_INVALID_ OPERATION当前状态下不允许指定的操作。违规命令被忽略,除了设置错误标志之外,没有其他副作用。

GL_INVALID_FRAMEBUFFER_OPERATION The framebuffer对象不完整。违规命令被忽略,除了设置错误标志之外,没有其他副作用。 

GL_OUT_OF_MEMORY 没有足够的内存来执行该命令。记录该错误后,除了错误标志的状态外,GL的状态未定义。 

GL_STACK_UNDERFLOW  试图执行会导致内部堆栈下溢的操作。 

GL _ STACK _UNDERFLOW 试图执行会导致内部堆栈溢出的操作。

我们定义两个函数,在其中用循环的方式来调用glGetError

在glDrawElements函数前调用ClearError来清除之前的其他的错误干扰,只关心glDrawElements函数有没有错误

得到的错误信息,我们拿到数字代码,在调试中转化为16进制,到文件中去找对应的错误信息

更智能一点的做法,就是使用断言,这样在出现错误的时候,就会在错误的代码处自动断点

这样方便我们更快的定位到错误处,且不会一直向控制台发送错误信息。

我们可以用一个宏来揽括以上操作

标签:

下一篇: 最后一页
上一篇: 汾酒启动“不同贮存环境对清香型白酒品质影响的研究”

相关新闻

保险时讯