Если взять ваш пример, у вас есть ступенчатая функция расстояния, которая дает совершенно твердый (псевдоним) край. Простой способ сгладить круг - это превратить его в мягкий порог, например:
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
самом деле представляю.