Если взять ваш пример, у вас есть ступенчатая функция расстояния, которая дает совершенно твердый (псевдоним) край. Простой способ сгладить круг - это превратить его в мягкий порог, например:
float distFromEdge = 1.0 - dist; // positive when inside the circle
float thresholdWidth = 0.01; // a constant you'd tune to get the right level of softness
float antialiasedCircle = saturate((distFromEdge / thresholdWidth) + 0.5);
return lerp(outsideColor, insideColor, antialiasedCircle);
Здесь я использовал фиксированный линейный уклон для функции мягкого порога, но вы также можете использовать smoothstepили что-то еще. + 0.5Является центр по трапу на математическом месте края. Во всяком случае, дело в том , что эта функция плавно изменяется от outsideColorдо insideColorв некотором диапазоне расстояний, так что если вы выбираете thresholdWidthсоответственно вы получите сглаженные красивый край.
Но как выбрать thresholdWidth? Если он слишком мал, вы снова получите псевдонимы, а если он будет слишком большим, то края будут чрезмерно размытыми. Более того, обычно это зависит от положения камеры: если distизмеряется в единицах измерения мирового пространства или пространства текстуры, то a, thresholdWidthкоторый работает для одной позиции камеры, будет неправильным для другой.
Вот где появляются производные экранного пространства (да, они ddxи ddyфункции, как вы уже догадались). Рассчитав длину градиента, distвы можете получить представление о том, как быстро он изменяется в пространстве экрана, и использовать его для оценки thresholdWidth, например:
float derivX = ddx(distFromEdge);
float derivY = ddy(distFromEdge);
float gradientLength = length(float2(derivX, derivY));
float thresholdWidth = 2.0 * gradientLength; // the 2.0 is a constant you can tune
У вас все еще есть значение, которое вы можете настроить, чтобы получить желаемый уровень мягкости, но теперь вы должны получать стабильные результаты независимо от положения камеры.
derivXи наderivYсамом деле представляю.