[英]Refraction in Raytracing?
我一直在研究我的光線追蹤器。 我添加了反射和多線程支持。 目前我正在努力添加折射,但它只有一半工作。
如您所見,有一個中心球體(沒有鏡面高光)、一個反射球體(右側)和一個折射球體(左側)。 我對反射很滿意,它看起來確實很好。 對於折射它有點工作......光線被折射並且球體的所有陰影在球體中都可見(折射率1.4),但有一個外部黑色環。
編輯:顯然,當我增加球體的折射率時,黑色環變大,因此球體變小。 相反,當降低折射率時,球體變大,黑環變小……直到將折射率設置為 1,環完全消失。 IOR = 1.9 IOR = 1.1 IOR = 1.00001 有趣的是,當 IOR = 1 時,球體失去透明度並變成白色。
我想我涵蓋了全內反射,這不是這里的問題。
現在的代碼:我正在使用operator |
對於點積,所以(vec|vec)
是點積和operator ~
來反轉向量。 對象,包括 ligths 和 spheres 都存儲在Object **objects;
. 光線追蹤功能
Colour raytrace(const Ray &r, const int &depth)
{
//first find the nearest intersection of a ray with an object
Colour finalColour = skyBlue *(r.getDirection()|Vector(0,0,-1)) * SKY_FACTOR;
double t, t_min = INFINITY;
int index_nearObj = -1;
for(int i = 0; i < objSize; i++)
{
if(!dynamic_cast<Light *>(objects[i]))//skip light src
{
t = objects[i]->findParam(r);
if(t > 0 && t < t_min)
{
t_min = t;
index_nearObj = i;
}
}
}
//no intersection
if(index_nearObj < 0)
return finalColour;
Vector intersect = r.getOrigin() + r.getDirection()*t_min;
Vector normal = objects[index_nearObj]->NormalAtIntersect(intersect);
Colour objectColor = objects[index_nearObj]->getColor();
Ray rRefl, rRefr; //reflected and refracted Ray
Colour refl = finalColour, refr = finalColour; //reflected and refracted colours
double reflectance = 0, transmittance = 0;
if(objects[index_nearObj]->isReflective() && depth < MAX_TRACE_DEPTH)
{
//handle reflection
rRefl = objects[index_nearObj]->calcReflectingRay(r, intersect, normal);
refl = raytrace(rRefl, depth + 1);
reflectance = 1;
}
if(objects[index_nearObj]->isRefractive() && depth < MAX_TRACE_DEPTH)
{
//handle transmission
rRefr = objects[index_nearObj]->calcRefractingRay(r, intersect, normal, reflectance, transmittance);
refr = raytrace(rRefr, depth + 1);
}
Ray rShadow; //shadow ray
bool shadowed;
double t_light = -1;
Colour localColour;
Vector tmpv;
//get material properties
double ka = 0.2; //ambient coefficient
double kd; //diffuse coefficient
double ks; //specular coefficient
Colour ambient = ka * objectColor; //ambient component
Colour diffuse, specular;
double brightness;
localColour = ambient;
//look if the object is in shadow or light
//do this by casting a ray from the obj and
// check if there is an intersection with another obj
for(int i = 0; i < objSize; i++)
{
if(dynamic_cast<Light *>(objects[i])) //if object is a light
{
//for each light
shadowed = false;
//create Ray to light
tmpv = objects[i]->getPosition() - intersect;
rShadow = Ray(intersect + (!tmpv) * BIAS, tmpv);
t_light = objects[i]->findParam(rShadow);
if(t_light < 0) //no imtersect, which is quite impossible
continue;
//then we check if that Ray intersects one object that is not a light
for(int j = 0; j < objSize; j++)
{
if(!dynamic_cast<Light *>(objects[j]) && j != index_nearObj)//if obj is not a light
{
t = objects[j]->findParam(rShadow);
//if it is smaller we know the light is behind the object
//--> shadowed by this light
if (t >= 0 && t < t_light)
{
// Set the flag and stop the cycle
shadowed = true;
break;
}
}
}
if(!shadowed)
{
rRefl = objects[index_nearObj]->calcReflectingRay(rShadow, intersect, normal);
//reflected ray from ligh src, for ks
kd = maximum(0.0, (normal|rShadow.getDirection()));
if(objects[index_nearObj]->getShiny() <= 0)
ks = 0;
else
ks = pow(maximum(0.0, (r.getDirection()|rRefl.getDirection())), objects[index_nearObj]->getShiny());
diffuse = kd * objectColor;// * objects[i]->getColour();
specular = ks * objects[i]->getColor();
brightness = 1 /(1 + t_light * DISTANCE_DEPENDENCY_LIGHT);
localColour += brightness * (diffuse + specular);
}
}
}
finalColour = localColour + (transmittance * refr + reflectance * refl);
return finalColour;
}
現在計算折射光線的函數,我使用了幾個不同的資源站點,每個站點都有相似的算法。 這是迄今為止我能做的最好的事情。 這可能只是我沒有看到的一個小細節......
Ray Sphere::calcRefractingRay(const Ray &r, const Vector &intersection,Vector &normal, double & refl, double &trans)const
{
double n1, n2, n;
double cosI = (r.getDirection()|normal);
if(cosI > 0.0)
{
n1 = 1.0;
n2 = getRefrIndex();
normal = ~normal;//invert
}
else
{
n1 = getRefrIndex();
n2 = 1.0;
cosI = -cosI;
}
n = n1/n2;
double sinT2 = n*n * (1.0 - cosI * cosI);
double cosT = sqrt(1.0 - sinT2);
//fresnel equations
double rn = (n1 * cosI - n2 * cosT)/(n1 * cosI + n2 * cosT);
double rt = (n2 * cosI - n1 * cosT)/(n2 * cosI + n2 * cosT);
rn *= rn;
rt *= rt;
refl = (rn + rt)*0.5;
trans = 1.0 - refl;
if(n == 1.0)
return r;
if(cosT*cosT < 0.0)//tot inner refl
{
refl = 1;
trans = 0;
return calcReflectingRay(r, intersection, normal);
}
Vector dir = n * r.getDirection() + (n * cosI - cosT)*normal;
return Ray(intersection + dir * BIAS, dir);
}
編輯:我也改變了周圍的折射率。來自
if(cosI > 0.0)
{
n1 = 1.0;
n2 = getRefrIndex();
normal = ~normal;
}
else
{
n1 = getRefrIndex();
n2 = 1.0;
cosI = -cosI;
}
到
if(cosI > 0.0)
{
n1 = getRefrIndex();
n2 = 1.0;
normal = ~normal;
}
else
{
n1 = 1.0;
n2 = getRefrIndex();
cosI = -cosI;
}
然后我得到了這個,並且幾乎相同(仍然顛倒),折射率為 1! 以及反射計算:
Ray Sphere::calcReflectingRay(const Ray &r, const Vector &intersection, const Vector &normal)const
{
Vector rdir = r.getDirection();
Vector dir = rdir - 2 * (rdir|normal) * normal;
return Ray(intersection + dir*BIAS, dir);
//the Ray constructor automatically normalizes directions
}
所以我的問題是:如何修復外黑圈? 哪個版本是正確的?
非常感謝幫助:)
這是在 Linux 上使用 g++ 4.8.2 編譯的。
警告:以下是猜測,不是確定的。 我必須更詳細地查看代碼才能確定發生了什么以及為什么。
也就是說,在我看來,您的原始代碼基本上是模擬凹透鏡而不是凸透鏡。
凸透鏡基本上是一個放大鏡,將來自相對較小區域的光線聚焦在一個平面上:
這也說明了為什么更正后的代碼顯示顛倒的圖像。 來自一側頂部的光線投射到另一側的底部(反之亦然)。
回到凹透鏡:凹透鏡是一種縮小透鏡,可以從鏡頭前面顯示廣角圖像:
如果你看這里的右下角,它會顯示出我懷疑的問題:特別是在高折射率的情況下,試圖進入鏡頭的光線與鏡頭本身的邊緣相交。 對於比這更寬的所有角度,您通常會看到一個黑色環,因為鏡頭的前邊緣充當遮光板以防止光線進入。
增加折射率會增加黑色環的寬度,因為光線彎曲得更多,因此邊緣處的更大部分與鏡頭的外邊緣相交。
如果您關心他們如何使用廣角相機鏡頭避免這種情況,通常的方法是使用彎月形鏡頭,至少對於前部元件:
這不是靈丹妙葯,但至少可以防止入射光線與前透鏡元件的外邊緣相交。 根據鏡頭需要覆蓋的角度到底有多廣,彎月面的激進程度通常會比這小得多(在某些情況下,它會是平凹),但您已經大致了解了。
最后警告:當然,所有這些都是手繪的,只是為了提供一般概念,而不是(例如)反映任何特定鏡頭的設計、具有任何特定折射率的元素等。
在處理光線追蹤器時,我也偶然發現了這個確切的問題。 @lightxbulb 關於規范化光線方向向量的評論為我解決了這個問題。
首先,在編輯之前保留計算折射率的代碼。 換句話說,您應該在渲染中看到那些黑環。
然后,在計算cosI
calcRefractingRay
函數中,使用normalize(r.getDirection())
和normal
的點積。 目前,您正在使用r.getDirection()
和normal
的點積。
其次,當您計算折射光線方向dir
,請使用normalize(r.getDirection())
而不是r.getDirection()
。 同樣,您目前正在計算中使用r.getDirection()
。
此外,您檢查全內反射的方式存在問題。 在實際計算平方根之前,您應該檢查您對 ( 1.0 - sinT2
) 取平方根的1.0 - sinT2
是否為非負數。
希望有幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.