日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【Ray Tracing in One Weekend 超详解】 光线追踪1-7 Dielectric 半径为负,实心球体镂空技巧...

發(fā)布時(shí)間:2025/3/15 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Ray Tracing in One Weekend 超详解】 光线追踪1-7 Dielectric 半径为负,实心球体镂空技巧... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?

今天講這本書最后一種材質(zhì)

?Preface

水,玻璃和鉆石等透明材料是電介質(zhì)。當(dāng)光線照射它們時(shí),它會(huì)分裂成反射光線和折射(透射)光線。

處理方案:在反射或折射之間隨機(jī)選擇并且每次交互僅產(chǎn)生一條散射光線

(實(shí)施方法:隨機(jī)取樣,具體見后文)

調(diào)試最困難的部分是折射光線。如果有折射光線的話,我通常首先讓所有的光折射。對(duì)于這個(gè)項(xiàng)目,我試圖在我們的場(chǎng)景中放置兩個(gè)玻璃球,我得到了這個(gè):

  ?

?

上述圖片是對(duì)的嗎?顯然,在實(shí)際生活中,那兩個(gè)玻璃球看起來怪怪的,實(shí)際情況下,里面的內(nèi)容應(yīng)該將現(xiàn)在的進(jìn)行上下顛倒,且沒有黑色成分。

?

Chapter9:Dielectrics?

?

?Ready

定量計(jì)算光的折射

?-------------------------------------------- 數(shù)學(xué)分割線?--------------------------------------------

公式中的η為相對(duì)折射率:n2/n1

而由于入射光線方向的隨機(jī)性和eta的不同,可能導(dǎo)致 1-η*η*(1-cosθ1 * cosθ1)小于0,此時(shí)取根號(hào)毫無意義

而事實(shí)上,這也就是全反射現(xiàn)象。即:當(dāng)光線從光密介質(zhì)進(jìn)入光疏介質(zhì)中如果入射角大于某個(gè)臨界值的時(shí)候,就會(huì)發(fā)生全反射現(xiàn)象。

該臨界角即折射角為90°時(shí)對(duì)應(yīng)的入射角,也就是cosθ2恰好等于0的時(shí)候

??------------------------------------------------ END?------------------------------------------------

?正文

我們來封裝一個(gè)電介質(zhì)類

首先明確,它是材質(zhì)的一種,即

?

#ifndef DIELECTRIC_H #define DIELECTRIC_Hnamespace rt {class dielectric :public material{public:dielectric(rtvar RI) :_RI(RI) { }virtual bool scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const;inline bool refract(const rtvec& rIn, const rtvec& n, rtvar eta, rtvec& refracted)const;private:rtvar _RI; //refractive indices };bool dielectric::scatter(const ray& rIn, const hitInfo& info, rtvec& attenuation, ray& scattered)const{rtvec outward_normal;rtvec refracted;rtvar eta;attenuation = rtvec(1., 1., 1.);if (dot(rIn.direction(), info._n) > 0){outward_normal = -info._n;eta = _RI;}else{outward_normal = info._n;eta = 1. / _RI;}if (refract(rIn.direction(), outward_normal, eta, refracted)){scattered = ray(info._p, refracted);return true;}return false;}inline bool dielectric::refract(const rtvec& rIn, const rtvec& n, rtvar eta, rtvec& refracted)const{rtvec unitIn = rIn.ret_unitization();rtvar cos1 = dot(-unitIn, n);rtvar cos2 = 1. - eta*eta*(1 - cos1*cos2);if (cos2 > 0){refracted = eta * rIn + n*(eta*cos1 - sqrt(cos2));return true;}return false; //全反射 } }#endif dielectric.h

?

?attenuation的值總是1,因?yàn)椴AП砻娌晃杖魏喂?#xff0c;即沒有rgb強(qiáng)度衰減

我們會(huì)很容易想到前言部分中的方法:如果有折射,那么讓所有的光線折射,就像上面代碼中scatter函數(shù)描述的那樣,那么就會(huì)得到那張圖

我們把metal中的reflect函數(shù)設(shè)置為靜態(tài)的,或者是命名空間內(nèi)“全局”函數(shù),這樣用起來比較方便,換句話講,這個(gè)公式并不屬于任何類,它是3D數(shù)學(xué)通用公式

main函數(shù)球體設(shè)置:

?

上述代碼是前言中圖像的生成代碼

然而,它沒有加入全反射,所以導(dǎo)致了黑色成分的出現(xiàn),所以,我們將全反射加入到上述代碼中

#ifndef DIELECTRIC_H #define DIELECTRIC_Hnamespace rt {class dielectric :public material{public:dielectric(const rtvar RI) :_RI(RI) { }virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const override;inline bool refract(const rtvec& rIn, const rtvec& n, rtvar eta, rtvec& refracted)const{rtvec unitIn = rIn.ret_unitization(); //將入射光線單位化 rtvar cos1 = dot(unitIn, n);rtvar cos2 = 1. - eta*eta*(1. - cos1*cos1);if (cos2 > 0){refracted = eta * (rIn - n * cos1) - n * sqrt(cos2);return true;}return false;}private:rtvar _RI;};bool dielectric::scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const{rtvec outward_normal;rtvec reflected = metal::reflect(InRay.direction(), info._n);rtvar eta;attenuation = rtvec(1., 1., 1.);rtvec refracted;if (dot(InRay.direction(), info._n) > 0){outward_normal = -info._n;eta = _RI;}else{outward_normal = info._n;eta = 1. / _RI;}if (refract(InRay.direction(), outward_normal, eta, refracted)){scattered = ray(info._p, refracted);}else{scattered = ray(info._p, reflected);return false;}return true;}}#endif dielectric.h

會(huì)得到如下圖:

?

?

?得到這張圖是真的不容易,踩了一天坑

?

主要是,渲染一張圖看下效果基本要7~10分鐘,玩不起,放開雙手~~

?

?坑點(diǎn)

這里的反射公式有三種形式,但是它們化簡(jiǎn)之后都是一個(gè)式子

我們這里采用的是紙上推出來的,但是用哪個(gè)式子,我們都要注意三點(diǎn):

1.向量的符號(hào)!!!

我們知道cos(theta1) = dot(- 入射向量,法線)

    折射向量 = eta * 入射 + 法線*eta*cos(theta1)- 法線 * cos(theta2)

但是,如果你代碼中的cos(theta1) = dot(入射,法線)

那么, ?折射向量 = eta * 入射 - .... ?這里就不是+了

這是公式的符號(hào)的問題

2.入射向量的單位化

為什么要單位化呢,這個(gè)還是很重要的

因?yàn)槟銈魅氲娜肷湎蛄渴怯虚L(zhǎng)度的,你用你傳入的入射向量計(jì)算出來的折射向量也是有長(zhǎng)度的,顯然,折射不會(huì)衰減光的強(qiáng)度,也不會(huì)平白無故縮短向量

這時(shí)候你就要考慮了,你傳出的折射向量是要干嘛用的

折射向量是要作為新的視線的方向向量的對(duì)吧

而我們都知道,視線有三部分,eye的位置,方向向量,t系數(shù)(伸長(zhǎng)長(zhǎng)度)

還有一點(diǎn),我們計(jì)算景物的畫面的時(shí)候,計(jì)算的是視線延伸后的離眼球最近的點(diǎn)畫在屏幕上

如果,你的視線最初的方向向量本身就有好長(zhǎng),你的眼球好大一顆,那么本來離eye點(diǎn)最近的點(diǎn)可能就被這顆偌大的眼球邊界包在里面可能不是眼球之外最近的點(diǎn)了

所以,我們的方向向量一定是最短的,即單位1,這樣,我們伸長(zhǎng)之后,觸碰到的第一個(gè)點(diǎn)才能保證是離眼球最近的點(diǎn),如果方向向量過長(zhǎng),可能包在里面的點(diǎn)就被忽略了

第二點(diǎn)不注意就會(huì)出現(xiàn)下面這張圖

  

左球的景象少了些,可能就是上述原因,視線的方向向量太長(zhǎng)了,未經(jīng)過單位化

3.向量統(tǒng)一

如果你要用入射向量的單位向量,那么,所有涉及入射向量的地方都用入射單位向量代替

如果不用入射單位向量,那么整個(gè)代碼計(jì)算過程中就都不用,不要混用。

例: 下面是書上的折射函數(shù)代碼

函數(shù)體第一行,它把入射光單位化,第二行用 uv 做了點(diǎn)乘,然而后面的五行卻用的是 v 而不是uv ,沒道理!!第五行的 dt 和discriminant都是用 uv 算出來的,前面突然用個(gè)v是什么操作??

我們把v改成uv就可以了

?坑點(diǎn)結(jié)束

?

然而,還是存在玻璃內(nèi)圖像顛倒的現(xiàn)象

解釋如下:

?

?

這里面有一個(gè)反射系數(shù)的問題,上面我們都考慮的是反射系數(shù)為0的情況,實(shí)際生活中的玻璃透明介質(zhì)是有反射系數(shù)的。

此時(shí),我們需要引入一個(gè)新的概念——反射系數(shù)

它是由 Christopher Schlick 提出的:

rtvar schlick(rtvar cosine, rtvar RI) {rtvar r0 = (1-RI)/(1+RI);r0 *= r0;return r0 + (1-r0)*pow((1-cosine),5); }

?這里面還有一個(gè)問題

?我們折射的 scatter 函數(shù)需要全反射的時(shí)候return 的 是false , 意思是 if 只計(jì)算折射情況,全反射是按照 rtvec(0,0,0)運(yùn)算的,壓根就沒算

?

所以,我們改一下代碼:

#ifndef DIELECTRIC_H #define DIELECTRIC_Hnamespace rt {class dielectric :public material{public:dielectric(const rtvar RI) :_RI(RI) { }virtual bool scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const override;inline static bool refract(const rtvec& rIn, const rtvec& n, rtvar eta, rtvec& refracted);protected:rtvar _RI;rtvar dielectric::schlick(const rtvar cosine, const rtvar RI)const;};bool dielectric::scatter(const ray& InRay, const hitInfo& info, rtvec& attenuation, ray& scattered)const{rtvec outward_normal;rtvec refracted;rtvec reflected = metal::reflect(InRay.direction(), info._n);rtvar eta;rtvar reflect_prob;rtvar cos;attenuation = rtvec(1., 1., 1.);if (dot(InRay.direction(), info._n) > 0){outward_normal = -info._n;eta = _RI;cos = _RI * dot(InRay.direction(), info._n) / InRay.direction().normal();}else{outward_normal = info._n;eta = 1.0 / _RI;cos = -dot(InRay.direction(), info._n) / InRay.direction().normal();}if (refract(InRay.direction(), outward_normal, eta, refracted))reflect_prob = schlick(cos, _RI); //如果有折射,計(jì)算反射系數(shù)elsereflect_prob = 1.0; //如果沒有折射,那么為全反射if (rtrand01() < reflect_prob)scattered = ray(info._p, reflected);elsescattered = ray(info._p, refracted);return true;}inline bool dielectric::refract(const rtvec& rIn, const rtvec& n, rtvar eta, rtvec& refracted){rtvec unitIn = rIn.ret_unitization(); //將入射光線單位化 rtvar cos1 = dot(-unitIn, n);rtvar cos2 = 1. - eta*eta*(1. - cos1*cos1);if (cos2 > 0){refracted = eta * unitIn + n * (eta * cos1 - sqrt(cos2));return true;}return false;}rtvar dielectric::schlick(const rtvar cosine, const rtvar RI)const{rtvar r0 = (1. - RI) / (1. + RI);r0 *= r0;return r0 + (1 - r0)*pow((1 - cosine), 5);} }#endif dielectric.h

?

里面涉及到了rtrand01,還記得嗎,這個(gè)是我們?cè)趯W(xué)漫反射的時(shí)候弄的

那么放在這里作什么嘞?

還記得Preface中我們說過的處理方案嗎

?

我們現(xiàn)在就是這么做的,我們得到一個(gè)reflect_prob,它介于0~1之間,如果我們?nèi)?~1之間的隨機(jī)數(shù),根據(jù)隨機(jī)數(shù)確定選擇反射還是折射,這個(gè)還是很科學(xué)的,為什么呢?因?yàn)槲覀冏隽?00次采樣!!,那么我們可以理直氣壯的說,我們的透明電介質(zhì)真正做到了反射和折射的混合(除了全反射現(xiàn)象),而且,前言也說過,光線照射透明電介質(zhì)時(shí),它會(huì)分裂為反射光線和折射光線。

?

主函數(shù):

#define LOWPRECISION#include ...... #define stds std:: using namespace rt;rtvec lerp(const ray& sight, intersect* world, int depth) {hitInfo info;if (world->hit(sight, (rtvar)0.001, rtInf(), info)){ray scattered;rtvec attenuation;if (depth < 50 && info.materialp->scatter(sight, info, attenuation, scattered))return attenuation * lerp(scattered, world, depth + 1);elsereturn rtvec(0, 0, 0);}else{rtvec unit_dir = sight.direction().ret_unitization();rtvar t = 0.5*(unit_dir.y() + 1.);return (1. - t)*rtvec(1., 1., 1.) + t*rtvec(0.5, 0.7, 1.0);} }void build_9_1() {stds ofstream file("graph9-1.ppm");size_t W = 400, H = 200, sample = 100;if (file.is_open()){file << "P3\n" << W << " " << H << "\n255\n" << stds endl;size_t sphereCnt = 4;intersect** list = new intersect*[sphereCnt];list[0] = new sphere(rtvec(0, 0, -1), 0.5, new lambertian(rtvec(0.1, 0.2, 0.5)));list[1] = new sphere(rtvec(0, -100.5, -1), 100, new lambertian(rtvec(0.8, 0.8, 0.)));list[2] = new sphere(rtvec(-1, 0, -1), 0.5, new dielectric(1.5));list[3] = new sphere(rtvec(1, 0, -1), 0.5, new metal(rtvec(0.8, 0.6, 0.2)));intersect* world = new intersections(list, sphereCnt);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{ (rtrand01() + x) / W,(rtrand01() + y) / H };color += lerp(cma.get_ray(para), world, 0);}color /= sample;color = rtvec(sqrt(color.r()), sqrt(color.g()), sqrt(color.b())); //gamma 校正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;}file.close();if (list[0])delete list[0];if (list[1])delete list[1];if (list[2])delete list[2];if (list[3])delete list[3];if (list)delete[] list;if (world)delete world;stds cout << "complished" << stds endl;}elsestds cerr << "open file error" << stds endl; }int main() {build_9_1(); }/*********************************************************/

?

電介質(zhì)球體的一個(gè)有趣且簡(jiǎn)單的技巧是要注意,如果使用負(fù)半徑,幾何體不受影響但表面法線指向內(nèi)部,因此它可以用作氣泡來制作空心玻璃球體:

?

我們實(shí)驗(yàn)一下書上的負(fù)半徑:

?

得到這樣的圖:

?

?

?

?

?為了能夠看懂空心球是個(gè)啥玩意兒,我把eta 顛倒了一下

?

dot小于0,說明入射光線是從表面法線指向的方向空間入射到內(nèi)部空間,例如:光從空氣入射到水中

dot大于0,說明入射光線是從表面法線的反方向空間入射到表面法線指向的空間

這樣,我們就可以看到那個(gè)內(nèi)球了

?

?原著

本章節(jié)原書pdf圖片,可放大

點(diǎn)此處查看或者翻閱相冊(cè)內(nèi)容

?

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

轉(zhuǎn)載于:https://www.cnblogs.com/lv-anchoret/p/10217719.html

總結(jié)

以上是生活随笔為你收集整理的【Ray Tracing in One Weekend 超详解】 光线追踪1-7 Dielectric 半径为负,实心球体镂空技巧...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。