"Tidak ada yang lebih buruk daripada gambar yang jelas dari konsep yang buram." - fotografer Ansel Adams
Pada bagian
pertama artikel, kami menciptakan pelacak sinar putih, yang mampu melacak pantulan sempurna dan bayangan yang tajam. Tetapi kita tidak memiliki efek ketidakjelasan: refleksi difus, refleksi mengkilap dan bayangan lembut.
Berdasarkan
kode yang sudah kita
miliki , kita akan menyelesaikan
persamaan rendering yang dirumuskan oleh James Cajia pada tahun 1986 dan mengubah renderer kita menjadi
pelacak jalur yang mampu mentransmisikan efek di atas. Kami akan kembali menggunakan C # untuk skrip dan HLSL untuk shader. Kode diunggah ke
Bitbucket .
Artikel ini jauh lebih matematis daripada yang sebelumnya, tetapi jangan khawatir. Saya akan mencoba menjelaskan setiap formula sejelas mungkin. Rumus diperlukan di sini untuk melihat apa yang terjadi dan
mengapa penyaji kami berfungsi, jadi saya sarankan mencoba memahaminya dan jika ada sesuatu yang tidak jelas, ajukan pertanyaan di komentar ke artikel asli.
Gambar di bawah ini dibuat menggunakan peta
Graffiti Shelter dari situs HDRI Haven. Gambar lain dalam artikel ini telah dibuat menggunakan kartu
Kiara 9 Dusk .
Persamaan rendering
Dari sudut pandang formal, tugas renderer fotorealistik adalah untuk menyelesaikan persamaan rendering, yang ditulis sebagai berikut:
L(x, vec omegao)=Le(x, vec omegao)+ int Omegafr(x, vec omegai, vec omegao)( vec omegai cdot vecn)L(x, vec omegai)d vec omegai
Mari kita analisa. Tujuan utama kami adalah untuk menentukan kecerahan piksel layar. Persamaan rendering memberi kita jumlah iluminasi
L(x, vec omegao) datang dari suatu titik
x (titik kejadian balok) dalam arah
vec omegao (arah di mana balok jatuh). Permukaan itu sendiri mungkin merupakan sumber cahaya yang memancarkan cahaya
Le(x, vec omegao) ke arah kita. Sebagian besar permukaan tidak, jadi mereka hanya memantulkan cahaya dari luar. Itu sebabnya integral digunakan. Ini mengumpulkan pencahayaan yang datang dari setiap arah yang mungkin dari belahan bumi.
Omega sekitar normal (karena itu, sementara kami memperhitungkan pencahayaan yang jatuh pada permukaan
dari atas , dan bukan
dari dalam , yang mungkin diperlukan untuk bahan tembus cahaya).
Bagian pertama adalah
fr disebut fungsi distribusi reflektansi dua arah (BRDF). Fungsi ini secara visual menggambarkan jenis bahan yang kita hadapi: logam atau dielektrik, gelap atau cerah, mengkilap atau matte. BRDF menentukan proporsi pencahayaan yang berasal
vec omegai yang tercermin dalam arah
vec omegao . Dalam praktiknya, ini diimplementasikan menggunakan vektor tiga komponen dengan nilai merah, hijau dan biru dalam interval
[0,1] .
Bagian kedua -
( vec omegai cdot vecn) Apakah setara dengan
1 cos theta dimana
theta - sudut antara cahaya datang dan permukaan normal
vecn . Bayangkan sebuah kolom sinar paralel jatuh di permukaan secara tegak lurus. Sekarang bayangkan sinar yang sama jatuh ke permukaan pada sudut yang datar. Cahaya akan didistribusikan ke area yang lebih besar, tetapi itu juga berarti bahwa setiap titik area ini akan terlihat lebih gelap. Cosine diperlukan untuk memperhitungkan ini.
Akhirnya, pencahayaan itu sendiri didapat dari
vec omegai ditentukan secara rekursif menggunakan persamaan yang sama. Artinya, pencahayaan di titik
x Tergantung pada cahaya insiden dari semua arah yang mungkin di belahan bumi atas. Di setiap arah ini dari satu titik
x ada hal lain
x prime , kecerahan yang sekali lagi tergantung pada cahaya yang jatuh dari semua arah yang mungkin dari belahan atas titik ini. Semua perhitungan diulang.
Inilah yang terjadi di sini, ini adalah persamaan integral rekursif yang tak terbatas dengan jumlah tak terbatas wilayah integrasi belahan otak. Kami tidak dapat menyelesaikan persamaan ini secara langsung, tetapi ada solusi yang cukup sederhana.
1 Jangan lupakan itu! Kita akan sering berbicara tentang kosinus, dan kita akan selalu mengingat produk skalar. Sejak
veca cdot vecb= | veca | | vecb | cos( theta) , dan kita berhadapan dengan
arah (vektor satuan), maka produk skalar
adalah kosinus dalam sebagian besar tugas grafik komputer.
Monte Carlo datang untuk menyelamatkan
Integrasi Monte Carlo adalah teknik integrasi numerik yang memungkinkan kita untuk menghitung kira-kira setiap integral menggunakan jumlah sampel acak yang terbatas. Selain itu, Monte Carlo menjamin konvergensi ke keputusan yang tepat - semakin banyak sampel yang kami ambil, semakin baik. Berikut ini bentuk umum:
FN approx frac1N sumNn=0 fracf(xn)p(xn)
Oleh karena itu, fungsi integral
f(xn) dapat kira-kira dihitung dengan rata-rata sampel acak dalam domain integrasi. Setiap sampel dibagi dengan probabilitas pemilihannya.
p(xn) . Karena itu, sampel yang lebih sering dipilih akan memiliki lebih banyak berat daripada yang lebih jarang dipilih.
Dalam kasus sampel seragam di belahan bumi (setiap arah memiliki probabilitas yang sama untuk dipilih), probabilitas sampel adalah konstan:
p( omega)= frac12 pi (karena
2 pi Apakah luas permukaan satu belahan bumi). Jika kita menggabungkan semua ini, kita mendapatkan yang berikut:
L(x, vec omegao) approxLe(x, vec omegao)+ frac1N sumNn=0 colorGreen2 pifr(x, vec omegai, vec omegao)( vec omegai cdot vecn)L(x, vec omegai)
Radiasi
Le(x, vec omegao) Apakah hanya nilai yang dikembalikan oleh fungsi
Shade
kami.
frac1N sudah berjalan di fungsi
AddShader
kami. Perkalian dengan
L(x, vec omegai) terjadi ketika kita memantulkan sinar dan melacaknya lebih jauh. Tugas kita adalah memberi kehidupan pada bagian hijau dari persamaan.
Prasyarat
Sebelum memulai perjalanan, mari kita selesaikan beberapa aspek: mengumpulkan sampel, adegan deterministik, dan keacakan shader.
Akumulasi
Untuk beberapa alasan, Unity tidak memberikan saya tekstur HDR sebagai
destination
di
OnRenderImage
. Format
R8G8B8A8_Typeless
bekerja untuk saya, sehingga akurasi dengan cepat menjadi terlalu rendah untuk mengakumulasikan sejumlah besar sampel. Untuk menangani ini, mari kita tambahkan
private RenderTexture _converged
ke
private RenderTexture _converged
C #
private RenderTexture _converged
. Ini akan menjadi buffer kami, terakumulasi dengan akurasi tinggi hasil sebelum menampilkannya di layar. Kami menginisialisasi / melepaskan tekstur dengan cara yang sama seperti
_target
dalam fungsi
InitRenderTexture
. Dalam fungsi
Render
, gandakan blitting:
Graphics.Blit(_target, _converged, _addMaterial); Graphics.Blit(_converged, destination);
Adegan Deterministik
Saat membuat perubahan pada rendering untuk mengevaluasi efeknya, ada baiknya membandingkan dengan hasil sebelumnya. Sejauh ini, dengan setiap restart mode Play atau kompilasi ulang skrip, kita akan mendapatkan adegan acak baru. Untuk menghindari ini, tambahkan
public int SphereSeed
ke
public int SphereSeed
C # dan baris berikut di awal
SetUpScene
:
Random.InitState(SphereSeed);
Sekarang kita dapat secara manual mengatur adegan seed. Masukkan nomor apa saja dan
RayTracingMaster
/ aktifkan
RayTracingMaster
lagi sampai Anda mendapatkan adegan yang tepat.
Parameter berikut digunakan untuk gambar sampel: Sphere Seed 1223832719, Sphere Radius [5, 30], Spheres Max 10000, Sphere Placement Radius 100.
Keacakan shader
Sebelum memulai pengambilan sampel stokastik, kita perlu menambahkan keacakan ke shader. Saya akan menggunakan
string kanonik yang saya temukan di jaringan, dimodifikasi untuk kenyamanan:
float2 _Pixel; float _Seed; float rand() { float result = frac(sin(_Seed / 100.0f * dot(_Pixel, float2(12.9898f, 78.233f))) * 43758.5453f); _Seed += 1.0f; return result; }
Inisialisasi
_Pixel
langsung di
CSMain
sebagai
_Pixel = id.xy
sehingga setiap piksel dapat menggunakan nilai acak yang berbeda.
_Seed
diinisialisasi dari C # dalam fungsi
SetShaderParameters
.
RayTracingShader.SetFloat("_Seed", Random.value);
Kualitas angka acak yang dihasilkan di sini tidak stabil. Di masa depan, ada baiknya mengeksplorasi dan menguji fungsi ini dengan menganalisis pengaruh parameter dan membandingkannya dengan pendekatan lain. Tapi untuk saat ini, kami hanya akan menggunakannya dan berharap yang terbaik.
Pengambilan sampel belahan otak
Mari kita mulai lagi: kita perlu arah acak yang terdistribusi secara seragam di belahan bumi. Tugas non-sepele ini untuk lingkup penuh dijelaskan secara rinci dalam
artikel ini oleh Corey Simon. Sangat mudah untuk beradaptasi dengan belahan bumi. Seperti apa bentuk kode shader:
float3 SampleHemisphere(float3 normal) { // float cosTheta = rand(); float sinTheta = sqrt(max(0.0f, 1.0f - cosTheta * cosTheta)); float phi = 2 * PI * rand(); float3 tangentSpaceDir = float3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta); // return mul(tangentSpaceDir, GetTangentSpace(normal)); }
Arah dihasilkan untuk belahan yang berpusat pada sumbu Z positif, jadi kita perlu mengubahnya agar terpusat pada normal yang diinginkan. Kami menghasilkan tangen dan binormal (dua vektor ortogonal ke normal dan ortogonal satu sama lain). Pertama, kami memilih vektor bantu untuk menghasilkan garis singgung. Untuk melakukan ini, kita mengambil sumbu X positif, dan kembali ke positif Z hanya jika biasanya (kira-kira) sejajar dengan sumbu X. Kemudian kita dapat menggunakan produk vektor untuk menghasilkan garis singgung, dan kemudian binormal.
float3x3 GetTangentSpace(float3 normal) {
Lambert berhamburan
Sekarang kami memiliki arahan acak yang seragam, kami dapat melanjutkan dengan implementasi BRDF pertama. Untuk refleksi difus, yang paling umum digunakan adalah Lambert BRDF, yang secara mengejutkan sederhana:
fr(x, vec omegai, vec omegao)= frackd pi dimana
kd - Ini adalah permukaan Albedo. Mari kita masukkan ke dalam persamaan rendering Monte Carlo kami (saya belum akan memperhitungkan emisivitas) dan lihat apa yang terjadi:
L(x, vec omegao) approx frac1N sumNn=0 colorBlueViolet2kd( vec omegai cdot vecn)L(x, vec omegai)
Mari kita masukkan persamaan ini ke shader segera. Dalam fungsi
Shade
, ganti kode di dalam
if (hit.distance < 1.#INF)
dengan baris berikut:
// ray.origin = hit.position + hit.normal * 0.001f; ray.direction = SampleHemisphere(hit.normal); ray.energy *= 2 * hit.albedo * sdot(hit.normal, ray.direction); return 0.0f;
Arah baru dari berkas pantulan ditentukan dengan menggunakan fungsi sampel hemisfer homogen kami. Energi balok dikalikan dengan bagian yang sesuai dari persamaan yang ditunjukkan di atas. Karena permukaan tidak memancarkan pencahayaan apa pun (hanya memantulkan cahaya yang diterima secara langsung atau tidak langsung dari langit), kami mengembalikan 0. Di sini, jangan lupa bahwa
AddShader
rata-rata sampel, jadi kami tidak perlu khawatir tentang
frac1N jumlah .
CSMain
sudah mengandung multiplikasi oleh
L(x, vec omegai) (balok pantulan berikutnya), jadi kami tidak punya banyak pekerjaan tersisa.
sdot
adalah fungsi pembantu yang saya buat sendiri. Ini hanya mengembalikan hasil produk skalar dengan koefisien tambahan, dan kemudian membatasi ke interval
[0,1] :
float sdot(float3 x, float3 y, float f = 1.0f) { return saturate(dot(x, y) * f); }
Mari kita simpulkan apa yang kode kita lakukan sejauh ini.
CSMain
menghasilkan sinar utama kamera dan memanggil
Shade
. Ketika melintasi permukaan, fungsi ini pada gilirannya menghasilkan balok baru (acak seragam di belahan bumi di sekitar normal) dan memperhitungkan BRDF dari material dan kosinus dalam energi balok. Di persimpangan sinar dengan langit, kami mencicipi HDRI (satu-satunya sumber penerangan kami) dan mengembalikan iluminasi, yang dikalikan dengan energi sinar (mis., Hasil dari semua persimpangan sebelumnya, mulai dari kamera). Ini adalah sampel sederhana yang bercampur dengan hasil yang konvergen. Akibatnya, dampaknya diperhitungkan dalam setiap sampel.
frac1N .
Sudah waktunya untuk memeriksa semuanya dalam pekerjaan. Karena logam tidak memiliki pantulan yang difus, mari kita matikan untuk saat ini dalam fungsi
SetUpScene
dari skrip C # (tapi masih panggil
Random.value
sini untuk mempertahankan determinisme adegan):
bool metal = Random.value < 0.0f;
Luncurkan mode Putar dan lihat bagaimana gambar yang awalnya bising dibersihkan dan konvergen menjadi rendering yang indah:
Gambar Cermin Phong
Tidak buruk hanya untuk beberapa baris kode (dan sebagian kecil dari matematika). Mari kita perbaiki gambar dengan menambahkan refleksi cermin menggunakan BRDF Phong. Formulasi asli Fong memiliki masalah (kurangnya hubungan dan konservasi energi), tetapi untungnya
orang lain menghilangkannya . BRDF yang disempurnakan ditunjukkan di bawah ini.
vec omegar Apakah arah cahaya yang dipantulkan dengan sempurna, dan
alpha Merupakan indikator Phong yang mengontrol kekasaran:
fr(x, vec omegai, vec omegao)=ks frac alpha+22 pi( vec omegar cdot vec omegao) alpha
Grafik dua dimensi interaktif menunjukkan seperti apa tampilan BRDF untuk Phong kapan
alpha=15 untuk insiden balok pada sudut 45 °. Coba ubah nilainya.
alpha .
Rekatkan ini ke dalam persamaan rendering Monte Carlo kami:
L(x, vec omegao) approx frac1N sumNn=0 colorbrownks( alpha+2)( vec omegar cdot vec omegao) alpha( vec omegai cdot vecn)L(x, vec omegai)
Dan akhirnya, mari kita tambahkan ini ke Lambert BRDF yang ada:
L(x, vec omegao) approx frac1N sumNn=0[ colorBlueViolet2kd+ warnabrownks( alpha+2)( vec omegar cdot vec omegao) alpha]( vec omegai cdot vecn)L(x, vec omegai)
Dan ini adalah bagaimana mereka terlihat dalam kode bersama dengan penyebaran Lambert:
Perhatikan bahwa kami mengganti produk skalar dengan yang sedikit berbeda, tetapi setara (tercermin
omegao bukannya
omegai ) Sekarang
SetUpScene
material logam kembali ke fungsi
SetUpScene
dan periksa cara kerjanya.
Bereksperimen dengan nilai yang berbeda
alpha , Anda mungkin melihat masalah: bahkan kinerja rendah memerlukan banyak waktu untuk konvergensi, dan pada kebisingan kinerja tinggi sangat mencolok. Bahkan setelah beberapa menit menunggu, hasilnya jauh dari ideal, yang tidak dapat diterima untuk adegan yang begitu sederhana.
alpha=15 dan
alpha=300 dengan 8192 sampel terlihat seperti ini:
Mengapa ini terjadi? Bagaimanapun, sebelum kita memiliki refleksi ideal yang begitu indah (
alpha= infty )! .. Masalahnya adalah kita menghasilkan sampel yang
homogen dan menetapkan bobotnya sesuai dengan BRDF. Dengan nilai Phong yang tinggi, BRDF kecil untuk semua orang, tetapi arah ini sangat dekat dengan refleksi sempurna, dan sangat tidak mungkin bahwa kami akan memilihnya secara acak menggunakan sampel
homogen kami. Di sisi lain,
jika kita benar-benar melewati salah satu arah ini, maka BRDF akan sangat besar untuk mengimbangi semua sampel kecil lainnya. Hasilnya adalah dispersi yang sangat besar. Jalur dengan beberapa pantulan specular bahkan lebih buruk dan menghasilkan noise yang terlihat dalam gambar.
Pengambilan Sampel yang Disempurnakan
Untuk membuat jalur pelacak kita praktis, kita perlu mengubah paradigma. Alih-alih membuang-buang sampel berharga pada area di mana mereka akhirnya menjadi tidak penting (karena mereka mendapatkan nilai BRDF dan / atau nilai kosinus yang sangat rendah), mari kita
hasilkan sampel penting .
Sebagai langkah pertama, kami akan mengembalikan refleksi ideal kami, dan kemudian melihat bagaimana ide ini dapat digeneralisasi. Untuk melakukan ini, kami membagi logika bayangan menjadi refleksi difus dan specular. Untuk setiap sampel, kami akan secara acak memilih satu atau yang lain (tergantung pada rasio
kd dan
ks ) Dalam kasus refleksi difus, kami akan mematuhi sampel homogen, tetapi untuk specular, kami akan secara eksplisit mencerminkan balok di satu-satunya arah penting. Karena lebih sedikit sampel sekarang akan dihabiskan untuk setiap jenis refleksi, kita perlu meningkatkan pengaruhnya sesuai, untuk mendapatkan nilai total yang sama:
energy
adalah fungsi pembantu kecil yang rata-rata saluran warna:
float energy(float3 color) { return dot(color, 1.0f / 3.0f); }
Jadi kami membuat pelacak sinar putih dari bagian sebelumnya, tetapi sekarang dengan bayangan difus nyata (yang menyiratkan bayangan lembut, oklusi ambien, pencahayaan global difus):
Pentingnya Sampel
Mari kita lihat lagi rumus dasar Monte Carlo:
FN approx frac1N sumNn=0 fracf(xn)p(xn)
Seperti yang Anda lihat, kami membagi pengaruh masing-masing sampel (sampel) pada probabilitas memilih sampel khusus ini. Sejauh ini kami telah menggunakan sampel hemisfer homogen, jadi kami memiliki konstanta
p( omega)= frac12 pi . Seperti yang kita lihat di atas, ini jauh dari optimal, misalnya, dalam kasus Phong BRDF, yang besar dalam sejumlah kecil arah.
Bayangkan kita dapat menemukan distribusi probabilitas yang cocok dengan fungsi yang dapat diintegrasikan:
p(x)=f(x) . Maka yang berikut akan terjadi:
FN approx frac1N sumNn=01
Sekarang kami tidak memiliki sampel yang memberikan kontribusi sangat sedikit. Sampel-sampel ini kemungkinan kecil akan dipilih. Ini akan secara signifikan mengurangi varians dari hasil dan mempercepat konvergensi rendering.
Dalam praktiknya, tidak mungkin untuk menemukan distribusi ideal seperti itu, karena beberapa bagian dari fungsi yang dapat diintegrasikan (dalam kasus kami BRDF × cosine × cahaya insiden) tidak diketahui (ini paling jelas untuk cahaya insiden), tetapi distribusi sampel menurut BRDF × cosine atau bahkan hanya menurut BRDF akan membantu kita Prinsip ini disebut pengambilan sampel oleh kepentingan.
Sampel Kosinus
Dalam langkah-langkah berikut, kita perlu mengganti distribusi sampel yang homogen dengan distribusi sesuai dengan aturan cosinus. Jangan lupa, alih-alih mengalikan sampel homogen dengan cosinus,
mengurangi pengaruhnya, kami ingin menghasilkan jumlah sampel yang
lebih kecil secara proporsional.
Artikel ini oleh Thomas Poole menjelaskan cara melakukan ini. Kami akan menambahkan parameter
alpha
ke fungsi
SampleHemisphere
kami. Fungsi menentukan indeks pemilihan cosinus: 0 untuk sampel seragam, 1 untuk pemilihan cosinus, atau lebih tinggi untuk nilai Phong yang lebih tinggi. Dalam kode, tampilannya seperti ini:
float3 SampleHemisphere(float3 normal, float alpha) {
Sekarang probabilitas setiap sampel sama
p( omega)= frac alpha+12 pi( vec omega cdot vecn) alpha . Keindahan persamaan ini mungkin tidak langsung tampak jelas, tetapi sedikit kemudian Anda akan memahaminya.
Sampel Lambert dengan Pentingnya
Sebagai permulaan, kami akan memperbaiki rendering refleksi difus. Dalam distribusi homogen kami, konstanta Lambert BRDF sudah digunakan, tetapi kami dapat memperbaikinya dengan menambahkan cosinus. Distribusi probabilitas sampel oleh cosinus (di mana
alpha=1 ) sama
frac( vec omegai cdot vecn) pi , yang menyederhanakan rumus Monte Carlo kami untuk refleksi difus:
L(x, vec omegao) approx frac1N sumNn=0 colorBlueVioletkdL(x, vec omegai)
Ini akan mempercepat naungan difus kami sedikit. Sekarang mari kita masuk ke masalah sebenarnya.
Pengambilan sampel Fongov penting
Untuk Phong BRDF, prosedurnya serupa. Kali ini kami memiliki produk dari dua cosinus: cosinus standar dari persamaan rendering (seperti dalam kasus dengan refleksi difus), dikalikan dengan cosinus BRDF yang tepat. Kami hanya akan berurusan dengan yang terakhir.
Mari kita masukkan distribusi probabilitas dari contoh di atas ke dalam persamaan Phong. Kesimpulan terperinci dapat ditemukan dalam
Lafortune dan Willem: Menggunakan Model Reflektansi Phong yang Dimodifikasi untuk Rendering Berbasis Fisik (1994) :
L(x, vec omegao) approx frac1N sumNn=0 colorbrownks frac alpha+2 alpha+1( V e c o m e g a i c d o t v e c n ) L ( x , v e c o m e g a i )
Perubahan ini cukup untuk menghilangkan masalah dengan kinerja tinggi di Phong dan membuat rendering kami bertemu dalam waktu yang jauh lebih masuk akal.
Material
Akhirnya, mari kita kembangkan generasi pemandangan kita untuk menciptakan nilai-nilai yang berubah demi kelancaran dan emisivitas bola-bola! Dalam
struct Sphere
dari skrip C #, tambahkan
public float smoothness
public Vector3 emission
dan
public Vector3 emission
. Karena kita mengubah ukuran struct, kita perlu mengubah langkah ketika membuat Compute Buffer (4 × jumlah angka float, ingat?). Buat nilai
SetUpScene
fungsi
SetUpScene
untuk kelancaran dan
SetUpScene
.
Di shader, tambahkan kedua variabel ke
struct Sphere
dan
struct RayHit
, dan kemudian inisialisasi mereka di
CreateRayHit
. Dan akhirnya, tetapkan kedua nilai di
IntersectGroundPlane
(hardcoded, rekatkan nilai apa saja) dan
IntersectSphere
(mendapatkan nilai dari
Sphere
).
Saya ingin menggunakan nilai-nilai kelancaran dengan cara yang sama seperti pada shader Unity standar, yang berbeda dari eksponen Fong yang agak sewenang-wenang. Berikut adalah konversi yang baik yang dapat digunakan dalam fungsi
Shade
:
float SmoothnessToPhongAlpha(float s) { return pow(1000.0f, s * s); }
float alpha = SmoothnessToPhongAlpha(hit.smoothness);
Menggunakan emisivitas dilakukan dengan mengembalikan nilai dalam
Shade
:
return hit.emission;
Hasil
Ambil napas dalam-dalam. santai dan tunggu sampai gambar berubah menjadi gambar yang begitu indah:
Selamat! Anda berhasil melewati rumpun ekspresi matematika. Kami menerapkan pelacak jalur yang melakukan penebaran difus dan cermin, mempelajari tentang pengambilan sampel berdasarkan kepentingan, segera menerapkan konsep ini sehingga rendering bertemu dalam hitungan menit, bukan jam atau hari.
Dibandingkan dengan yang sebelumnya, artikel ini merupakan langkah besar dalam hal kompleksitas, tetapi juga secara signifikan meningkatkan kualitas hasilnya. Bekerja dengan perhitungan matematis membutuhkan waktu, tetapi itu membenarkan dirinya sendiri karena dapat secara signifikan memperdalam pemahaman Anda tentang apa yang terjadi dan akan memungkinkan Anda untuk memperluas algoritme tanpa merusak keandalan fisik.
Terima kasih sudah membaca! Pada bagian ketiga, kita (untuk sementara waktu) akan meninggalkan hutan pengambilan sampel dan naungan, dan kembali ke peradaban untuk bertemu tuan-tuan Moller dan Trumbor. Kita perlu berbicara dengan mereka tentang segitiga.
Tentang Pengarang: David Curie adalah pengembang Three Eyed Games, programmer Virtual Engineering Lab Volkswagen, peneliti grafis komputer, dan musisi heavy metal.