Tomando su ejemplo, tiene una función de paso de la distancia, que produce un borde perfectamente duro (alias). Una forma simple de antialias del círculo sería convertirlo en un umbral blando, como:
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);
Aquí utilicé una rampa lineal sujeta para una función de umbral suave, pero también podría usar smoothstep
o algo más. El + 0.5
es centrar la rampa en la ubicación matemática del borde. De todos modos, el punto es que esta función cambia suavemente desde outsideColor
que insideColor
más de algún rango de distancias, por lo que si tienes que elegir thresholdWidth
adecuadamente obtendrá una ventaja mirando antialiased.
¿Pero cómo debes elegir thresholdWidth
? Si es demasiado pequeño, volverá a aparecer el alias, y si es demasiado grande, el borde estará demasiado borroso. Además, generalmente dependerá de la posición de la cámara: si dist
se mide en unidades de espacio mundial o espacio de textura, entonces una thresholdWidth
que funcione para una posición de cámara será incorrecta para otra.
Aquí es donde entran las derivadas del espacio de pantalla (sí, son las funciones ddx
y ddy
como lo adivinó). Al calcular la longitud del gradiente dist
, puede hacerse una idea de qué tan rápido está cambiando en el espacio de la pantalla y usarlo para estimar thresholdWidth
, por ejemplo:
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
Aún tiene un valor que puede ajustar para obtener el nivel deseado de suavidad, pero ahora debería obtener resultados consistentes independientemente de la posición de la cámara.
derivX
yderivY
realmente representar.