OpenGL文档指出fwidth returns the sum of the absolute value of derivatives in x and y

用较少的数学术语表示什么,有没有办法可视化?

基于我对函数的理解, fwidth(p)可以访问相邻像素中的p的值。在GPU上如何在不显着影响性能的情况下工作?它在所有像素上均可靠且均匀地工作吗?

#1 楼

像素屏幕空间派生确实会严重影响性能,但是无论您是否使用它们都会影响性能,因此从某种角度来看,它们是免费的!四个像素放在一起,并将它们置于相同的扭曲/波前,这实际上意味着它们在GPU上彼此相邻运行,因此从它们访问值非常便宜。因为扭曲/波前是按步长运行的,所以其他像素也将与您在着色器中的位置完全相同,因此这些像素的p的值将仅位于寄存器中等待您。即使其他三个像素的结果将被丢弃,也将始终执行。因此,覆盖单个像素的三角形将始终使四个像素着色并丢弃其中三个的结果,以使这些派生功能起作用!

这被认为是可以接受的成本(对于当前硬件)因为不仅仅像fwidth这样的函数使用这些派生类:每个单个纹理样本也都这样做,以便选择要读取的纹理mipmap。考虑:如果您非常靠近表面,则用于采样纹理的UV坐标将在屏幕空间中具有非常小的导数,这意味着您需要使用较大的mipmap,而如果距离更远,则UV坐标将具有屏幕空间中较大的导数,这意味着您需要使用较小的mipmap。

就数学上而言,它的含义较少:fwidth等效于abs(dFdx(p)) + abs(dFdy(p))dFdx(p)只是像素x + 1处的p的值与像素x处的p的值之间的差,对于dFdy(p)同样。

评论


$ \ begingroup $
因此,如果dFdx(p)= p(x1)-p(x),则x1可以是(x + 1)或(x-1),具体取决于四边形中像素x的位置。无论哪种方式,x1都必须与x处于相同的扭曲/波前。我对么?
$ \ endgroup $
– ApoorvaJ
2015年8月5日在15:44



$ \ begingroup $
@ApoorvaJ对于2x2网格中的2个相邻像素中的每个像素,dFdx的计算值基本上相同。而这个值只是使用两个相邻值之间的差来计算的,如果那是p(x + 1)-p(x)或p(x)-p(x-1)仅取决于您对x是正是在这里。结果是一样的。是的,你是对的。
$ \ endgroup $
–克里斯蒂安·劳(Christian Rau)
2015年8月5日15:46



$ \ begingroup $
@ChristianRau回答了我的问题。谢谢。
$ \ endgroup $
– ApoorvaJ
2015年8月5日15:47

#2 楼

用完全技术术语来说,fwidth(p)定义为<43,dFdx(p) / dFdy(p)是关于px屏幕尺寸的值y的偏导数。因此,它们表示当向右移动一个像素(p)或向上移动一个像素(x)时y的值如何表现。

现在如何计算它们?好吧,如果您知道p的相邻像素值,则可以将这些导数计算为直接有限差分,作为其实际数学导数的近似值(可能根本没有确切的解析解):

fwidth(p) := abs(dFdx(p)) + abs(dFdy(p))


但是,当然,现在您可能会问,我们如何知道相邻像素的p的值(毕竟它可能是着色器程序中的任意计算值)?我们如何通过两次(或三次)整个着色器计算来计算这些值而又不产生大量开销?还运行片段着色器。因此,您需要做的就是在为邻近像素运行时访问此邻近片段着色器调用。但这甚至更容易,因为这些相邻的值也是在同一时间计算的。

现代光栅化器将片段着色器称为大于一个相邻像素的较大图块。最小的像素将是2x2的像素网格。并且对于每个这样的像素块,为每个像素调用片段着色器,并且这些调用以完全并行的锁定步骤运行,从而对于该块中的每个像素,都以完全相同的顺序并在完全相同的时间执行所有计算(这也是为什么应该避免在片段着色器中分支的原因,尽管它不是致命的,但是如果可能的话,应该避免分支,因为对块的每次调用都必须探索至少一个调用所占用的每个分支,即使它只是丢弃之后的结果,以及与此相关问题的答案中所述的结果)。因此,在任何时候,片段着色器理论上都可以访问其相邻像素的片段着色器值。而且,虽然您无法直接访问这些值,但可以访问从它们计算出的值,例如派生函数dFdxdFdyfwidth,...