Katakanlah Anda bekerja dengan warna RGB: setiap warna diwakili dengan tiga intensitas atau kecerahan. Anda harus memilih antara "RGB linier" dan "sRGB". Untuk saat ini, kami akan menyederhanakan berbagai hal dengan mengabaikan tiga intensitas berbeda, dan menganggap Anda hanya memiliki satu intensitas: yaitu, Anda hanya berurusan dengan bayangan abu-abu.
Dalam ruang warna linier, hubungan antara angka yang Anda simpan dan intensitas yang direpresentasikannya adalah linier. Secara praktis, ini berarti bahwa jika Anda menggandakan angkanya, Anda menggandakan intensitas (kecerahan abu-abu). Jika Anda ingin menambahkan dua intensitas bersamaan (karena Anda menghitung intensitas berdasarkan kontribusi dua sumber cahaya, atau karena Anda menambahkan objek transparan di atas objek buram), Anda dapat melakukannya hanya dengan menambahkan dua angka bersama. Jika Anda melakukan pencampuran 2D atau bayangan 3D, atau hampir semua pemrosesan gambar, Anda ingin intensitas Anda dalam ruang warna linier., jadi Anda bisa menambah, mengurangi, mengalikan, dan membagi angka untuk mendapatkan efek yang sama pada intensitas. Sebagian besar algoritme pemrosesan dan rendering warna hanya memberikan hasil yang benar dengan RGB linier, kecuali Anda menambahkan bobot ekstra pada semuanya.
Kedengarannya sangat mudah, tapi ada masalah. Kepekaan mata manusia terhadap cahaya lebih baik pada intensitas rendah daripada intensitas tinggi. Artinya, jika Anda membuat daftar semua intensitas yang dapat Anda bedakan, ada lebih banyak intensitas gelap daripada yang terang. Dengan kata lain, Anda dapat membedakan warna abu-abu gelap lebih baik daripada warna abu-abu terang. Secara khusus, jika Anda menggunakan 8 bit untuk mewakili intensitas Anda, dan Anda melakukan ini dalam ruang warna linier, Anda akan mendapatkan terlalu banyak bayangan terang, dan tidak cukup bayangan gelap. Anda mendapatkan garis melintang di area gelap Anda, sementara di area terang Anda, Anda membuang-buang bit pada berbagai nuansa hampir putih yang tidak dapat dibedakan oleh pengguna.
Untuk menghindari masalah ini, dan memanfaatkan 8 bit tersebut dengan sebaik-baiknya, kami cenderung menggunakan sRGB . Standar sRGB memberi tahu Anda kurva yang akan digunakan, untuk membuat warna Anda non-linier. Kurva lebih dangkal di bagian bawah, sehingga Anda dapat memiliki lebih banyak abu-abu gelap, dan lebih curam di bagian atas, sehingga Anda memiliki lebih sedikit abu-abu terang. Jika Anda menggandakan angkanya, Anda akan menggandakan intensitasnya. Ini berarti bahwa jika Anda menambahkan warna sRGB bersamaan, Anda akan mendapatkan hasil yang lebih terang dari yang seharusnya. Saat ini, sebagian besar monitor menafsirkan warna input mereka sebagai sRGB. Jadi, saat Anda meletakkan warna di layar, atau menyimpannya dalam tekstur 8-bit-per-saluran, simpan sebagai sRGB , sehingga Anda dapat memanfaatkan 8 bit tersebut dengan sebaik-baiknya.
Anda akan melihat kami sekarang memiliki masalah: kami ingin warna kami diproses dalam ruang linier, tetapi disimpan dalam sRGB. Ini berarti Anda akhirnya melakukan konversi sRGB-ke-linier saat dibaca, dan konversi linier-ke-sRGB saat tulis. Seperti yang telah kita katakan bahwa intensitas 8-bit linier tidak memiliki cukup gelap, ini akan menimbulkan masalah, jadi ada satu aturan praktis lagi: jangan gunakan warna linier 8-bit jika Anda dapat menghindarinya. Menjadi konvensional untuk mengikuti aturan bahwa warna 8-bit selalu sRGB, jadi Anda melakukan konversi sRGB-ke-linier pada saat yang sama dengan memperluas intensitas Anda dari 8 menjadi 16 bit, atau dari integer ke floating-point; demikian pula, setelah Anda menyelesaikan pemrosesan floating-point, Anda mempersempit menjadi 8 bit pada saat yang sama dengan mengonversi ke sRGB. Jika Anda mengikuti aturan ini,
Saat Anda membaca gambar sRGB, dan Anda menginginkan intensitas linier, terapkan rumus ini ke setiap intensitas:
float s = read_channel();
float linear;
if (s <= 0.04045) linear = s / 12.92;
else linear = pow((s + 0.055) / 1.055, 2.4);
Sebaliknya, saat Anda ingin menulis gambar sebagai sRGB, terapkan rumus ini ke setiap intensitas linier:
float linear = do_processing();
float s;
if (linear <= 0.0031308) s = linear * 12.92;
else s = 1.055 * pow(linear, 1.0/2.4) - 0.055; ( Edited: The previous version is -0.55 )
Dalam kedua kasus, nilai floating-point berkisar dari 0 hingga 1, jadi jika Anda membaca bilangan bulat 8-bit, Anda ingin membagi dengan 255 terlebih dahulu, dan jika Anda menulis bilangan bulat 8-bit, Anda ingin mengalikan dengan 255 terakhir, dengan cara yang sama seperti biasanya. Itu saja yang perlu Anda ketahui untuk bekerja dengan sRGB.
Sampai sekarang, saya hanya berurusan dengan satu intensitas, tetapi ada hal-hal cerdas yang berkaitan dengan warna. Mata manusia dapat membedakan kecerahan yang berbeda dengan lebih baik daripada warna yang berbeda (secara teknis, ini memiliki resolusi luminansi yang lebih baik daripada chrominance), sehingga Anda dapat menggunakan 24 bit Anda dengan lebih baik dengan menyimpan kecerahan secara terpisah dari warnanya. Inilah yang coba dilakukan oleh representasi YUV, YCrCb, dll. Saluran Y adalah kecerahan warna keseluruhan, dan menggunakan lebih banyak bit (atau memiliki resolusi spasial lebih banyak) daripada dua saluran lainnya. Dengan cara ini, Anda tidak (selalu) perlu menerapkan kurva seperti yang Anda lakukan dengan intensitas RGB. YUV adalah ruang warna linier, jadi jika Anda menggandakan angka di saluran Y, Anda menggandakan kecerahan warna, tetapi Anda tidak dapat menambahkan atau mengalikan warna YUV bersama-sama seperti yang Anda bisa dengan warna RGB, jadi '
Saya pikir itu menjawab pertanyaan Anda, jadi saya akan mengakhiri dengan catatan sejarah singkat. Sebelum sRGB, CRT lama biasanya memiliki non-linearitas yang tertanam di dalamnya. Jika Anda menggandakan tegangan untuk satu piksel, Anda akan melipatgandakan intensitas. Berapa banyak lagi yang berbeda untuk setiap monitor, dan parameter ini disebut gamma . Perilaku ini berguna karena itu berarti Anda bisa mendapatkan lebih banyak kegelapan daripada cahaya, tetapi itu juga berarti Anda tidak dapat mengetahui seberapa terang warna Anda pada CRT pengguna, kecuali Anda mengkalibrasi terlebih dahulu. Koreksi gammaberarti mengubah warna yang Anda mulai (mungkin linier) dan mengubahnya untuk gamma CRT pengguna. OpenGL berasal dari era ini, itulah sebabnya perilaku sRGB-nya terkadang sedikit membingungkan. Tetapi vendor GPU sekarang cenderung bekerja dengan konvensi yang saya jelaskan di atas: bahwa ketika Anda menyimpan intensitas 8-bit dalam tekstur atau framebuffer, itu adalah sRGB, dan saat Anda memproses warna, itu linier. Misalnya, OpenGL ES 3.0, setiap framebuffer dan tekstur memiliki "tanda sRGB" yang dapat Anda aktifkan untuk mengaktifkan konversi otomatis saat membaca dan menulis. Anda tidak perlu secara eksplisit melakukan konversi sRGB atau koreksi gamma sama sekali.