Game 3D Shaders for Beginners: Effects

[ Bagian pertama ]

Setelah membahas dasar-dasarnya, pada bagian artikel ini kami menerapkan efek seperti garis besar objek, bloom, SSAO, blur, kedalaman bidang, pixelation, dan lainnya.

Garis besar



Menciptakan kontur di sekitar geometri adegan memberikan permainan tampilan yang unik yang menyerupai komik atau kartun.

Bahan difus


Shader kontur membutuhkan tekstur input untuk mengenali dan mewarnai pinggirannya. Calon untuk tekstur yang masuk dapat warna difus dari bahan, warna dari tekstur difus, vertex normal, atau bahkan warna dari peta normal.

uniform struct { vec4 diffuse ; } p3d_Material; out vec4 fragColor; void main() { vec3 diffuseColor = p3d_Material.diffuse.rgb; fragColor = vec4(diffuseColor, 1); } 

Ini adalah shader fragmen kecil yang membuat warna difus dari bahan geometri menjadi tekstur penyangga bingkai. Tekstur warna difus ini dari buffer bingkai akan menjadi tekstur input untuk path shader.


Ini adalah tekstur warna bahan yang difus dari frame buffer, yang menampilkan warna yang kami atur di Blender. Shader kontur akan mengenali tepi dalam adegan dan mewarnai mereka.

Perlu dicatat bahwa warna bahan yang menyebar tidak akan berfungsi jika bagian tertentu dari pemandangan tidak memiliki warna bahan yang menyebar sendiri.

Membuat Tepian



Membuat edge mirip dengan menggunakan filter edge recognition di GIMP .

Semua perhitungan untuk teknik naungan ini dilakukan dalam shader fragmen. Untuk membuat kontur untuk shader vertex, cukup untuk melewati empat simpul dari mesh persegi panjang ke output agar sesuai dengan layar.

 // ... uniform sampler2D materialDiffuseTexture; // ... vec2 texSize = textureSize(materialDiffuseTexture, 0).xy; vec2 texCoord = gl_FragCoord.xy; // ... 

Sebelum Anda mulai mengenali ujung-ujungnya, Anda perlu menyiapkan tekstur yang masuk, yang dengannya kami akan bekerja. Karena tekstur memiliki ukuran layar, kita dapat menghitung koordinat UV, mengetahui koordinat fragmen dan ukuran tekstur yang masuk.

  // ... int separation = 1; // ... 

separation dapat disesuaikan dengan selera Anda. Semakin besar pemisahan, semakin tebal bagian tepi atau garis.

  // ... float threshold = 0; // ... vec4 mx = vec4(0); vec4 mn = vec4(1); int x = -1; int y = -1; for (int i = 0; i < 9; ++i) { vec4 color = texture ( materialDiffuseTexture , (texCoord + (vec2(x, y) * separation)) / texSize ); mx = max(color, mx); mn = min(color, mn); x += 1; if (x >= 2) { x = -1; y += 1; } } float alpha = ((mx.r + mx.g + mx.b) / 3) - ((mn.r + mn.g + mn.b) / 3); if (alpha > threshold) { alpha = 1; } // ... 


Teknik pengenalan tepi menemukan perubahan warna pada tekstur yang masuk. Berfokus pada fragmen saat ini, ia menggunakan jendela fragmen 3x3 untuk menemukan warna paling terang dan paling gelap dari sembilan sampel. Kemudian dia mengurangi kecerahan satu warna, kecerahan yang lain, mendapatkan perbedaannya.

  // ... vec3 lineRgb = vec3(0.012, 0.014, 0.022); // ... vec4 lineColor = vec4(lineRgb, alpha); // ... fragColor = lineColor; // ... 

Perbedaan ini digunakan dalam saluran alfa dari warna keluaran. Jika tidak ada perbedaan, maka tepi atau garis tidak ditarik. Jika ada perbedaan, maka ujungnya ditarik.

  // ... float threshold = 0; // ... if (alpha > threshold) { alpha = 1; } // ... 

Coba bereksperimen dengan nilai ambang. Sekarang nol. Nilai bukan nol menjadi tepi, ambang ini dapat diubah. Ini sangat berguna untuk tekstur masuk yang ribut dengan perbedaan kecil. Dalam kasus tekstur masuk yang bising, Anda biasanya perlu membuat garis besar hanya untuk perbedaan besar.

Kode sumber



Kabut



Kabut (atau kabut, seperti yang disebut dalam Blender) menambah kabut atmosfer ke tempat kejadian, menciptakan bagian-bagian misterius yang melunak. Bagian yang menonjol muncul ketika beberapa geometri tiba-tiba jatuh ke dalam piramida visibilitas kamera.

 // ... uniform struct p3d_FogParameters { vec4 color ; float start ; float end ; } p3d_Fog; // ... 

Panda3D memiliki struktur data yang nyaman yang berisi semua parameter kabut, tetapi Anda dapat mentransfernya ke shader Anda secara manual.

  // ... float fogIntensity = clamp ( ( p3d_Fog.end - vertexPosition.y) / ( p3d_Fog.end - p3d_Fog.start) , 0 , 1 ); fogIntensity = 1 - fogIntensity; // ... 

Dalam contoh kode, model linier digunakan untuk menghitung kecerahan kabut saat bergerak menjauh dari kamera. Sebagai gantinya, Anda dapat menggunakan model eksponensial. Kecerahan kabut adalah nol sebelum atau di awal kabut. Ketika posisi puncak mendekati akhir kabut, fogIntensity mendekati kesatuan. Untuk semua simpul setelah akhir kabut, fogIntensity terbatas pada 1 dari atas.

  // ... fragColor = mix ( outputColor , p3d_Fog.color , fogIntensity ); // ... 

Berdasarkan kecerahan kabut, kami mencampur warna kabut dengan warna output. Saat fogIntensity mendekati kesatuan, akan ada semakin sedikit outputColor dan semakin banyak warna kabut. Ketika fogIntensity mencapai kesatuan, hanya warna kabut yang akan tersisa.

Kabut pada kontur




 // ... uniform sampler2D positionTexture; // ... vec4 position = texture(positionTexture, texCoord / texSize); float fogIntensity = clamp ( ( p3d_Fog.end - position.y) / ( p3d_Fog.end - p3d_Fog.start) , 0 , 1 ); fogIntensity = 1 - fogIntensity; vec4 lineWithFogColor = mix ( lineColor , p3d_Fog.color , fogIntensity ); fragColor = vec4(lineWithFogColor.rgb, alpha); // ... 

Path Shader menerapkan kabut ke warna tepi untuk gambar yang lebih holistik. Jika dia tidak melakukan ini, maka geometri kontur akan dikaburkan oleh kabut, yang akan terlihat aneh. Namun, ia masih membuat kontur pada tepi terluar dari geometri panggung dengan gilingan, karena tepiannya melampaui geometri - ke tempat di mana tidak ada posisi titik.

positionTexture adalah tekstur penyangga bingkai yang berisi posisi simpul ruang tampilan. Anda akan belajar tentang ini ketika kami menerapkan shader SSAO.

Kode sumber



Bloom



Menambahkan mekar ke pemandangan dapat membuat ilusi yang meyakinkan dari model pencahayaan. Objek yang memancarkan cahaya menjadi lebih meyakinkan, dan pantulan cahaya menerima sejumlah cahaya tambahan.

  //... float separation = 3; int samples = 15; float threshold = 0.5; float amount = 1; // ... 

Anda dapat menyesuaikan pengaturan ini sesuai dengan keinginan Anda. Pemisahan meningkatkan ukuran blur. Sampel menentukan kekuatan blur. Ambang batas menentukan apa yang akan dan tidak akan terpengaruh oleh efek ini. Jumlah mengontrol jumlah output mekar.

  // ... int size = samples; int size2 = size * size; int x = 0; int y = 0; // ... float value = 0; vec4 result = vec4(0); vec4 color = vec4(0); // ... for (int i = 0; i < size2; ++i) { // ... } // ... 

Teknik ini dimulai dengan melewatkan samples berukuran jendela ke samples berpusat relatif terhadap fragmen saat ini. Sepertinya jendela yang digunakan untuk membuat jalur.

  // ... color = texture ( bloomTexture , ( gl_FragCoord.xy + vec2(x * separation, y * separation) ) / texSize ); value = ((0.3 * color.r) + (0.59 * color.g) + (0.11 * color.b)); if (value < threshold) { color = vec4(0); } result += color; // ... 

Kode ini mendapatkan warna dari tekstur yang masuk dan mengubah nilai merah, hijau, dan biru menjadi nilai dalam skala abu-abu. Jika nilai dalam skala abu-abu kurang dari ambang, maka membuang warna ini, menjadikannya hitam.

Melewati semua sampel di dalam jendela, ia mengakumulasikan semua nilainya dalam result .

  // ... result = result / size2; // ... 

Setelah menyelesaikan pengumpulan sampel, ia membagi jumlah sampel warna dengan jumlah sampel yang diambil. Hasilnya adalah warna tengah fragmen itu sendiri dan tetangganya. Dengan melakukannya untuk setiap fragmen, kami mendapatkan gambar buram. Jenis blur ini disebut blur kotak.


Di sini Anda melihat proses mengeksekusi algoritma bloom.

Kode sumber



Layar Space Ambient Occlusion (SSAO)



SSAO adalah salah satu efek yang Anda tidak tahu ada, tetapi begitu Anda tahu Anda tidak bisa lagi hidup tanpanya. Dia bisa mengubah adegan biasa-biasa saja menjadi yang menakjubkan! Dalam adegan statis, oklusi ambien dapat dimasukkan ke dalam tekstur, tetapi untuk adegan yang lebih dinamis kita membutuhkan shader. SSAO adalah salah satu teknik shading yang lebih canggih, tetapi begitu Anda mengetahuinya, Anda akan menjadi master shader.

Perhatikan bahwa istilah "ruang layar" dalam judul tidak sepenuhnya benar, karena tidak semua perhitungan dilakukan dalam ruang layar.

Data yang masuk


Shader SSAO akan membutuhkan input berikut.

  • Vektor posisi titik di ruang tampilan.
  • Vektor normal ke simpul di ruang tampilan.
  • Sampel vektor dalam ruang singgung.
  • Vektor kebisingan di ruang singgung.
  • Matriks proyeksi pada lensa kamera.

Posisi



Tidak perlu menyimpan posisi simpul dalam tekstur frame buffer. Kami dapat membuatnya kembali dari buffer kedalaman kamera . Saya menulis panduan untuk pemula, jadi kami tidak akan menggunakan pengoptimalan ini, dan segera memulai bisnis. Dalam implementasi Anda, Anda dapat dengan mudah menggunakan buffer kedalaman.

 PT(Texture) depthTexture = new Texture("depthTexture"); depthTexture->set_format(Texture::Format::F_depth_component32); PT(GraphicsOutput) depthBuffer = graphicsOutput->make_texture_buffer("depthBuffer", 0, 0, depthTexture); depthBuffer->set_clear_color(LVecBase4f(0, 0, 0, 0)); NodePath depthCameraNP = window->make_camera(); DCAST(Camera, depthCameraNP.node())->set_lens(window->get_camera(0)->get_lens()); PT(DisplayRegion) depthBufferRegion = depthBuffer->make_display_region(0, 1, 0, 1); depthBufferRegion->set_camera(depthCameraNP); 

Jika Anda memutuskan untuk menggunakan buffer kedalaman, berikut adalah cara Anda dapat mengonfigurasinya di Panda3D.

 in vec4 vertexPosition; out vec4 fragColor; void main() { fragColor = vertexPosition; } 

Berikut ini adalah shader sederhana untuk merender posisi simpul di ruang tampilan menjadi tekstur penyangga bingkai. Tugas yang lebih sulit adalah menyesuaikan tekstur buffer bingkai sehingga komponen-komponen vektor fragmen yang diperolehnya tidak terbatas pada interval [0, 1] , dan bahwa masing-masing memiliki akurasi yang cukup tinggi (jumlah bit yang cukup besar). Misalnya, jika beberapa jenis posisi vertex interpolasi adalah <-139.444444566, 0.00000034343, 2.5> , maka Anda tidak dapat menyimpannya ke tekstur sebagai <0.0, 0.0, 1.0> .

  // ... FrameBufferProperties fbp = FrameBufferProperties::get_default(); // ... fbp.set_rgba_bits(32, 32, 32, 32); fbp.set_rgb_color(true); fbp.set_float_color(true); // ... 

Berikut adalah contoh kode yang menyiapkan tekstur buffer bingkai untuk menyimpan posisi titik. Ia membutuhkan 32 bit untuk merah, hijau, biru dan alfa, sehingga ia menonaktifkan batasan nilai dengan interval [0, 1] . Panggilan ke set_rgba_bits(32, 32, 32, 32) mengatur volume bit dan menonaktifkan pembatasan.

  glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB32F , 1200 , 900 , 0 , GL_RGB , GL_FLOAT , nullptr ); 

Ini adalah panggilan serupa di OpenGL. GL_RGB32F menetapkan bit dan menonaktifkan pembatasan.

Jika penyangga warna memiliki koma tetap, maka komponen nilai awal dan akhir, serta indeks pencampuran, sebelum menghitung persamaan pencampuran dibatasi masing-masing untuk [0, 1] atau [βˆ’1, 1], untuk buffer warna yang dinormalisasi dan ditandatangani, ditandai dan ditandatangani. Jika buffer warna memiliki titik apung, maka batasannya tidak terpenuhi.

Sumber


Di sini Anda melihat posisi simpul; sumbu y naik.

Ingat bahwa Panda3D mendefinisikan sumbu z sebagai vektor yang mengarah ke atas, sedangkan di OpenGL sumbu y mencari ke atas. Shader posisi menampilkan posisi simpul dengan z ke atas, karena di Panda3D
parameter gl-coordinate-system default dikonfigurasi.

Normal



Untuk orientasi yang benar dari sampel yang diperoleh dalam shader SSAO, kita perlu normals ke simpul. Kode sampel menghasilkan beberapa vektor sampel yang didistribusikan di belahan bumi, tetapi Anda dapat menggunakan bola dan sepenuhnya menyelesaikan masalah kebutuhan akan normals.

 in vec3 vertexNormal; out vec4 fragColor; void main() { vec3 normal = normalize(vertexNormal); fragColor = vec4(normal, 1); } 

Seperti posisi shader, shader normal sangat sederhana. Ingatlah untuk menormalkan normals ke simpul dan ingat bahwa mereka berada di ruang pandang.


Normal ke simpul ditunjukkan di sini; sumbu y naik.

Ingat bahwa Panda3D menganggap sumbu z sebagai vektor ke atas, dan OpenGL ke sumbu y. Shader normal menampilkan posisi simpul dengan sumbu z mengarah ke atas, karena gl-coordinate-system default dikonfigurasikan dalam Panda3D.

Sampel


Untuk menentukan nilai oklusi ambien untuk setiap fragmen tunggal, kita perlu mengambil sampel area sekitarnya.

  // ... for (int i = 0; i < numberOfSamples; ++i) { LVecBase3f sample = LVecBase3f ( randomFloats(generator) * 2.0 - 1.0 , randomFloats(generator) * 2.0 - 1.0 , randomFloats(generator) ).normalized(); float rand = randomFloats(generator); sample[0] *= rand; sample[1] *= rand; sample[2] *= rand; float scale = (float) i / (float) numberOfSamples; scale = lerp(0.1, 1.0, scale * scale); sample[0] *= scale; sample[1] *= scale; sample[2] *= scale; ssaoSamples.push_back(sample); } // ... 

Kode sampel menghasilkan 64 sampel acak yang didistribusikan di belahan bumi. ssaoSamples ini akan diteruskan ke shader SSAO.

  LVecBase3f sample = LVecBase3f ( randomFloats(generator) * 2.0 - 1.0 , randomFloats(generator) * 2.0 - 1.0 , randomFloats(generator) * 2.0 - 1.0 ).normalized(); 

Jika Anda ingin mendistribusikan sampel Anda di bola, maka ubah interval komponen acak z sehingga itu berubah dari minus satu ke satu.

Kebisingan


  // ... for (int i = 0; i < 16; ++i) { LVecBase3f noise = LVecBase3f ( randomFloats(generator) * 2.0 - 1.0 , randomFloats(generator) * 2.0 - 1.0 , 0.0 ); ssaoNoise.push_back(noise); } // ... 

Untuk menutupi area sampel dengan baik, kita perlu menghasilkan vektor kebisingan. Vektor kebisingan ini dapat memutar sampel di sekitar bagian atas permukaan.

Oklusi ambien



SSAO menyelesaikan tugasnya dengan mengambil sampel ruang tampilan di sekitar fragmen. Semakin banyak sampel di bawah permukaan, semakin gelap warna fragmen. Sampel-sampel ini terletak di fragmen dan menunjukkan arah umum normal ke verteks. Setiap sampel digunakan untuk mencari posisi dalam tekstur posisi penyangga bingkai. Posisi yang dikembalikan dibandingkan dengan sampel. Jika sampel lebih jauh dari kamera daripada posisi, maka sampel ke arah fragmen tersumbat.


Di sini Anda melihat ruang di atas permukaan sampel untuk penyumbatan.

  // ... float radius = 1.1; float bias = 0.026; float lowerRange = -2; float upperRange = 2; // ... 

Seperti beberapa teknik lainnya, shader SSAO memiliki beberapa parameter kontrol yang dapat diubah untuk mendapatkan tampilan yang diinginkan. Bias ditambahkan ke jarak dari sampel ke kamera. Parameter ini dapat digunakan untuk memerangi noda. radius menambah atau mengurangi area cakupan ruang sampel. lowerRange dan upperRange mengubah rentang standar metrik faktor dari [0, 1] ke nilai apa pun yang Anda pilih. Dengan meningkatkan rentang, Anda dapat meningkatkan kontras.

  // ... vec4 position = texture(positionTexture, texCoord); vec3 normal = texture(normalTexture, texCoord).xyz; int noiseX = int(gl_FragCoord.x - 0.5) % 4; int noiseY = int(gl_FragCoord.y - 0.5) % 4; vec3 random = noise[noiseX + (noiseY * 4)]; // ... 

Kami mendapatkan posisi, vektor normal dan acak untuk digunakan lebih lanjut. Ingatlah bahwa dalam contoh kode, 16 vektor acak telah dibuat. Vektor acak dipilih berdasarkan posisi layar dari fragmen saat ini.

  // ... vec3 tangent = normalize(random - normal * dot(random, normal)); vec3 binormal = cross(normal, tangent); mat3 tbn = mat3(tangent, binormal, normal); // ... 

Menggunakan vektor acak dan vektor normal, kami mengumpulkan matriks garis singgung, binormal, dan normal. Kita perlu matriks ini untuk mengubah vektor sampel dari ruang singgung ke ruang survei.

  // ... float occlusion = NUM_SAMPLES; for (int i = 0; i < NUM_SAMPLES; ++i) { // ... } // ... 

Memiliki matriks, shader dapat mengulangi semua sampel dalam loop, mengurangi jumlah yang belum dibuka.

  // ... vec3 sample = tbn * samples[i]; sample = position.xyz + sample * radius; // ... 

Menggunakan matriks, tempatkan sampel di sebelah posisi vertex / fragmen dan skala dengan jari-jari.

  // ... vec4 offset = vec4(sample, 1.0); offset = lensProjection * offset; offset.xyz /= offset.w; offset.xyz = offset.xyz * 0.5 + 0.5; // ... 

Dengan menggunakan posisi sampel di ruang tampilan, kami mentransformasikannya dari ruang tampilan ke ruang kliping, dan kemudian ke ruang UV.

 -1 * 0.5 + 0.5 = 0 1 * 0.5 + 0.5 = 1 

Jangan lupa bahwa komponen ruang kliping berada dalam kisaran dari minus satu ke satu, dan koordinat UV berada dalam kisaran dari nol hingga satu. Untuk mengkonversi koordinat ruang kliping ke koordinat UV, gandakan dengan satu detik dan tambahkan satu detik.

  // ... vec4 offsetPosition = texture(positionTexture, offset.xy); float occluded = 0; if (sample.y + bias <= offsetPosition.y) { occluded = 0; } else { occluded = 1; } // ... 

Menggunakan koordinat offset UV yang diperoleh dengan memproyeksikan sampel 3D ke tekstur posisi 2D, kami menemukan vektor posisi yang sesuai. Ini membawa kita dari ruang tampilan ke ruang kliping ke ruang UV dan kemudian kembali ke ruang tampilan. Shader menjalankan loop ini untuk menentukan apakah ada geometri di belakang sampel, di lokasi sampel, atau di depan sampel. Jika sampel terletak di depan atau dalam beberapa geometri, maka sampel ini tidak diperhitungkan relatif terhadap fragmen yang tumpang tindih. Jika sampel berada di belakang beberapa geometri, maka sampel ini diperhitungkan relatif terhadap fragmen yang tumpang tindih.

  // ... float intensity = smoothstep ( 0.0 , 1.0 , radius / abs(position.y - offsetPosition.y) ); occluded *= intensity; occlusion -= occluded; // ... 

Sekarang tambahkan bobot pada posisi sampel ini berdasarkan seberapa jauh berada di dalam atau di luar radius. Kemudian, kurangi sampel ini dari metrik oklusi karena mengasumsikan bahwa semua sampel tumpang tindih sebelum loop.

  // ... occlusion /= NUM_SAMPLES; // ... fragColor = vec4(vec3(occlusion), position.a); // ... 

Bagilah jumlah yang tumpang tindih dengan jumlah sampel untuk mengkonversi indikator oklusi dari interval [0, NUM_SAMPLES] ke interval [0, 1] . Nol berarti oklusi lengkap, unit berarti tidak ada oklusi. Sekarang tetapkan metrik oklusi ke warna fragmen, dan hanya itu.

Harap dicatat bahwa dalam kode contoh, saluran alfa ditetapkan nilai alfa dari tekstur posisi dari buffer bingkai untuk menghindari latar belakang yang tumpang tindih.

Kabur



Tekstur frame buffer SSAO sedikit bising, jadi Anda harus mengaburkannya untuk menghaluskan.

  // ... for (int i = 0; i < size2; ++i) { x = size - xCount; y = yCount - size; result += texture ( ssaoTexture , texCoord + vec2(x * parameters.x, y * parameters.x) ).rgb; xCount -= 1; if (xCount < countMin) { xCount = countMax; yCount -= 1; } } result = result / size2; // ... 

SSAO blur shader adalah blur kotak biasa. Seperti mekar shader, ia menggambar jendela di atas tekstur yang masuk dan rata-rata setiap fragmen dengan nilai-nilai tetangganya.

Perhatikan bahwa parameters.x adalah parameter pemisahan.

Warna sekitar


  // ... vec2 ssaoBlurTexSize = textureSize(ssaoBlurTexture, 0).xy; vec2 ssaoBlurTexCoord = gl_FragCoord.xy / ssaoBlurTexSize; float ssao = texture(ssaoBlurTexture, ssaoBlurTexCoord).r; vec4 ambient = p3d_Material.ambient * p3d_LightModel.ambient * diffuseTex * ssao; // ... 

Tantangan terakhir untuk SSAO adalah lagi dalam perhitungan pencahayaan. Di sini kita melihat bagaimana oklusi ditemukan dalam buffer tekstur SSAO tekstur dan termasuk dalam perhitungan cahaya sekitar.

Kode sumber



Kedalaman bidang



Kedalaman bidang juga merupakan efek seperti itu, setelah mempelajari tentang itu, Anda tidak dapat hidup tanpanya. Dari sudut pandang artistik, Anda dapat menggunakannya untuk menarik perhatian penonton ke objek tertentu. Tetapi dalam kasus umum, kedalaman bidang dengan biaya sedikit usaha menambah sebagian besar realisme.

Dalam fokus


Langkah pertama adalah membuat adegan sepenuhnya fokus. Render ke tekstur bingkai penyangga. Ini akan menjadi salah satu nilai input untuk kedalaman buffer bidang.

Tidak fokus


  // ... vec4 result = vec4(0); for (int i = 0; i < size2; ++i) { x = size - xCount; y = yCount - size; result += texture ( blurTexture , texCoord + vec2(x * parameters.x, y * parameters.x) ); xCount -= 1; if (xCount < countMin) { xCount = countMax; yCount -= 1; } } result = result / size2; // ... 

Langkah kedua adalah mengaburkan adegan seolah-olah itu benar-benar tidak fokus. Seperti halnya bloom dan SSAO, Anda bisa menggunakan box blur. Jadikan adegan ini tidak fokus ke tekstur penyangga bingkai. Ini akan menjadi nilai input lain untuk kedalaman shader bidang.

Perhatikan bahwa parameters.x adalah parameter pemisahan.

Kebingungan




  // ... float focalLengthSharpness = 100; float blurRate = 6; // ... 

Anda dapat menyesuaikan opsi ini sesuai selera Anda. focalLengthSharpness memengaruhi seberapa tidak fokusnya adegan pada focal length. Semakin kecil focalLengthSharpness , semakin tidak fokus adegan akan pada focal length. blurRate memengaruhi kecepatan mengaburkan adegan saat menjauh dari focal length. Semakin kecil blurRate , semakin tidak buram pemandangan saat blurRate dari titik fokus.

  // ... vec4 focusColor = texture(focusTexture, texCoord); vec4 outOfFocusColor = texture(outOfFocusTexture, texCoord); // ... 

Kita akan membutuhkan warna dalam fokus dan gambar yang tidak fokus.

  // ... vec4 position = texture(positionTexture, texCoord); // ... 

Kita juga mungkin membutuhkan posisi titik di ruang tampilan. Anda dapat menerapkan kembali tekstur posisi dari penyangga bingkai yang digunakan untuk SSAO.

  // ... float blur = clamp ( pow ( blurRate , abs(position.y - focalLength.x) ) / focalLengthSharpness , 0 , 1 ); // ... fragColor = mix(focusColor, outOfFocusColor, blur); // ... 

Dan di sini kebingungan terjadi. Semakin dekat blursatu, semakin banyak yang akan digunakan outOfFocusColor. Nilai nol blurberarti bahwa fragmen ini sepenuhnya dalam fokus. Dengan blur >= 1fragmen ini benar-benar tidak fokus.

Kode sumber



Posterisasi



Posterisasi, atau sampling warna, adalah proses mengurangi jumlah warna unik dalam suatu gambar. Anda dapat menggunakan shader ini untuk memberikan permainan komik atau tampilan retro. Jika Anda menggabungkannya dengan garis besar, Anda mendapatkan gaya kartun yang nyata.

  // ... float levels = 8; // ... 

Anda dapat bereksperimen dengan parameter ini. Semakin besar, semakin banyak bunga yang tersisa.

  // ... vec4 texColor = texture(posterizeTexture, texCoord); // ... 

Kami akan membutuhkan warna yang masuk.

  // ... vec3 grey = vec3((texColor.r + texColor.g + texColor.b) / 3.0); vec3 grey1 = grey; grey = floor(grey * levels) / levels; texColor.rgb += (grey - grey1); // ... 

Saya belum melihat metode posterisasi seperti itu. Setelah memeriksanya, saya melihat bahwa itu menciptakan hasil yang lebih indah dibandingkan dengan metode konvensional. Untuk mengurangi palet warna, pertama-tama konversikan warna ke nilai dalam skala abu-abu. Kami mendiskritasikan warnanya dengan mengikatnya ke salah satu level. Kami menghitung perbedaan antara nilai yang didiskritisasi dalam skala abu-abu dan nilai yang tidak didiskritisasi dalam skala abu-abu. Tambahkan perbedaan ini ke warna input. Perbedaan ini adalah jumlah di mana warna harus naik / turun untuk mencapai nilai diskritisasi dalam skala abu-abu.

  // ... fragColor = texColor; // ... 

Jangan lupa untuk menetapkan nilai warna input ke warna fragmen.

Cel shading



Posterisasi dapat membuat gambar tampak seperti cel shading, karena cel shading adalah proses mendiskritasikan warna difus dan difus menjadi warna diskrit. Kami hanya ingin menggunakan warna difus padat tanpa detail peta normal dan nilai kecil levels.

Kode sumber



Pixelisasi



Pixelisasi game 3D dapat memberikan tampilan yang menarik, atau dapat menghemat waktu Anda untuk membuat semua seni pixel secara manual. Gabungkan dengan posterisasi untuk menciptakan tampilan retro yang benar.

  // ... int pixelSize = 5; // ... 

Anda dapat mengatur sendiri ukuran pikselnya. Semakin besar, semakin kasar gambarnya.



  // ... float x = int(gl_FragCoord.x) % pixelSize; float y = int(gl_FragCoord.y) % pixelSize; x = floor(pixelSize / 2.0) - x; y = floor(pixelSize / 2.0) - y; x = gl_FragCoord.x + x; y = gl_FragCoord.y + y; // ... 

Teknik ini menempelkan setiap fragmen ke tengah jendela berukuran piksel terdekat yang tidak tumpang tindih. Jendela-jendela ini berbaris di atas tekstur yang masuk. Fragmen di tengah jendela menentukan warna fragmen lain di jendela mereka.

  // ... fragColor = texture(pixelizeTexture, vec2(x, y) / texSize); // ... 

Setelah kami menentukan koordinat fragmen yang diinginkan untuk digunakan, ambil warnanya dari tekstur yang masuk dan tetapkan ke warna fragmen.

Kode sumber



Pertajam



Efek mempertajam (menajamkan) meningkatkan kontras di tepi gambar. Ini sangat berguna ketika grafik ternyata terlalu lunak.

  // ... float amount = 0.8; // ... 

Dengan mengubah nilainya, kita dapat mengontrol besarnya ketajaman hasilnya. Jika nilainya nol, gambar tidak akan berubah. Dengan nilai negatif, gambar mulai terlihat aneh.

  // ... float neighbor = amount * -1; float center = amount * 4 + 1; // ... 

Fragmen yang berdekatan dikalikan dengan amount * -1. Fragmen saat ini dikalikan dengan amount * 4 + 1.

  // ... vec3 color = texture(sharpenTexture, vec2(gl_FragCoord.x + 0, gl_FragCoord.y + 1) / texSize).rgb * neighbor + texture(sharpenTexture, vec2(gl_FragCoord.x - 1, gl_FragCoord.y + 0) / texSize).rgb * neighbor + texture(sharpenTexture, vec2(gl_FragCoord.x + 0, gl_FragCoord.y + 0) / texSize).rgb * center + texture(sharpenTexture, vec2(gl_FragCoord.x + 1, gl_FragCoord.y + 0) / texSize).rgb * neighbor + texture(sharpenTexture, vec2(gl_FragCoord.x + 0, gl_FragCoord.y - 1) / texSize).rgb * neighbor ; // ... 

Fragmen tetangga ada di atas, bawah, kiri dan kanan. Setelah mengalikan tetangga dan fragmen saat ini dengan nilainya, hasilnya ditambahkan.

  // ... fragColor = vec4(color, texture(sharpenTexture, texCoord).a); // ... 

Jumlah ini adalah warna akhir dari fragmen.

Kode sumber



Butir film



Butiran film (dalam dosis kecil, dan tidak seperti dalam contoh) dapat menambahkan realisme, yang tidak terlihat sampai efek ini dihilangkan. Biasanya ini adalah ketidaksempurnaan yang membuat gambar yang dihasilkan secara digital lebih meyakinkan.

Perhatikan bahwa butiran film biasanya merupakan efek terakhir yang diterapkan pada bingkai sebelum ditampilkan.

Nilai


  // ... float amount = 0.1; // ... 

amountmengontrol visibilitas butir film. Semakin tinggi nilainya, semakin banyak "salju" dalam gambar.

Kecerahan acak


 // ... uniform float osg_FrameTime; //... float toRadians = 3.14 / 180; //... float randomIntensity = fract ( 10000 * sin ( ( gl_FragCoord.x + gl_FragCoord.y * osg_FrameTime ) * toRadians ) ); // ... 

Sepotong kode ini menghitung kecerahan acak yang diperlukan untuk menyesuaikan nilainya.

 Time Since F1 = 00 01 02 03 04 05 06 07 08 09 10 Frame Number = F1 F3 F4 F5 F6 osg_FrameTime = 00 02 04 07 08 

Nilai yang osg_FrameTime diberikan oleh Panda3D. Waktu bingkai adalah cap waktu dengan informasi tentang berapa detik telah berlalu sejak bingkai pertama. Kode sampel menggunakannya untuk menghidupkan butir film, yang osg_FrameTimeakan berbeda di setiap frame.

  // ... ( gl_FragCoord.x + gl_FragCoord.y * 8009 // Large number here. // ... 

Untuk butiran statis, film harus diganti dengan osg_FrameTimejumlah besar. Untuk menghindari melihat pola, Anda dapat mencoba angka yang berbeda.



  // ... * sin ( ( gl_FragCoord.x + gl_FragCoord.y * someNumber // ... 

Untuk membuat titik, atau bintik-bintik butiran film, digunakan koordinat, dan x, dan y. Jika Anda menggunakan x, maka hanya garis vertikal yang akan ditampilkan, jika Anda menggunakan y, maka hanya garis horizontal yang akan ditampilkan.

Dalam kode, satu koordinat dikalikan dengan yang lain untuk menghancurkan simetri diagonal.


Tentu saja, Anda dapat menghilangkan multiplier koordinat, dan mendapatkan efek hujan yang sepenuhnya dapat diterima.

Perhatikan bahwa untuk menghidupkan efek hujan, gandakan hasilnya sindengan osg_FrameTime.

Percobaan dengan koordinat x dan y untuk mengubah arah hujan. Untuk mandi bawah, hanya menyisakan koordinat x.

 input = (gl_FragCoord.x + gl_FragCoord.y * osg_FrameTime) * toRadians frame(10000 * sin(input)) = fract(10000 * sin(6.977777777777778)) = fract(10000 * 0.6400723818964882) = 

sindigunakan sebagai fungsi hash. Koordinat fragmen di hash dengan nilai output sin. Berkat ini, properti nyaman muncul - apa pun data input (besar atau kecil), interval output akan berada dalam kisaran dari minus satu hingga satu.

 fract(10000 * sin(6.977777777777778)) = fract(10000 * 0.6400723818964882) = fract(6400.723818964882) = 0.723818964882 

sindalam kombinasi dengan fractjuga digunakan sebagai generator angka acak semu.

 >>> [floor(fract(4 * sin(x * toRadians)) * 10) for x in range(0, 10)] [0, 0, 1, 2, 2, 3, 4, 4, 5, 6] >>> [floor(fract(10000 * sin(x * toRadians)) * 10) for x in range(0, 10)] [0, 4, 8, 0, 2, 1, 7, 0, 0, 5] 

Pertama lihat pada baris pertama angka, dan kemudian pada yang kedua. Setiap baris ditentukan, tetapi polanya kurang terlihat di baris kedua daripada di baris kedua. Oleh karena itu, terlepas dari kenyataan bahwa output bersifat fract(10000 * sin(...))deterministik, polanya diakui jauh lebih lemah.


Di sini kita melihat bagaimana faktornya sinadalah pertama 1, lalu 10, lalu 100, dan kemudian 1000.

Ketika pengali dari nilai output meningkat, sinpolanya menjadi kurang terlihat. Karena alasan ini, kode sinini dikalikan 10.000.

Warna fragmen


  // ... vec2 texSize = textureSize(filmGrainTexture, 0).xy; vec2 texCoord = gl_FragCoord.xy / texSize; vec4 color = texture(filmGrainTexture, texCoord); // ... 

Ubah koordinat fragmen menjadi koordinat UV. Menggunakan koordinat UV ini, kami mencari warna tekstur untuk fragmen saat ini.

  // ... amount *= randomIntensity; color.rgb += amount; // ... 

Ubah nilainya menjadi kecerahan acak dan tambahkan ke warna.

  // ... fragColor = color; // ... 

Atur warna fragmen, dan hanya itu.

Kode sumber



Ucapan Terima Kasih


Source: https://habr.com/ru/post/id453692/


All Articles