我认为最有效的方法是绘制一个single“全屏”triangle。为了让三角形覆盖整个屏幕,它需要比实际视口大。在 NDC 中(还有剪辑空间,如果我们设置w=1
),视口将始终是[-1,1]
正方形。为了使三角形完全覆盖该区域,我们需要两条边的长度是视口矩形的两倍,以便第三条边将穿过视口的边缘,因此我们可以使用以下坐标(在逆时针顺序):(-1,-1)
, (3,-1)
, (-1,3)
.
我们也不需要担心文本坐标。为了得到通常的标准化[0,1]
在整个可见视口的范围内,我们只需要使顶点的相应纹理坐标变大,并且重心插值对于任何视口像素都会产生与使用四边形时完全相同的结果。
这种方法当然可以与无属性渲染相结合,如德曼泽的回答 https://stackoverflow.com/a/51625078/2327517:
out vec2 texcoords; // texcoords are in the normalized [0,1] range for the viewport-filling quad part of the triangle
void main() {
vec2 vertices[3]=vec2[3](vec2(-1,-1), vec2(3,-1), vec2(-1, 3));
gl_Position = vec4(vertices[gl_VertexID],0,1);
texcoords = 0.5 * gl_Position.xy + vec2(0.5);
}
为什么单个三角形会更有效?
This is not关于保存一个顶点着色器调用,以及在前端处理的少一个三角形。使用单个三角形最显着的效果是更少的片段着色器调用
一旦图元的单个像素落入 2x2 像素大小的块(“四边形”),真正的 GPU 总是会调用该块的片段着色器。这对于计算窗口空间导函数 https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/dFdx.xhtml(纹理采样也隐含需要这些,请参见这个问题 https://stackoverflow.com/questions/52975878/what-is-in-simple-terms-texturegrad/52977548#52977548).
如果基元没有覆盖该块中的所有 4 个像素,则剩余的片段着色器调用将不会执行任何有用的工作(除了提供用于导数计算的数据之外),并且将被称为辅助调用(甚至可以通过gl_HelperInvocationGLSL功能 https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/gl_HelperInvocation.xhtml)。也可以看看Fabian“ryg”Giesen 的博客文章 https://fgiesen.wordpress.com/2011/07/10/a-trip-through-the-graphics-pipeline-2011-part-8/更多细节。
如果您渲染具有两个三角形的四边形,则两个三角形的一条边都会对角地穿过视口,并且在两个三角形上,您将在对角边生成大量无用的辅助调用。对于完美的正方形视口(纵横比 1),效果最差。如果您绘制单个三角形,则不会有这样的对角线边缘(它位于视口之外,根本不关心光栅化器),因此不会有额外的帮助器调用。
等一下,如果三角形延伸穿过视口边界,它不会被剪切并实际放置吗more在 GPU 上工作?
如果您阅读有关图形管道(甚至 GL 规范)的教科书材料,您可能会有这样的印象。但现实世界的 GPU 使用一些不同的方法,例如保护带削波。我不会在这里详细介绍(这将是一个单独的主题,请查看Fabian“ryg”Giesen 的精彩博客文章 https://fgiesen.wordpress.com/2011/07/05/a-trip-through-the-graphics-pipeline-2011-part-5/详细信息),但总体思路是,无论如何,光栅化器只会为视口(或剪刀矩形)内的像素生成片段,无论图元是否完全位于其中,因此我们可以简单地向其抛出更大的三角形,如果以下两项均属正确:
我们的三角形只比视口大 3 倍,所以我们可以非常确定根本不需要裁剪它。
但是这值得吗?
嗯,片段着色器调用的节省是真实的(特别是当您有复杂的片段着色器时),但在现实场景中总体效果可能几乎无法衡量。另一方面,该方法并不比使用全屏四边形更复杂,并且使用数据较少,所以即使可能不会产生巨大的差异,也不会造成伤害,所以为什么not使用它?
这种方法是否可以用于各种轴对齐矩形,而不仅仅是全屏矩形?
理论上,您可以将其与剪刀测试结合起来绘制一些任意轴对齐的矩形(剪刀测试将非常有效,因为它只是限制首先生成哪些片段,这不是真正的“测试” “在丢弃片段的硬件中)。但是,这需要您更改要绘制的每个矩形的剪刀参数,这意味着大量的状态更改并限制您每次绘制调用只能使用单个矩形,因此在大多数情况下这样做并不是一个好主意。