Answers:
像素屏幕空间派生确实会严重影响性能,但是无论您是否使用它们,它们都会影响性能,因此从某种角度来看,它们是免费的!
最近的历史记录中的每个GPU都将四个像素的四分之一压缩在一起,并将它们置于相同的扭曲/波前,这实际上意味着它们在GPU上彼此相邻运行,因此从它们访问值非常便宜。因为扭曲/波前是按步长运行的,所以其他像素也将与您在着色器中的位置完全相同,因此p
这些像素的值将仅位于寄存器中等待您。即使其他三个像素的结果将被丢弃,也将始终执行。因此,覆盖单个像素的三角形将始终使四个像素着色并丢弃其中三个的结果,以使这些派生功能起作用!
对于当前的硬件,这被认为是可以接受的成本,因为它不只是fwidth
使用这些派生类的函数:每个单个纹理样本也都可以这样做,以便选择要读取的纹理mipmap。考虑:如果您非常靠近曲面,则用于采样纹理的UV坐标将在屏幕空间中具有非常小的导数,这意味着您需要使用较大的mipmap,而如果距离更远,则UV坐标将具有屏幕空间中较大的导数,这意味着您需要使用较小的mipmap。
就用较少的数学术语表示的意义:fwidth
等效于abs(dFdx(p)) + abs(dFdy(p))
。dFdx(p)
只是p
像素x + 1处的值与p
像素x处的值之差,类似地适用于dFdy(p)
。
dFdx
2x2网格中的2个相邻像素中的每个像素计算相同的值。而这个值只是使用两个相邻值之间的差来计算的,这取决于p(x+1)-p(x)
或p(x)-p(x-1)
仅取决于您对x
此处确切含义的理解。结果是一样的。是的,你是对的。
完全以技术术语fwidth(p)
定义为
fwidth(p) := abs(dFdx(p)) + abs(dFdy(p))
和dFdx(p)
/ dFdy(p)
是p
相对于x
和y
屏幕尺寸的值的偏导数。因此,它们表示p
当向右移动x
一个像素()或向上移动一个像素(y
)时,值的行为。
现在如何计算它们?好吧,如果您知道的相邻像素的值p
,则可以将这些导数计算为直接有限差分,作为其实际数学导数的近似值(它们可能根本没有确切的解析解):
dFdx(p) := p(x+1) - p(x)
但是,当然,现在您可能会问,我们如何知道p
相邻像素的值(毕竟它可能是着色器程序中的任意计算值)?我们如何通过两次(或三次)整个着色器计算来计算这些值而又不产生大量开销?
好了,您知道这些相邻值是如何计算的,因为对于相邻像素,您还运行了片段着色器。因此,您需要做的就是在为邻近像素运行时访问该邻近片段着色器调用。但这更容易,因为这些相邻的值也是在同一时间计算的。
现代光栅化器将片段着色器称为大于一个相邻像素的较大图块。最小的像素将是2x2的像素网格。并且对于每个这样的像素块,为每个像素调用片段着色器,并且这些调用以完全并行的锁定步骤运行,以便针对该块中的每个像素,以完全相同的顺序并在相同的时间进行所有计算(这也是为什么应该避免在片段着色器中分支的原因,尽管它不是致命的,但如果可能的话,应该避免分支,因为对块的每次调用都必须探究至少一个调用所占用的每个分支,即使它只是丢弃了之后的结果,以及与此相关问题的答案中也提到的)。因此,片段着色器理论上随时都可以访问其相邻像素的片段着色器值。虽然你没有这些价值观的直接访问,你有机会从他们计算的值,如衍生功能dFdx
,dFdy
,fwidth
,...
dFdx(p) = p(x1) - p(x)
,则x1
可以是(x+1)
或(x-1)
,具体取决于x
四边形中像素的位置。无论哪种方式,x1
都必须与相同x
。我对么?