Prendendo il tuo esempio, hai una funzione passo della distanza, che produce un bordo perfettamente duro (aliasato). Un modo semplice per antialias il cerchio sarebbe quello di trasformarlo in una soglia morbida, come:
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);
Qui ho usato una rampa lineare bloccata per una funzione di soglia morbida, ma potresti anche usare smoothstep
o qualcos'altro. Il + 0.5
è centrare la rampa sulla posizione matematica del bordo. Comunque, il punto è che questa funzione modifica senza intoppi da outsideColor
a insideColor
più di qualche intervallo di distanze, quindi se si sceglie thresholdWidth
in modo appropriato si otterrà un vantaggio antialiased dall'aspetto.
Ma come dovresti scegliere thresholdWidth
? Se è troppo piccolo, otterrai di nuovo l'aliasing e se è troppo grande il bordo sarà eccessivamente sfocato. Inoltre, dipenderà generalmente dalla posizione della telecamera: se dist
misurata in unità di spazio-mondo o spazio-trama, una thresholdWidth
che funziona per una posizione di telecamera sarà sbagliata per un'altra.
Ecco dove entrano le derivate dello spazio dello schermo (sì, sono le funzioni ddx
e ddy
come hai intuito). Calcolando la lunghezza del gradiente dist
, puoi avere un'idea di quanto velocemente stia cambiando nello spazio dello schermo e utilizzarlo per stimare thresholdWidth
, come:
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
Hai ancora un valore che puoi sintonizzare per ottenere il livello di morbidezza desiderato, ma ora dovresti ottenere risultati coerenti indipendentemente dalla posizione della telecamera.
derivX
e inderivY
realtà rappresentano.