Apa itu fwidth dan bagaimana cara kerjanya?

18

The dokumentasi OpenGL menyatakan fwidth yang returns the sum of the absolute value of derivatives in x and y.

Apa artinya ini dalam istilah yang kurang matematis, dan adakah cara untuk memvisualisasikannya?

Berdasarkan pemahaman saya tentang fungsi, fwidth(p)memiliki akses ke nilai pdalam piksel tetangga. Bagaimana ini bekerja pada GPU tanpa secara drastis memengaruhi kinerja, dan apakah itu bekerja dengan andal dan seragam di semua piksel?

ApoorvaJ
sumber

Jawaban:

18

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 ppiksel 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 fwidthyang 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: fwidthsama dengan abs(dFdx(p)) + abs(dFdy(p)). dFdx(p)hanyalah perbedaan antara nilai ppada piksel x + 1 dan nilai ppada piksel x, dan juga untuk dFdy(p).

John Calsbeek
sumber
Jadi, jika dFdx(p) = p(x1) - p(x), maka x1dapat berupa salah satu (x+1)atau (x-1), tergantung pada posisi piksel xdi quad. Either way, x1harus berada di warp / wavefront yang sama dengan x. Apakah saya benar?
ApoorvaJ
3
@ApoorvaJ Pada dasarnya nilai yang sama untuk dFdxdihitung untuk masing-masing dari 2 piksel tetangga di kisi 2x2. Dan nilai ini hanya dihitung dengan menggunakan perbedaan antara dua nilai tetangga, jika itu p(x+1)-p(x)atau p(x)-p(x-1)hanya tergantung pada gagasan Anda tentang apa xpersis di sini. Namun hasilnya sama. Jadi ya, Anda benar.
Chris berkata Reinstate Monica
@ChristianRau Itu menjawab pertanyaanku. Terima kasih.
ApoorvaJ
11

Dalam istilah yang sepenuhnya teknis, fwidth(p)didefinisikan sebagai

fwidth(p) := abs(dFdx(p)) + abs(dFdy(p))

Dan dFdx(p)/ dFdy(p)adalah turunan parsial dari nilai psehubungan dengan xdan ylayar dimensi. Jadi mereka menunjukkan bagaimana nilai pberperilaku 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):

dFdx(p) := p(x+1) - p(x)

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, ...

Kata Chris Reinstate Monica
sumber