Come posso usare i derivati ​​dello spazio dello schermo per antialias una forma parametrica in un pixel shader?


8

Nel documento di ingrandimento alfa testato di Valve , menziona l'uso di "derivati ​​dello spazio dello schermo per pixel" per l'antialiasing. La mia comprensione è che questa è la ddxe ddyle funzioni intrinseche in HLSL?

Sto cercando di disegnare forme parametriche (ad esempio un cerchio: x² + y² <1 ) in uno shader e non so come usare questa tecnica per anti-alias corretto dei pixel del bordo della mia forma. Qualcuno può fornire un esempio?

Per completezza, ecco un esempio del tipo di pixel shader che sto realizzando:

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    float dist = input.TexCoord.x * input.TexCoord.x
               + input.TexCoord.y * input.TexCoord.y;
    if(dist < 1)
        return float4(0, 0, 0, 1);
    else
        return float4(1, 1, 1, 1);
}

Risposte:


10

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 smoothstepo qualcos'altro. Il + 0.5è centrare la rampa sulla posizione matematica del bordo. Comunque, il punto è che questa funzione modifica senza intoppi da outsideColora insideColorpiù di qualche intervallo di distanze, quindi se si sceglie thresholdWidthin 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 distmisurata in unità di spazio-mondo o spazio-trama, una thresholdWidthche funziona per una posizione di telecamera sarà sbagliata per un'altra.

Ecco dove entrano le derivate dello spazio dello schermo (sì, sono le funzioni ddxe ddycome 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.


Buona risposta - il codice funziona bene. +1 e accettato. Ma potrei aiutarti a espandere la tua risposta e spiegare perché funziona? Sono un po 'confuso su cosa derivXe in derivYrealtà rappresentano.
Andrew Russell,

@AndrewRussell Hai familiarità con i derivati, come nel calcolo? derivXe derivYsono solo (approssimazioni di) le derivate parziali di qualunque espressione in cui passi ddxe ddy, rispetto allo spazio dello schermo x e y. Se non hai studiato il calcolo, questo è un argomento più ampio di quello che posso spiegare qui. :)
Nathan Reed,

Il mio calcolo è un po 'arrugginito - ho dovuto andare e ri-imparare cos'è un derivato parziale . Ma da lì penso di averlo capito. Grazie :)
Andrew Russell,

OpenGL non supporta ddx / ddy, quindi in quei casi potresti aver bisogno di questo http.developer.nvidia.com/GPUGems/gpugems_ch25.html che fondamentalmente usa una trama pre-calcolata per identificare il livello di mipmap usato, per approssimare ddx / ddy.
Runonthespot,

2
@Runonthespot OpenGL ha derivati; si chiamano semplicemente qualcos'altro: dFdx/dFdy anziché ddx/ ddy.
Nathan Reed,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.