Derivatif layar-ruang Pixel benar- benar memengaruhi kinerja, tetapi memengaruhi kinerja apakah Anda menggunakannya atau tidak, jadi dari sudut pandang tertentu, mereka gratis!
Setiap GPU dalam sejarah baru-baru ini mengemas quad empat piksel bersama-sama dan menempatkan mereka di warp / wavefront yang sama, yang pada dasarnya berarti mereka berjalan tepat di samping satu sama lain pada GPU, sehingga mengakses nilai dari mereka sangat murah. Karena warps / wavefronts dijalankan berbaris, piksel lainnya juga akan berada di tempat yang sama persis di shader seperti Anda, sehingga nilai p
piksel tersebut hanya akan duduk di register menunggu Anda. Tiga piksel lainnya akan selalu dieksekusi, meskipun hasilnya akan dibuang. Jadi segitiga yang menutupi satu piksel akan selalu menaungi empat piksel dan membuang hasil ketiganya, hanya agar fitur turunan ini bekerja!
Ini dianggap sebagai biaya yang dapat diterima (untuk perangkat keras saat ini) karena bukan hanya fungsi seperti fwidth
yang menggunakan turunan ini: setiap sampel tekstur juga melakukan hal yang sama, untuk memilih mipmap dari tekstur Anda untuk dibaca. Pertimbangkan: jika Anda sangat dekat dengan permukaan, koordinat UV yang Anda gunakan untuk sampel tekstur akan memiliki turunan yang sangat kecil dalam ruang layar, artinya Anda perlu menggunakan mipmap yang lebih besar, dan jika Anda lebih jauh koordinat UV akan memiliki turunan yang lebih besar di ruang layar, artinya Anda perlu menggunakan mipmap yang lebih kecil.
Sejauh apa artinya dalam istilah matematika yang kurang: fwidth
sama dengan abs(dFdx(p)) + abs(dFdy(p))
. dFdx(p)
hanyalah perbedaan antara nilai p
pada piksel x + 1 dan nilai p
pada piksel x, dan juga untuk dFdy(p)
.
dFdx(p) = p(x1) - p(x)
, makax1
dapat berupa salah satu(x+1)
atau(x-1)
, tergantung pada posisi pikselx
di quad. Either way,x1
harus berada di warp / wavefront yang sama denganx
. Apakah saya benar?dFdx
dihitung untuk masing-masing dari 2 piksel tetangga di kisi 2x2. Dan nilai ini hanya dihitung dengan menggunakan perbedaan antara dua nilai tetangga, jika itup(x+1)-p(x)
ataup(x)-p(x-1)
hanya tergantung pada gagasan Anda tentang apax
persis di sini. Namun hasilnya sama. Jadi ya, Anda benar.Dalam istilah yang sepenuhnya teknis,
fwidth(p)
didefinisikan sebagaiDan
dFdx(p)
/dFdy(p)
adalah turunan parsial dari nilaip
sehubungan denganx
dany
layar dimensi. Jadi mereka menunjukkan bagaimana nilaip
berperilaku ketika pergi satu piksel ke kanan (x
) atau satu piksel naik (y
).Sekarang bagaimana mereka bisa dihitung secara praktis? Nah, jika Anda tahu nilai piksel tetangga untuk
p
, Anda bisa menghitung derivasi tersebut sebagai perbedaan hingga langsung sebagai perkiraan untuk turunan matematika aktual (yang mungkin tidak memiliki solusi analitik yang tepat sama sekali):Tapi tentu saja sekarang Anda mungkin bertanya, bagaimana kita bahkan tahu nilai-nilai
p
(yang nantinya bisa menjadi nilai yang dihitung secara sewenang-wenang di dalam program shader) untuk piksel tetangga? Bagaimana kita menghitung nilai-nilai itu tanpa menimbulkan overhead besar dengan melakukan seluruh perhitungan shader dua (atau tiga) kali?Nah, Anda tahu, nilai-nilai tetangga dihitung pula, karena untuk piksel tetangga Anda juga menjalankan shader fragmen. Jadi yang Anda butuhkan hanyalah akses ke permintaan shader fragmen tetangga saat dijalankan untuk piksel tetangga. Tetapi itu bahkan lebih mudah, karena nilai-nilai tetangga juga dihitung pada waktu yang bersamaan.
Rasterizer modern menyebut fragmen shader dalam ubin yang lebih besar dengan lebih dari satu piksel tetangga. Paling tidak itu adalah grid 2x2 piksel. Dan untuk setiap blok piksel seperti itu, shader fragmen dipanggil untuk setiap piksel dan doa-doa itu berjalan dalam langkah kunci paralel sempurna sehingga semua perhitungan dilakukan dalam urutan yang persis sama dan pada waktu yang sama persis untuk masing-masing piksel dalam blok tersebut. (yang juga mengapa bercabang di shader fragmen, sementara tidak mematikan, harus dihindari jika memungkinkan, karena setiap doa blok harus menjelajahi setiap cabang yang diambil oleh setidaknya satu dari doa, bahkan jika itu hanya membuang) hasilnya setelah itu, seperti juga dikemukakan dalam jawaban untuk pertanyaan terkait ini). Jadi setiap saat, sebuah shader fragmen secara teoritis memiliki akses ke nilai-nilai shader fragmen piksel yang berdekatan. Dan sementara Anda tidak memiliki akses langsung ke nilai-nilai, Anda memiliki akses ke nilai-nilai dihitung dari mereka, seperti fungsi turunan
dFdx
,dFdy
,fwidth
, ...sumber