我目前正在制作光线跟踪器,只是为了好玩而且我在折射处理方面遇到了麻烦。
可以找到整个光线跟踪器的代码源on Github。
这是渲染的图像:
右侧球体的折射率为1.5(玻璃)。
在折射之上,我想处理一个“透明度”系数,其定义如下:
此球体的透明度为1.
以下是处理折射部分的代码。它可以找到on github here。
Color handleTransparency(const Scene& scene,
const Ray& ray,
const IntersectionData& data,
uint8 depth)
{
Ray refracted(RayType::Transparency, data.point, ray.getDirection());
Float_t eta = data.material->getRefraction();
if (eta != 1 && eta > Globals::Epsilon)
refracted.setDirection(Tools::Refract(ray.getDirection(), data.normal, eta));
refracted.setOrigin(data.point + Globals::Epsilon * refracted.getDirection());
return inter(scene, refracted, depth + 1);
}
// http://graphics.stanford.edu/courses/cs148-10-summer/docs/2006--degreve--reflection_refraction.pdf
Float_t getFresnelReflectance(const IntersectionData& data, const Ray& ray)
{
Float_t n = data.material->getRefraction();
Float_t cosI = -Tools::DotProduct(ray.getDirection(), data.normal);
Float_t sin2T = n * n * (Float_t(1.0) - cosI * cosI);
if (sin2T > 1.0)
return 1.0;
using std::sqrt;
Float_t cosT = sqrt(1.0 - sin2T);
Float_t rPer = (n * cosI - cosT) / (n * cosI + cosT);
Float_t rPar = (cosI - n * cosT) / (cosI + n * cosT);
return (rPer * rPer + rPar * rPar) / Float_t(2.0);
}
Color handleReflectionAndRefraction(const Scene& scene,
const Ray& ray,
const IntersectionData& data,
uint8 depth)
{
bool hasReflexion = data.material->getReflexion() > Globals::Epsilon;
bool hasTransparency = data.material->getTransparency() > Globals::Epsilon;
if (!(hasReflexion || hasTransparency) || depth >= MAX_DEPTH)
return 0;
Float_t reflectance = data.material->getReflexion();
Float_t transmittance = data.material->getTransparency();
Color reflexion;
Color transparency;
if (hasReflexion && hasTransparency)
{
reflectance = getFresnelReflectance(data, ray);
transmittance = 1.0 - reflectance;
}
if (hasReflexion)
reflexion = handleReflection(scene, ray, data, depth) * reflectance;
if (hasTransparency)
transparency = handleTransparency(scene, ray, data, depth) * transmittance;
return reflexion + transparency;
}
Tools::Refract
只是在内部调用glm::refract
。 (如果我愿意,我可以轻松改变)
我不处理n1
和n2
的概念:n2
被认为永远是1代表空气。
我有什么明显的东西吗?
修改
在添加一种方法来了解光线是否在一个物体内(如果是这样的话,否定正常光线)我有这个:
在环顾四周找到帮助时,我偶然发现this帖子,但我认为答案没有回答。通过阅读它,我不明白我应该做什么。
编辑2
我已经尝试了很多东西,目前我正处于这个阶段:
它更好,但我仍然不确定它是否正确。我正在使用这个图像作为灵感:
但这个是使用两个折射率(更接近现实),而我想简化并始终将空气视为第二种(进出)材料。
我在代码中实质上改变的是:
inline Vec_t Refract(Vec_t v, const IntersectionData& data, Float_t eta)
{
Float_t n = eta;
if (data.isInside)
n = 1.0 / n;
double cosI = Tools::DotProduct(v, data.normal);
return v * n - data.normal * (-cosI + n * cosI);
}
以下是同一领域的另一种观点:
答案 0 :(得分:6)
在阅读完所有评论,问题的新版本并自己做一些实验后,我制作了以下版本的refract
例程:
float3 refract(float3 i, float3 n, float eta)
{
eta = 2.0f - eta;
float cosi = dot(n, i);
float3 o = (i * eta - n * (-cosi + eta * cosi));
return o;
}
这次调用它不需要任何额外的操作:
float3 refr = refract(rayDirection, normal, refrIdx);
我唯一不确定的是在进行内部光线交叉时反射折射率。在我的测试中,无论我是否反转了索引,所产生的图像都没有多大差异。
下面有一些不同指数的图像:
有关更多图片,请参阅link,因为该网站不允许我在此处添加更多图片。
答案 1 :(得分:3)
我作为一名物理学家而不是程序员回答这个问题,因为没有时间阅读所有代码,所以不会给代码执行修复只是一般的想法。
根据你上面所说的,黑环是指当n_object小于n_air时。如果你在一个物体里面,如果你在水里面或类似的东西,但是材料已经被构造成具有类似的奇怪属性并且它应该得到支持,那么这通常都是正确的。
在这种情况下,存在不能衍射的光线,因为衍射光谱将折射光线放在材料之间的界面的相同侧,这显然不具有衍射作用。在这种情况下,表面将会像它是一个反射表面。这种情况通常被称为全内反射。
如果完全精确,那么几乎所有的折射物体也会部分反射,反射或透射(因此折射)的光的比例由Fresnel equations给出。对于这种情况,尽管它仍然是一个很好的近似,只要处理是反射,如果角度太远和传输(因此折射)否则。
如果无法进行反射(由于它在那些方向上是暗的),也有可能看到这种黑环效应的情况,但是可以传输的光。这可以通过以下方式来完成:将一根卡紧紧地贴在物体的边缘上并直接指向远处,并且只在管内发光,而不是在外面。