Lấy ví dụ của bạn, bạn có một hàm bước của khoảng cách, tạo ra một cạnh hoàn toàn cứng (bí danh). Một cách đơn giản để chống lại vòng tròn sẽ là biến nó thành một ngưỡng mềm, như:
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);
Ở đây tôi đã sử dụng đoạn đường nối được kẹp cho hàm ngưỡng mềm, nhưng bạn cũng có thể sử dụng smoothstep
hoặc một cái gì đó khác. Đây + 0.5
là để tập trung các đoạn đường nối vào vị trí toán học của cạnh. Dù sao, vấn đề là chức năng này trơn tru thay đổi từ outsideColor
đến insideColor
qua một số phạm vi khoảng cách, vì vậy nếu bạn chọn thresholdWidth
một cách thích hợp bạn sẽ nhận được một cạnh chống răng cưa nhìn.
Nhưng bạn nên chọn thresholdWidth
như thế nào? Nếu nó quá nhỏ, bạn sẽ lại bị răng cưa và nếu nó quá lớn thì cạnh sẽ bị mờ quá mức. Ngoài ra, thông thường sẽ phụ thuộc vào vị trí camera: nếu dist
được đo bằng đơn vị không gian thế giới hoặc không gian kết cấu, thì một thresholdWidth
vị trí hoạt động cho một vị trí camera sẽ bị sai đối với vị trí khác.
Đây là nơi các dẫn xuất không gian màn hình xuất hiện (vâng, chúng là ddx
và các ddy
chức năng như bạn đoán). Bằng cách tính toán độ dài của độ dốc, dist
bạn có thể biết được tốc độ thay đổi trong không gian màn hình và sử dụng điều đó để ước tính thresholdWidth
, như:
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
Bạn vẫn có một giá trị bạn có thể điều chỉnh để có được mức độ mềm mong muốn, nhưng bây giờ bạn sẽ nhận được kết quả nhất quán bất kể vị trí máy ảnh.
derivX
vàderivY
thực sự đại diện.