Menggunakan LUT untuk mempercepat trigonometri berat untuk perangkat seluler

10

Saya mencoba membuat shader ini berjalan di iDevice yang sangat tua, dan juga Android.

Bahkan ketika saya mengurangi kode ke 2 fungsi sinus per fragmen shader berjalan sekitar 20fps.

Saya telah mempertimbangkan mengambil daun dari buku teknik naungan lama dan menciptakan sebuah array yang menampung sekelompok nilai trigonometri yang telah ditentukan dan entah bagaimana menggunakannya untuk memperkirakan shader.

Dalam shader yang saya tautkan di atas, saya sudah mensimulasikan bahwa dengan membulatkan nilai-nilai yang dikirim ke fungsi trigon, semakin jauh meninggalkan mouse (saat turun) semakin sedikit kualitas shader. Ini sebenarnya cukup keren karena sangat dekat dengan sisi kiri sepertinya shader yang benar-benar berbeda dan sangat keren.

Pokoknya saya punya dua dilema:

  1. Saya tidak tahu apa cara paling efisien untuk memiliki array nilai 360 seperti di dalamnya dalam shader GLSL, konstan atau seragam?
  2. Saya tidak tahu bagaimana cara menempatkan angka dalam kisaran seperti biasanya jika saya ingin sudut antara 0 dan 360 (ya saya tahu GPU menggunakan radian) Saya akan melakukannya seperti itu.

    func range(float angle)
    {
       float temp = angle
       while (temp > 360) {temp -= 360;}
       while (temp < 0)   {temp += 360;}
       return temp;
    }
    

    Namun GLSL tidak memungkinkan saat loop atau fungsi rekursif.

J.Apakah
sumber
Saya tidak tahu apakah akan praktis untuk mengimplementasikan ini, tetapi apakah akan membantu untuk memiliki nilai sinus yang dikomputasi spasi tidak merata, dengan nilai-nilai lebih terkelompok di mana kemiringan kurva sinus paling curam, dan nilai yang lebih sedikit di mana ia keluar dan tingkat tidak banyak berubah? Apakah ini memungkinkan akurasi yang lebih tinggi di mana diperlukan, tanpa perlu menyimpan sejumlah besar nilai?
trichoplax
5
Mengenai # 2, modfungsi bawaan adalah yang Anda inginkan. Anda akan menulis mod(angle, 360.0).
Nathan Reed
1
@trichoplax ide cemerlang tapi saya tidak tahu bagaimana Anda bisa mencari nilai di atas meja itu. Katakanlah kita meletakkan mereka dalam array dengan beberapa dari mereka lebih berkonsentrasi. Bagaimana kita bisa menemukan indeks yang tepat?
J. Aduh
6
Bagaimana dengan menempatkan nilai Anda ke dalam tekstur 1D 3-saluran? Dengan begitu Anda bisa mendapatkan dosa, cos dan tan untuk harga pencarian tekstur tunggal. Jika Anda memetakan sudut 0 - 2pi ke 0 - 1 UV dan menggunakan mode tekstur berulang, Anda bahkan tidak memerlukan mod call, itu akan 'membungkus' secara otomatis, dan Anda juga bisa mendapatkan perkiraan linear-filtered di antara nilai yang tersimpan daripada gertakan ke yang terdekat.
russ
3
Banyak waktu Anda dapat menghilangkan fungsi trigonometri ketika digunakan untuk geometri dengan tidak menggunakan sudut tetapi mulai dan diakhiri dengan pasangan sin / cos dan menggunakan identitas trigonometri untuk setengah sudut dan semacamnya.
ratchet freak

Jawaban:

8

Sudah disarankan dalam komentar berulang kali, tetapi tidak ada yang merasa perlu untuk memberikan jawaban yang tepat, jadi demi kelengkapan, solusi langsung dan umum untuk masalah ini mungkin menggunakan tekstur sebagai tabel pencarian, khususnya tekstur 1D yang berisi semua nilai fungsi Anda untuk rentang input yang dimungkinkan (yaitu / ). Ini memiliki berbagai keunggulan:[ 0 , 2 π )[0,360)[0,2π)

  • Itu menggunakan koordinat dinormalisasi, yaitu Anda mengakses tekstur dengan memetakan sudut Anda dari ke . Ini berarti shader Anda sebenarnya tidak perlu peduli dengan jumlah nilai tertentu . Anda dapat menyesuaikan ukurannya sesuai dengan memori / kecepatan vs pengorbanan kualitas apa pun yang Anda inginkan (dan terutama pada perangkat keras yang lebih lama / tertanam, Anda mungkin menginginkan kekuatan dua sebagai ukuran tekstur).[ 0 , 1 ][0,360][0,1]
  • Anda mendapatkan manfaat tambahan dari tidak harus melakukan penyesuaian interval seperti loop (walaupun, Anda tidak perlu loop lagi dan hanya bisa menggunakan operasi modulus). Cukup gunakan GL_REPEATsebagai mode pembungkus untuk tekstur dan itu akan secara otomatis mulai dari awal lagi ketika mengakses dengan argumen> 1 (dan juga untuk argumen negatif).
  • Dan Anda juga mendapatkan manfaat interpolasi linier antara dua nilai pada dasarnya array gratis (atau katakanlah hampir gratis) dengan menggunakan GL_LINEARsebagai filter tekstur, cara ini mendapatkan nilai yang Anda bahkan tidak menyimpan. Tentu saja interpolasi linier tidak 100% akurat untuk fungsi trigonometri, tetapi tentu saja lebih baik daripada tidak ada interpolasi.
  • Anda dapat menyimpan lebih dari satu nilai dalam tekstur dengan menggunakan tekstur RGBA (atau seberapa banyak komponen yang Anda butuhkan). Dengan cara ini Anda bisa mendapatkan mis dosa dan cos dengan pencarian tekstur tunggal.
  • Untuk sin dan cos, Anda hanya perlu menyimpan nilai dalam , yang secara alami Anda dapat naik kelas dari rentang normal dari format titik tetap 8-bit yang umum. Namun, itu mungkin tidak cukup presisi untuk kebutuhan Anda. Beberapa orang menyarankan untuk menggunakan nilai floating point 16-bit, karena nilai tersebut lebih tepat daripada nilai fixed point normal 8-bit tetapi kurang intensif memori daripada float 32-bit yang sebenarnya. Tapi sekali lagi, saya juga tidak tahu apakah implementasi Anda mendukung tekstur titik mengambang untuk memulai. Jika tidak, maka mungkin Anda dapat menggunakan 2 komponen titik tetap 8-bit dan menggabungkannya menjadi satu nilai dengan sesuatu seperti (atau bahkan lebih banyak komponen untuk gandum yang lebih halus). Ini memungkinkan Anda mendapat keuntungan dari tekstur multi-komponen lagi.[ 0 , 1 ][1,1][0,1]float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;

Tentu saja masih harus dievaluasi jika ini adalah solusi yang lebih baik, karena akses tekstur juga tidak sepenuhnya gratis, serta kombinasi terbaik dari ukuran dan format tekstur.

Untuk mengisi tekstur dengan data dan memberikan komentar pada salah satu komentar Anda, Anda harus mempertimbangkan bahwa pemfilteran tekstur mengembalikan nilai tepat di pusat texel , yaitu tekstur terkoordinasi dengan setengah ukuran texel. Jadi ya, Anda harus menghasilkan nilai pada .5texels , yaitu sesuatu seperti ini dalam kode aplikasi:

float texels[256];
for(unsigned int i = 0; i < 256; ++i)
    texels[i] = sin((i + .5f) / 256.f) * TWO_PI);
glTexImage1D(GL_TEXTURE_1D, 0, ..., 256, 0, GL_RED, GL_FLOAT, texels);

Namun, Anda mungkin masih ingin membandingkan kinerja pendekatan ini dengan pendekatan yang menggunakan array seragam kecil (yaitu uniform float sinTable[361], atau mungkin kurang dalam praktiknya, perhatikan batas implementasi Anda pada ukuran array seragam) yang baru saja Anda muat dengan nilai masing-masing menggunakan glUniform1fvdan akses dengan menyesuaikan sudut Anda ke menggunakan fungsi dan membulatkannya ke nilai terdekat:[0,360)mod

angle = mod(angle, 360.0);
float value = sinTable[int(((angle < 0.0) ? (angle + 360.0) : angle) + 0.5)];
Kata Chris Reinstate Monica
sumber
1
Berikut ini adalah ekstensi yang menarik untuk menyimpan mencari tabel dalam tekstur. Itu (ab) menggunakan interpolasi tekstur N-linier untuk mendapatkan interpolasi urutan yang lebih tinggi (alias lebih baik dari linier) dari titik data, permukaan, volume, dan hypervolume. blog.demofox.org/2016/02/22/…
Alan Wolfe