【问题标题】:How do fragments get generated by rasterizer in OpenGLOpenGL中的光栅化器如何生成片段
【发布时间】:2016-08-03 13:17:16
【问题描述】:

我遇到了光栅化的描述,它基本上说当一个对象被投影到屏幕上时,会发生扫描窗口/屏幕上的所有像素并决定像素/片段是否在三角形,因此确定像素/片段在三角形内部,然后对像素/片段进行进一步处理,例如着色等

现在因为我正在研究 OpenGL,而且我知道 OpenGL 可能有它自己的这个过程的实现,所以我想知道这是否也发生在 OpenGL 中,因为我在 OpenGL 中阅读了顶点的“扫描转换”过程教程

现在与此相关的另一个问题是我知道像素的图像/屏幕/窗口是图像或像素的二维数组,也称为线性默认帧缓冲区

所以我想知道的是,如果是这样的话,投影三角形的 3 个顶点如何定义它的侧面覆盖了哪些像素?

光栅化器是否首先绘制三角形的边缘,然后扫描每个像素或二维像素数组(也称为默认帧缓冲区),并使用某种数学方法或其他更简单的过程查看这些点是否位于线条之间发生了什么?

【问题讨论】:

  • 不,它不会先绘制边缘。这会使相邻的三角形重叠一个像素,这会给半透明的三角形带来麻烦。此外,它会很慢。它做了什么它可能会扫描包围三角形的最小矩形,并使用三个叉积(或者可能是点积)来查看像素是否在该三角形内部。
  • 我想这样的事情会起作用:bool inside(ivec2 a, ivec2 b, ivec2 c, ivec2 p) {return cross(p-a,b-a)>0 && cross(p-b,c-b)>0 && cross(p-c,a-c);}.
  • 如果它使用线性插值来确定边缘上的点怎么办?然后使用这些点从三角形的一个边缘扫描到另一侧?这似乎是一种可能性?
  • @gettingfaster:OpenGL 没有详细说明如何进行三角形的扫描转换。无论是先做边缘还是扫描线或其他什么,这都取决于实现。 OpenGL 确实指定了有关结果的某些内容(连接性保证等),但对于您所谈论的内容,规范并没有说明。
  • @gettingfaster:你链接的这些算法,它们不是 GPU 的工作方式。您对 GPU 如何实现光栅转换有错误的想法。没有某种程序正在执行;整个光栅化以一种非常平行的、无序的、纯硬连线的方式发生。此外,内存在 GPU 中也不是线性组织的。数据结构和布局与您在 GPU 上看到的完全不同。内存不是通过平铺坐标的线性地址来寻址的。

标签: c++ opengl rasterizing


【解决方案1】:

引自 GPU Gems:Parallel Prefix Sum (Scan) with CUDA,它描述了 OpenGL 如何进行扫描 转换并将其与 CUDA 进行比较,我认为这足以回答我的问题:

在引入 CUDA 之前,一些研究人员实施了 使用 OpenGL 和 Direct3D 等图形 API 进行扫描(参见第 39.3.4 更多)。为了展示 CUDA 相对于这些 API 进行扫描等计算的优势,在本节中,我们将简要介绍 Sengupta et 的高效 OpenGL 包容扫描实现 人。 (2006 年)。他们的实现是一种混合算法,它执行 可配置的减少步骤数,如算法 5 所示。然后 之前运行和扫描算法的双缓冲版本 在算法 2 中显示了 reduce 步骤的结果。最后它 执行下扫,如算法 6 所示。

示例 5. OpenGL 扫描算法的归约步骤

1: for d = 1 to log2 n do 
2:     for all k = 1 to n/2 d  – 1 in parallel do 
3:          a[d][k] = a[d – 1][2k] + a[d – 1][2k + 1]]
Example 6. The Down-Sweep Step of the OpenGL Scan Algorithm

1: for d = log2 n – 1 down to 0 do 
2:     for all k = 0 to n/2 d  – 1 in parallel do 
3:          if i > 0 then 
4:             if k mod 2 U2260.GIF 0 then 
5:                  a[d][k] = a[d + 1][k/2]
6:             else 
7:                  a[d][i] = a[d + 1][k/2 – 1]

OpenGL 扫描计算是使用像素着色器实现的,并且 每个 a[d] 数组都是 GPU 上的二维纹理。写给 这些数组是使用 OpenGL 中的渲染到纹理执行的。因此, 算法 5 和算法 2 中的每个循环迭代都需要阅读 从一种纹理写入另一种纹理。

CUDA 相对于 OpenGL 的主要优势在于其片上共享 内存、线程同步功能和分散写入 内存,不会暴露给 OpenGL 像素着色器。 CUDA 划分 一次大扫描的工作分成许多块,并且每个块都被处理 在写入任何数据之前由单个多处理器完全在芯片上 到片外存储器。在OpenGL中,所有内存更新都是片外内存 更新。因此,OpenGL 实现使用的带宽非常多 更高,因此性能更低,如前面所示 图 39-7.

【讨论】:

  • 见鬼,你为什么要发布并接受一个不能回答你自己问题的答案?文章中描述的问题与如何以任何方式光栅化图元无关。
【解决方案2】:

扫描线算法是 1990 年代人们在软件中所做的,当时还没有现代 GPU。 GPU 开发人员很快就发现,您用于软件渲染的算法与您在具有数十亿个晶体管的 VLSI 实现中实现的算法大不相同。无论如何,针对硬件实现优化的算法对于任何具有软件背景的人来说都显得相当陌生。

我想澄清的另一件事是,OpenGL 没有说明“如何”渲染,它只是“渲染什么”。 OpenGL 实现可以随意执行。我们可以通过阅读 OpenGL 标准来找出“什么”,但“如何”被隐藏在 GPU 供应商的秘密中。

最后,在我们开始之前,您链接的文章是不相关的。它们是关于超声波扫描的工作原理的。

我们对扫描转换了解多少?

  • 扫描转换有许多原语作为输入。出于我们的目的,让我们假设它们都是三角形(现在越来越正确)。

  • 每个三角形都必须被剪切平面剪切。在最坏的情况下(将其变成六边形),这最多可以为三角形添加三个额外的边。这必须在透视投影之前发生。

  • 每个图元都必须经过透视投影。此过程获取具有齐次坐标(X、Y、Z、W)的每个顶点并将其转换为(X/W、Y/W、Z/W)。

  • 帧缓冲区通常按层次组织成图块,而不是像在软件中那样以线性方式组织。此外,处理可能在一个以上的层级上完成。我们在软件中使用线性组织的原因是因为在分层布局中计算内存地址需要额外的周期。但是,VLSI 实现不会遇到这个问题,他们可以简单地将寄存器中的位连接起来,以便从中获取地址。

因此您可以看到,在软件中,图块“复杂且缓慢”,但在硬件中,它们“简单且快速”。

查看 R5xx 手册的一些注意事项:

R5xx 系列确实很古老(2005 年),但文档可在线获取(搜索“R5xx_Acceleration_v1.5.pdf”)。它提到了两个扫描转换器,所以管道看起来像这样:

primitive output -> coarse scan converter -> quad scan converter -> fragment shader

粗略扫描转换器似乎可以在可配置尺寸(8x8 到 32x32)的较大图块上运行,并具有多种可选模式,“基于拦截”和“基于边界框”模式。

然后四边形扫描转换器获取粗略扫描转换器的输出并输出单个四边形,它们是四个样本的组。每个四边形的深度值可以表示为四个离散值或平面方程。如果深度缓冲区中的相应四边形也被指定为平面方程,则平面方程允许快速丢弃整个四边形。这称为“早期 Z”,是一种常见的优化。

片段着色器然后一次在一个四边形上工作。四边形可能包含三角形之外的样本,然后将被丢弃。

值得一提的是,这是一张显卡。现代显卡更复杂。例如,R5xx 甚至不允许您从顶点着色器中采样纹理。

如果您想要更完全不同的画面,请查看 PowerVR GPU 实现,它使用了一种称为“基于切片的延迟渲染”的东西。这些功能强大的现代 GPU 已针对低成本和低功耗进行了优化,它们挑战了您对渲染器工作方式的许多假设。

【讨论】:

  • 虽然您有权更改所有这些方法的名称,但有一件事没有改变,那就是它背后的数学所有发生的计算都是基于数学思想,所以无论是否他们称其为“PowerVR”或“基于图块的延迟渲染”或其他任何东西,最终这一切都归结为老派的数学,在那所学校没有任何改变,方法仍然是它们,相同且不变的http.developer.nvidia.com/GPUGems3/gpugems3_ch34.html 最后他们总是使用数学模型进行所有转换
  • 唯一的区别是 GPU 可以并行运行所有东西,但最终做的事情与 cpu 相同,但有多个线程,工作被分配到不同的晶体管上
【解决方案3】:

我知道 OpenGL 可能有自己的这个过程的实现

OpenGL 只是一个规范文档。在计算机上运行的是 OpenGL 实现,大部分时间是作为 GPU 驱动程序的一部分。实际工作负载由 GPU 执行……

这也发生在 OpenGL 中,因为我在 OpenGL 教程中阅读了顶点的“扫描转换”过程

很可能不是。事实上,上周末我参加了由 AMD 主办的 Khronos(指定 OpenGL 的组织)活动,AMD 的一位 GPU 工程师感叹新手在 OpenGL、Direct3D、Mantel、Vulkan 等方面考虑到了扫描线算法。 ,而 GPU 则完全不同。

像素的二维数组也称为默认的线性帧缓冲区

实际上,GPU 内部使用的像素内存布局不是线性的(即逐行),而是遵循提供高效本地化访问的模式。对于线性访问,GPU 具有极其高效的复制引擎,允许在内部格式和线性格式之间进行几乎零开销的转换。

不过,内部使用的确切布局是只有 GPU 工程师才能深入了解的细节。但内存不是线性组织而是以局部方式组织的事实也是 GPU 不使用传统扫描线算法的原因之一。

所以我想知道的是,如果是这样的话,投影三角形的 3 个顶点如何定义它的侧面覆盖了哪些像素?

任何满足 OpenGL 规范要求的方法都是允许的。细节是 OpenGL 实现的一部分,即通常是特定 GPU 型号和驱动程序版本的组合。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-14
    • 2011-09-19
    相关资源
    最近更新 更多