Untuk rendering grafik yang kompleks pada halaman web, ada Web Graphics Library, disingkat sebagai WebGL. Perancang antarmuka Dmitry Vasiliev berbicara tentang pemrograman GPU dari sudut pandang perancang tata letak, tentang apa itu WebGL dan bagaimana kami memecahkan masalah memvisualisasikan data cuaca besar menggunakan teknologi ini.
- Saya sedang mengembangkan antarmuka di kantor Yekaterinburg Yandex. Saya mulai dalam kelompok Olahraga. Kami sedang mengembangkan proyek olahraga khusus ketika ada Kejuaraan Dunia dalam hoki, sepak bola, Olimpiade, Paralimpiade dan acara keren lainnya. Saya juga bekerja pada pengembangan hasil pencarian khusus, yang didedikasikan untuk trek Sochi baru.




Selain itu, dalam satu setengah helm, kami memulai kembali layanan Work on Errors. Dan kemudian pekerjaan dimulai di Pogoda, di mana saya terlibat dalam mendukung pengoperasian API, pengembangannya, menulis infrastruktur di sekitar API ini dan menulis pengikat simpul untuk formula pembelajaran mesin yang terlatih.

Kemudian pekerjaan mulai lebih menarik. Berpartisipasi dalam desain ulang layanan cuaca kami. Desktop, gerobak dorong.


Setelah kami menyusun prakiraan standar, kami memutuskan untuk membuat prakiraan yang tidak dimiliki siapa pun. Prakiraan ini adalah ramalan untuk pergerakan curah hujan di seluruh wilayah.

Ada radar cuaca khusus yang mendeteksi curah hujan dalam radius 2000 km, mereka tahu kepadatan dan jaraknya.

Dengan menggunakan data ini dan memprediksi dengan bantuan mesin mempelajari pergerakan mereka selanjutnya, kami membuat
visualisasi pada peta. Anda bisa bergerak maju dan mundur.

Kami melihat ulasan orang. Orang-orang menyukainya. Semua jenis meme mulai muncul, dan ada foto-foto keren ketika Moskow membanjiri neraka.
Karena semua orang menyukai formatnya, kami memutuskan untuk melanjutkan dan mencurahkan ramalan berikut ke angin.

Layanan yang menunjukkan perkiraan angin sudah ada di sana. Ini adalah beberapa yang berkelas yang menonjol.

Melihat mereka, kami menyadari bahwa kami ingin melakukan hal yang sama - atau setidaknya tidak lebih buruk.
Oleh karena itu, kami memutuskan untuk memvisualisasikan partikel yang bergerak dengan lancar di peta tergantung pada kecepatan angin, dan meninggalkan semacam lingkaran sehingga mereka dapat dilihat, lintasan angin dapat terlihat.
Karena kami sudah hebat dan membuat peta keren dengan curah hujan menggunakan kanvas 2D, kami memutuskan untuk melakukan hal yang sama dengan partikel.

Setelah berkonsultasi dengan perancang, kami menyadari bahwa kami perlu mengisi sekitar 6% layar dengan partikel untuk mendapatkan efek keren.
Untuk menggambar sejumlah partikel menggunakan pendekatan standar, kami memiliki waktu minimum 5 ms.

Jika Anda berpikir bahwa kita masih perlu menggerakkan partikel dan membawa beberapa jenis keindahan seperti menggambar ekor partikel, kita dapat mengasumsikan bahwa kita akan jatuh untuk waktu minimum 40 ms untuk menampilkan animasi yang halus untuk menghasilkan setidaknya 25 frame per detik.
Masalahnya adalah bahwa di sini setiap partikel akan diproses secara berurutan. Tetapi bagaimana jika Anda memprosesnya secara paralel?
Perbedaan yang jelas antara pengoperasian prosesor sentral dan grafis ditunjukkan oleh "Legend Destroyers" di salah satu konferensi. Mereka meluncurkan mesin tempat penanda paintball dipasang, tugasnya adalah menggambar smiley dalam satu warna. Dalam sekitar 10 detik, dia menggambar seperti itu. (
Tautan ke video - kira-kira.)



Kemudian orang-orang meluncurkan sampan, yang merupakan GPU, dan beberapa meludah melukis Mona Lisa. Inilah perbedaan kecepatan komputasi CPU dan GPU.





Untuk memanfaatkan fitur-fitur tersebut di browser, teknologi WebGL diciptakan.
Apa ini Dengan pertanyaan ini, saya naik ke Internet. Menambahkan beberapa kata dengan animasi partikel dan angin, saya menemukan beberapa artikel.

Salah satunya adalah demo Vladimir Agafonkin, seorang insinyur dari Mapbox, yang membuat angin di WebGL dan merujuk ke blog Chris Wellons, yang berbicara tentang cara memindahkan dan menyimpan keadaan partikel pada GPU.
Kami mengambil dan menyalin. Kami mengharapkan hasil seperti itu. Di sini partikel-partikel bergerak dengan lancar.

Kami tidak mengerti apa.

Mencoba mencari tahu kodenya. Meningkatkan, sekali lagi mendapatkan hasil yang tidak memuaskan. Kami mendaki lebih dalam lagi - kami mendapat hujan alih-alih angin.

Oke, kami memutuskan untuk melakukannya sendiri.

Untuk bekerja dengan WebGL, ada kerangka kerja. Hampir semuanya ditujukan untuk bekerja dengan objek 3D. Kami tidak membutuhkan kemampuan 3D ini. Kita hanya perlu menggambar partikel dan memindahkannya. Karena itu, kami memutuskan untuk melakukan segalanya dengan tangan kami.

Saat ini ada dua versi teknologi WebGL. Versi kedua, yang keren, memiliki versi bahasa pemrograman modern yang tinggi di mana program dijalankan dalam adapter grafis, dapat langsung melakukan perhitungan, dan tidak hanya menggambar. Tetapi memiliki kompatibilitas yang buruk.

Yah, kami memutuskan untuk menggunakan WebGL 1 yang sudah terbukti lama, yang memiliki dukungan yang baik selain Opera Mini, yang tidak diperlukan siapa pun.

WebGL adalah hal dua komponen. Ini adalah JS yang menjalankan status program yang berjalan pada kartu grafis. Dan ada komponen yang berjalan langsung di kartu grafis.
Mari kita mulai dengan JS. WebGL hanyalah konteks yang tepat untuk elemen kanvas. Selain itu, setelah menerima konteks ini, bukan hanya objek spesifik yang dialokasikan, sumber daya besi dialokasikan. Dan jika kita menjalankan sesuatu yang indah di WebGL di browser, dan kemudian memutuskan untuk bermain Quake, maka sangat mungkin bahwa sumber daya ini akan hilang, dan konteksnya bisa hilang, dan seluruh program Anda akan rusak.

Karena itu, ketika bekerja dengan WebGL, Anda juga harus mendengarkan hilangnya konteks dan dapat memulihkannya. Karena itu, saya menekankan bahwa init adalah.

Selanjutnya, semua pekerjaan JS bermuara pada mengumpulkan program yang berjalan pada GPU, mengirimkannya kartu grafis, mengatur beberapa parameter dan mengatakan "menggambar".

Di WebGL, jika Anda melihat elemen konteks itu sendiri, Anda melihat sekelompok konstanta. Konstanta ini merujuk ke alamat dalam memori. Mereka tidak benar-benar konstan dalam proses program. Karena jika konteksnya hilang dan dikembalikan lagi, kumpulan alamat lain mungkin dialokasikan, dan konstanta ini akan berbeda untuk konteks saat ini. Oleh karena itu, hampir semua operasi di WebGL di sisi JS dilakukan melalui utilitas. Tidak ada yang ingin melakukan pekerjaan rutin untuk menemukan alamat dan sampah lainnya.

Kami beralih ke apa yang dilakukan pada kartu video itu sendiri - sebuah program yang terdiri dari dua set instruksi yang ditulis dalam bahasa C-like GLSL. Instruksi ini disebut vertex shader dan fragment shader. Suatu program dibuat dari pasangannya.

Apa perbedaan antara shader ini? Vertex shader menetapkan permukaan tempat sesuatu harus ditarik. Setelah primitif diatur, yang harus dicat, shader fragmen yang termasuk dalam kisaran ini disebut.


Dalam kode, tampilannya seperti ini. Shader memiliki bagian untuk mendeklarasikan variabel yang ditetapkan secara eksternal dari JS, tipe dan namanya ditentukan. Serta bagian utama, yang mengeksekusi kode yang diperlukan untuk iterasi ini.
Vertex shader dalam kebanyakan kasus diharapkan untuk mengatur variabel gl_Position ke beberapa koordinat dalam ruang empat dimensi. Ini adalah x, y, z dan lebar ruang, yang tidak perlu diketahui saat ini.
Shader fragmen berharap untuk mengatur warna piksel tertentu.
Dalam contoh ini, kami memiliki warna piksel yang dipilih dari tekstur yang terhubung.

Untuk mentransfer ini ke JS, cukup bungkus kode sumber shader dalam variabel.

Selanjutnya variabel-variabel ini ditransformasikan menjadi shader. Ini adalah konteks WebGL, kami membuat shader dari kode sumber, membuat program secara paralel, dan kami melampirkan beberapa shader ke program. Kami mendapatkan program yang bisa diterapkan.
Dalam perjalanan, kami memverifikasi bahwa kompilasi shader berhasil, bahwa program berhasil dibuat. Kami mengatakan bahwa Anda perlu menggunakan program ini, karena mungkin ada beberapa program untuk nilai rendering yang berbeda.
Atur dan katakan gambar. Ternyata semacam gambar.

Naik lebih dalam. Di vertex shader, semua perhitungan dilakukan dalam ruang dari -1 hingga 1, terlepas dari ukuran titik output Anda. Misalnya, ruang dari -1 hingga 1 dapat menempati seluruh layar 1920x1080. Untuk menggambar segitiga di tengah layar, Anda perlu menggambar permukaan yang mencakup koordinat 0, 0.

Shader fragmen bekerja di ruang dari 0 hingga 1, dan warna ditampilkan oleh empat komponen: R, G, B, Alpha.
Menggunakan CSS sebagai contoh, Anda mungkin menemukan notasi warna yang sama saat menggunakan persentase.

Untuk menggambar sesuatu, Anda perlu mengatakan data apa yang perlu diambil. Khusus untuk segitiga, kita mendefinisikan larik yang diketik dari tiga simpul, masing-masing terdiri dari tiga komponen, x, y dan cukup.
Untuk kasus ini, vertex shader terlihat seperti mendapatkan pasangan titik saat ini, koordinat, serta mengatur koordinat ini di layar. Di sini, sebagaimana adanya, tanpa transformasi, kami menempatkan titik di layar.

Shader fragmen dapat mewarnai konstanta yang dilewatkan dari JS dengan warna, juga tanpa perhitungan tambahan. Selain itu, jika beberapa variabel dalam fragmen shader ditransfer dari luar atau dari shader sebelumnya, maka akurasi harus ditentukan. Dalam hal ini, akurasi sedang sudah cukup, dan hampir selalu sudah cukup.

Kami lolos ke JS. Kami menetapkan shader yang sama ke variabel dan mendeklarasikan fungsi yang akan membuat shader ini. Yaitu, shader dibuat, sumber dituangkan ke dalamnya, dan kemudian dikompilasi.

Kami membuat dua shader, vertex dan fragmen.

Setelah itu, buat program, unggah shader yang sudah dikompilasi ke dalamnya. Kami mengikat program karena shader dapat bertukar variabel antara satu sama lain. Dan pada tahap ini, korespondensi dari jenis-jenis variabel yang ditukar dengan shader diperiksa.
Kami mengatakan bahwa gunakan program ini.

Selanjutnya, kita membuat daftar simpul yang ingin kita visualisasikan. WebGL memiliki fitur menarik untuk beberapa variabel. Untuk mengubah tipe data tertentu, Anda perlu mengatur konteks global untuk mengedit array_buffer, dan kemudian mengunggah sesuatu ke alamat ini. Tidak ada tugas eksplisit dari data apa pun untuk variabel. Semuanya dilakukan melalui dimasukkannya beberapa konteks.
Anda juga perlu menetapkan aturan untuk membaca dari buffer ini. Dapat dilihat bahwa kami menetapkan array dari enam elemen, tetapi program perlu menjelaskan bahwa setiap vertex terdiri dari dua komponen, tipe yang float, ini dilakukan pada baris terakhir.

Untuk mengatur warna, program mencari alamat untuk variabel u_color dan menetapkan nilai untuk variabel ini. Kami mengatur warna, merah 255, 0,8 dari hijau, 0 biru dan piksel yang sepenuhnya buram - berubah menjadi kuning. Dan kami katakan untuk menjalankan program ini menggunakan segitiga primitif, di WebGL Anda dapat menggambar titik, garis, segitiga, segitiga bentuk kompleks, dan sebagainya. Dan buat tiga puncak.

Anda juga dapat menentukan bahwa array yang kita render harus dihitung sejak awal.

Jika Anda sedikit menyulitkan contoh, Anda dapat menambahkan ketergantungan warna pada posisi kursor. Pada saat yang sama, fps melewati atap.

Untuk menggambar partikel di seluruh dunia, Anda perlu mengetahui kecepatan angin di setiap titik di dunia ini.
Untuk memperbesar dan memindahkan peta, Anda harus membuat wadah yang cocok dengan posisi peta saat ini.
Untuk memindahkan partikelnya sendiri, Anda harus membuat format data yang dapat diperbarui menggunakan GPU. Buat gambar itu sendiri dan gambar lingkaran.

Kami melakukan semua data melalui tekstur. Kami menggunakan 22 saluran untuk menentukan kecepatan horizontal dan vertikal, di mana kecepatan angin nol sesuai dengan bagian tengah rentang warna. Ini sekitar 128. Karena kecepatannya bisa negatif dan positif, kami mengatur warna relatif ke tengah kisaran.
Ternyata gambar seperti itu.

Untuk memuatnya di kartu, kita perlu memotongnya. Untuk menghubungkan gambar ke peta, kita akan menggunakan alat Lapisan Yandex.Map standar, di mana kita menentukan alamat dari mana untuk memotong ubin, dan menambahkan lapisan ini ke peta.

Kami mendapatkan gambar di mana warna hijau yang tidak menyenangkan diberi kode kecepatan angin.

Selanjutnya, Anda perlu mendapatkan tempat di mana kami akan menggambar animasi itu sendiri, sementara tempat ini harus sesuai dengan koordinat peta, gerakannya dan tindakan lainnya.
Secara default, kita dapat mengasumsikan bahwa kita akan menggunakan Layer, tetapi Layer kartu menciptakan kanvas dari mana ia segera menangkap konteks 2D yang dapat ditangkap. Tetapi jika kita mencoba untuk mengambil dari kanvas, yang sudah memiliki konteks dari tipe yang berbeda, dan mengambil darinya konteks GL, kita mendapatkan null sebagai hasilnya. Jika Anda mengaksesnya, program macet.

Oleh karena itu, kami menggunakan Pane, ini adalah wadah untuk tata letak, dan menambahkan kanvas kami di sana, dari mana kami telah mengambil konteks yang kami butuhkan.

Untuk mengatur partikel di layar dan memindahkannya, format posisi partikel dalam tekstur digunakan.
Bagaimana cara kerjanya? Tekstur persegi dibuat untuk optimasi, dan di sini ukuran sisinya diketahui.

Dengan menggambar partikel secara berurutan dan mengetahui nomor seri partikel dan ukuran tekstur tempat penyimpanannya, Anda dapat menghitung piksel tertentu tempat posisi pada layar nyata dikodekan.


Dalam shader itu sendiri, sepertinya membaca indeks yang diberikan, tekstur dengan posisi partikel saat ini dan ukuran sisi. Selanjutnya, kita menentukan koordinat x, y untuk partikel ini, membaca nilai ini dan mendekodekannya. Macam apa sihir ini: rg / 255 + ba?
Untuk posisi partikel kami menggunakan 20 saluran ganda. Saluran warna memiliki nilai dari 0 hingga 255, dan untuk layar 1080 kami tidak dapat menempatkan partikel pada posisi layar sedikitpun, karena kami dapat menempatkan maksimum pada 255 piksel paling banyak. Oleh karena itu, dalam satu saluran kami menyimpan pengetahuan tentang berapa kali sebuah partikel telah melewati 255 piksel, dan di saluran kedua kami menyimpan nilai persis berapa banyak partikel yang dilewatinya.
Selanjutnya, vertex shader harus mengonversi nilai-nilai ini ke ruang kerjanya, yaitu dari -1 hingga 1, dan mengatur titik ini pada tampilan.

Untuk melihat partikel-partikel kita, cat saja dengan warna putih. GLSL memiliki gula sedemikian rupa sehingga jika kita mendefinisikan jenis variabel dan meneruskannya menjadi konstanta, maka konstanta ini akan didistribusikan ke keempat komponen, misalnya.

Setelah menggambar program seperti itu, kami melihat satu set kotak yang identik. Mari kita coba tambahkan keindahan pada mereka.

Pertama, tambahkan ketergantungan kotak ini pada kecepatan angin saat ini. Kami cukup membaca kecepatan saat ini dan tekstur yang sesuai untuk setiap partikel. Kami mendapatkan panjang vektor, yang sesuai dengan kecepatan absolut pada titik tersebut, dan menambahkan kecepatan ini ke ukuran partikel.

Selanjutnya, agar tidak menggambar kotak, dalam fragmen shader kami memotong semua piksel yang berada di luar jari-jari, yang tidak termasuk dalam jari-jari lingkaran yang tertulis. Artinya, shader kita berubah menjadi hal seperti itu.

Kami menghitung jarak ke piksel yang diberikan dari pusat. Jika melebihi setengah ruang, maka kami tidak menunjukkannya.

Kami mendapatkan gambaran yang lebih beragam.
Selanjutnya, Anda harus memindahkan barang-barang ini. Karena WebGL 1 tidak tahu cara menghitung sesuatu, bekerja secara langsung dengan data, kami akan mengambil keuntungan dari kemampuan untuk membuat program menjadi komponen khusus, bingkai buffer.
Frame buffer dapat dipetakan, misalnya, ke tekstur yang dapat diperbarui. Jika frame buffer tidak dideklarasikan, maka menggambar secara default dilakukan di layar.
Mengubah output dari satu posisi tekstur ke posisi lain, kita dapat memperbaruinya satu per satu dan kemudian menggunakannya untuk rendering.


Prosedur untuk memperbarui posisi itu sendiri terlihat seperti ini: baca posisi saat ini, tambahkan ke vektor kecepatan saat ini dan tambahkan, disandikan dengan warna baru.

Dalam kode, sepertinya membaca posisi saat ini, decoding, membaca kecepatan saat ini, membawa kecepatan ke normal, melipat dua komponen, mengkode warna.

Ternyata gambar seperti itu. Keadaan partikel terus berubah, dan beberapa jenis animasi muncul.
Jika Anda menjalankan animasi seperti itu selama 5-10 menit, akan jelas bahwa semua partikel akan tiba di tujuan akhir mereka. Mereka semua meluncur ke corong. Anda mendapatkan gambar seperti itu.

Untuk menghindari ini, kami memperkenalkan faktor permutasi partikel di tempat acak.
Itu tergantung pada kecepatan angin saat ini, pada posisi partikel saat ini dan nomor acak yang kami transmisikan dari JS - karena versi pertama WebGL tidak memiliki fungsi pengacakan dan beberapa jenis fungsi noise.

Dalam contoh ini, kami menghitung posisi partikel yang diprediksi, posisi acak, dan memilih salah satu atau yang lain, tergantung pada faktor reset.

Untuk memahami apa yang ada di slide terakhir, Anda dapat membaca artikel ini. Yang pertama memberikan dorongan besar dalam memahami apa yang disediakan WebGL, terdiri dari apa dan bagaimana tidak membuat kesalahan di dalamnya. Di Khronos, ini adalah kelompok yang terlibat dalam pengembangan standar, ada deskripsi semua fungsi.

Poin terakhir dari tugas kita adalah menggambar jejak partikel. , , , , , , , .

.


WebGL 2D canvas, . 64 . 2D canvas, 25 , WebGL 0,3 . .
, WebGL , , .
, , , - break points, - , . WebGL β .

, . , Firefox «», WebGL-, , , . , .
Alat kedua yang membuat hidup jauh lebih mudah adalah ekstensi browser Spector.js. Ini juga menangkap kanvas dari konteks WebGL dan memungkinkan Anda untuk melihat semua operasi yang dilakukan pada kanvas ini, pengaturan waktu dan variabel lewat.
Total selama seminggu bekerja, dari awal kami mendapat solusi turnkey di angin. Saya harap saya bisa mengetahui jenis teknologi ini, WebGL, terdiri dari apa, dan memberikan contoh nyata penggunaannya dalam produk. Itu saja.