С технической точки зрения 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
(которые могут быть любым произвольно вычисленным значением в программе шейдера) для соседних пикселей? Как мы можем вычислить эти значения без больших накладных расходов, выполнив весь шейдерный расчет два (или три) раза?
Ну, вы знаете, что эти соседние значения все равно вычисляются, поскольку для соседнего пикселя вы также запускаете фрагментный шейдер. Так что все, что вам нужно, это доступ к этому соседнему вызову фрагмента шейдера при запуске для соседнего пикселя. Но это даже проще, потому что эти соседние значения также вычисляются в одно и то же время.
Современные растеризаторы называют фрагментные шейдеры в больших тайлах размером более одного соседнего пикселя. Наименьшее это будет сетка пикселей 2х2. И для каждого такого блока пикселей фрагментный шейдер вызывается для каждого пикселя, и эти вызовы выполняются в совершенно параллельном режиме блокировки, так что все вычисления выполняются в точно таком же порядке и в одно и то же время для каждого из этих пикселей в блоке. (именно поэтому также следует избегать ветвления в фрагментном шейдере, хотя и не смертельно, если это возможно, поскольку каждый вызов блока должен был бы исследовать каждую ветвь, которая занята хотя бы одним из вызовов, даже если он просто выбрасывает результаты после, а также указано в ответах на этот связанный вопрос). Таким образом, в любой момент фрагментный шейдер теоретически имеет доступ к значениям фрагментного шейдера соседних пикселей. И пока вы не имеете прямой доступ к этим значениям, вы имеете доступ к значениям вычисленных из них, как и производных функций dFdx
, dFdy
, fwidth
, ...
dFdx(p) = p(x1) - p(x)
, тоx1
может быть либо,(x+1)
либо(x-1)
, в зависимости от положения пикселяx
в квадре. В любом случае,x1
должен быть в том же варп / волновом фронте, что иx
. Я прав?