【Ray Tracing in One Weekend 超詳解】 光線追蹤1-5

 

一天一篇,今天來學習第7章 (散射)漫反射材質html

Chapter7: Diffuse Materials算法

 Prefacedom

從這一章開始,咱們將經過光線追蹤製做一些逼真的材質。 學習

咱們將從漫射(磨砂)材料開始。 ui

 先看效果spa

  

 正文3d

不發光的漫射物體僅僅呈現其周圍的顏色,可是它們用它們本身的固有顏色來調和這些色彩。 code

從漫反射表面反射的光方向是隨機的,好比:若是咱們將三條光線發送到一個漫反射表面,它們將各自具備不一樣的隨機行爲:orm

引用書上的圖:htm

  

                      diagram 7-1

它們也可能被吸取而不是被反射。 表面越暗,光線越可能被吸取。 (這就是爲何它是黑的!)

任何隨機化方向的算法都會產生看起來很粗糙的表面。 最簡單的方法之一是理想的漫反射表面。 

 原文還提到了Lambertian發射面

 

咱們來看一下,如何實現上述功能

圖說一切:

   

 

                          diagram 7-2

 

 圖解

先簡述一下各個原件:左黃球是以eye爲中心的一個單位圓,右黃球是一個和左黃球同樣的圓,至於怎麼生成的,後續說

左黃球上有兩個隨機點,藍紫色的s1,紅紫色的s2,對應於右黃球上爲s1' 和s2' 

紅色爲視線;深綠色爲反射線;三個黑球爲漫反射球體,黑色只是用顏色來區分各個原件的功能,並非黑色的漫反射球(畫完才發現,黑球都把光線吸取了。。。。==!)

 實現過程

步驟一:從eye發出一條視線,交球面於p點,以後咱們肯定隨機反射方向

將右邊的黃色圓部分放大:

引用書中一張圖:

    

                                    diagram 7-3

 n爲P點的單位法向量,方向向外,下面那個點是碰撞點P,找一個和點P相切的單位圓

而這個圓的圓心o的位置就等於p+n: eye->P),由於咱們的原點就是eye,因此根據向量就能夠得出位置信息

基於eye的向量和位置體系,其實方便了咱們利用向量運算代替位置運算,更直觀。這個本身理解下就好,不是重點。

步驟二:then, we pick a random point s from the unit randius sphere.

當咱們找到這個s點以後,咱們將沿着p->s的方向進行反射,可是咱們如何找這個random point呢?

這個時候咱們就須要用到咱們的diagram 7-2了,回去看一眼那個藍紫色點s1,作一個平行四邊形,對應到s1',他們是等價的(向量只用方向和大小進行定義,不規定起始位置,因此咱們能說它們等價)。

咱們先在原點單位球中找一個隨機點,構成一個eye->s的向量s1而後,將s1的起點移動到o處,即s1',也就是說s1'就是咱們要求的隨機點,由於直接求隨機點s1'的位置並很差求,因此,只能這樣,其實想是很好想,可是要描述清楚就應該是這麼描述。

步驟三:最後咱們獲得反射線的方向dir = s1' - p,s1' = o + s1, o = p + n

而後,咱們來求s1:

#include <random>
#define stds std::
using namespace rt; stds mt19937 mt; stds uniform_real_distribution<rtvar> rtrand; const rtvec random_unit_sphere() { rtvec p; do { p = 2.0*rtvec(rtrand(mt), rtrand(mt), rtrand(mt)) - rtvec(1, 1, 1); } while (dot(p, p) >= 1.0);      //rejection method return p; }

關於隨機數生成,在上一篇講過了,應該是靠後講的

rtrand生成的是0~1的隨機數,而後乘以2再減去1,獲得的p的每個份量均位於-1~1,其實它的範圍是一個正方體,而咱們要求的是球內隨機點。

因此咱們採用書中所述的rejection方法,拒絕非法點:若是基於原點eye找一個隨機點(x,y,z)

若是x*x+y*y+z*z>=1,那麼它不符合咱們的須要,咱們從新找。

  

最後,咱們經過上面的代碼就獲得了一個球內隨機點。

上述就是diagram 7-2中基於藍紫色點進行反射的深綠色光線的反射過程

固然,還有基於紅紫色的反射線,前半部分就和上面同樣,因此也沒有畫平行四邊形,關於後續反射

步驟四:將當前碰撞點P做爲eye,以反射方向向量dir爲視線方向進行步驟一

直到沒有碰撞,爲止

並且,光線沒通過一次反射強度就會衰減,咱們也是這麼作的,咱們採用的是每反射一次,衰減一半。

#define LOWPRECISION #include <fstream> #include "intersect.h" #include "sphere.h" #include "intersections.h" #include "camera.h" #include <random>
#define stds std::
using namespace rt; stds mt19937 mt; stds uniform_real_distribution<rtvar> rtrand; const rtvec random_unit_sphere() { rtvec p; do { p = 2.0*rtvec(rtrand(mt), rtrand(mt), rtrand(mt)) - rtvec(1, 1, 1); } while (dot(p, p) >= 1.0); return p; } rtvec lerp(const ray& sight, const intersect* world) { hitInfo rec; if (world->hit(sight, 0., intersect::inf(), rec))        //若是沒有有效碰撞點
 { rtvec target = rec._p + rec._n + random_unit_sphere();    //隨機點s的最後位置
        return 0.5*lerp(ray{ rec._p,target - rec._p }, world);    //強度衰減,新建eye繼續發射視線
 } else { rtvec dirUnit = sight.direction().ret_unitization(); rtvar t = 0.5*(dirUnit.y() + 1.); return (1. - t)*rtvec(1., 1., 1.) + t*rtvec(0.5, 0.7, 1.0); } } void build_7_1() { stds ofstream file("graph7-1.ppm"); size_t W = 400, H = 200, sample = 100; if (file.is_open()) { file << "P3\n" << W << " " << H << "\n255\n" << stds endl; intersect** list = new intersect*[2]; list[0] = new sphere(rtvec(0, 0, -1), 0.5); list[1] = new sphere(rtvec(0, -100.5, -1), 100); intersect* world = new intersections(list, 2); camera cma; for (int y = H - 1; y >= 0; --y) for (int x = 0; x < W; ++x) { rtvec color; for (int cnt = 0; cnt < sample; ++cnt) { lvgm::vec2<rtvar> para{ (rtrand(mt) + x) / W, (rtrand(mt) + y) / H }; color += lerp(cma.get_ray(para), world); } color /= sample; int r = int(255.99 * color.r()); int g = int(255.99 * color.g()); int b = int(255.99 * color.b()); file << r << " " << g << " " << b << stds endl; } stds cout << "complished" << stds endl; file.close(); if (list[0])delete list[0]; if (list[1])delete list[1]; if (list)delete[] list; if (world)delete world; } else stds cerr << "open file error" << stds endl; } int main() { build_7_1(); }

 

 效果圖以下:

 

注意球體下的陰影。 這張照片很是暗,可是咱們的球體在光線每次反射時只吸取了一半的能量,所以它們是50%的反射器。

在現實生活中, 這些球體應該是淺灰色的。 其緣由在於幾乎全部圖像觀看者都假設圖像是「伽馬校訂的」,這意味着這些0到1的值在被存儲爲字節以前作了一些變換。這種作法有不少好處,但就咱們的目的而言,今天不講這個,瞭解便可。

若是咱們對咱們平常的視覺作一個近似,咱們可使用「gamma 2」,即只是簡單的平方根:

 

這樣就會獲得下圖:

看起來更好些。

 

感謝您的閱讀,生活愉快~

 

原文出處:https://www.cnblogs.com/lv-anchoret/p/10198423.html

相關文章
相關標籤/搜索