刚刚开始写光线跟踪,今天准备实现阴影的效果,但是发现了一些问题。
先上图:
球体自身的阴影叠加在了球体上方,
并且来自其他球体的影子显示也不正确。我想了很久也没发现问题在哪。
还请各位前辈指点一下。
相关代码:
RGBColor Phong::Shade(ShadeRec &sr)
{
Vector3D wo(-sr.m_ray.d);
RGBColor L = m_ambientBRDF->rho(sr, wo) * sr.m_world.m_ambient->L(sr);
int lightsNum = sr.m_world.m_lights.size();
for (int i = 0; i < lightsNum; ++i)
{
Vector3D wi = sr.m_world.m_lights[i]->GetDirection(sr);
float ndotwi = sr.m_hitPointNormal * wi;
if(ndotwi > 0.0)
{
bool inShadow = false;
if(sr.m_world.m_lights[i]->castsShadow())
{
Ray shadowRay((sr.m_worldHitPoint + kEpsilon), wi);
inShadow = sr.m_world.m_lights[i]->inShadow(shadowRay, sr);
}
if(!inShadow)
{
L += (m_diffuseBRDF->fr(sr, wo, wi) + m_specularBRDF->fr(sr, wo, wi))
* sr.m_world.m_lights[i]->L(sr) * ndotwi;
}
}
}
return L;
}
bool PointLight::inShadow(const Ray &shadowray, const ShadeRec &sr)
{
float ht(sr.m_t);
float t;
int ObjectNum = sr.m_world.m_objects.size();
float d = m_location.distance(shadowray.o);
for (int i = 0; i < ObjectNum; ++i)
{
if(sr.m_world.m_objects[i]->ShadowHit(shadowray, t) && t < d)
{
return true;
}
}
return false;
}
bool Sphere::ShadowHit(const Ray &ray, float &tmin) const
{
const double ep = 0.01;
double t0, t1;
Vector3D oc = ray.o - center;
double a = ray.d * ray.d;
double b = 2.0 * oc * ray.d;
double c = oc * oc - radius * radius;
double disc = b * b - 4.0 * a * c;
if (disc < 0.0)
{
return false;
}
double e = sqrt(disc);
double denom = 2.0 * a;
t0 = (-b - e) / denom;
if (t0 > ep)
{
tmin = t0;
return true;
}
t1 = (-b + e) / denom;
if (t1 > ep)
{
tmin = t1;
return true;
}
return true;
}
論變項名稱取名字的重要性,寫成這樣我都不知道你在寫什麼。不過根據我的經驗,你可能是在計算一個點的陰影的時候,忘記判斷可能造成陰影的物體是不是不在點和光源的中間,或者由於筆誤而造成了等價效果。
還有,當你獲得一個交點要計算陰影的時候,可能要把這個點往法向方嚮往外移動一個epsilon。我看你好像做了類似的事。
解決這個問題的方法:給你的程式碼寫上詳細的註釋,寫著寫著你就開竅了。
最後,我給一個我以前寫的ray tracing給你參考。我寫的也比較隨便,只做了反射、折射、陰影、AO和一些貼圖功能。完全沒有效能優化,程式碼怎麼好看怎麼寫。給複雜物件做求交的時候完全是平均切分的kd-tree(正常的做法是根據點的分佈來切,盡量把空白的地方切掉)。
圖形學真是打發時間的好東西系列
http://www.cppblog.com/vczh/archive/2011/01/09/138196.html
http://www.cppblog.com/vczh/archive/2011/01/21/138990 .html
http://www.cppblog.com/vczh/archive/2011/01/22/139112.html
1 沒有看到您的kEpsilon 在何處計算的,我猜您想要表達的意思應該是浮在交點表面上一點點的位置重新投射shadow ray,那麼是不是應該使用kEpsilon * normal 來作為bias?
2 inShadow函數沒有判斷t
3 注意到你的wo有做反向來取得Li Direction 不知道在取得shadow ray方向的時候, wi=light::GetDirection(sr)的方向是否是正確的?