这里是折射函数:
Vec3f refract(const Vec3f &I, const Vec3f &N, const float &ior)
{
float cosi = clamp(-1, 1, dotProduct(I, N));
float etai = 1, etat = ior;
Vec3f n = N;
if (cosi < 0) { cosi = -cosi; } else { std::swap(etai, etat); n= -N; }
float eta = etai / etat;
float k = 1 - eta * eta * (1 - cosi * cosi);
return k < 0 ? 0 : eta * I + (eta * cosi - sqrtf(k)) * n;
}
1)我知道如果存在不是全内反射,首先必须找到折射方向(
ior
是物体的折射率):Vec3f refractionDirection = refract(dir, hitNormal, isect.hitObject->ior).normalize();
2)然后,我们计算折射射线的原点(射线将离开物体的位置是哪一点?)
Vec3f refractionRayOrig = outside ? hitPoint - bias : hitPoint + bias;
3)最后,要获得折射射线的颜色,我们使用
refractionDirection
和refractionRayOrig
因此,当光线从内部撞击物体时,我们使用BRDF来计算表面特性,然后光线以折射原点离开玻璃块,并且方向?
#1 楼
这是一个有趣的问题(我实际上是Scratchapixel的作者,所以我可能可以就这一问题提供帮助))。情况如下:
将主光线投射到场景中
光线撞击到玻璃上,该玻璃是折射反射/透明的材质
,您需要计算和投射来自交点的两条光线:反射光线和折射光线
,如果折射光线撞击到透明物体(玻璃)中包含的物体,则需要对其进行着色也是对象。因此,如果是塑料材质,请计算该着色器的结果(包括照明,因此循环所有照明并添加其贡献等)。
最终,可以将交点处的颜色计算为反射和折射颜色(混合是由菲涅耳效应驱动的)。
现在可以工作,但这本质上是错误的。
为什么?因为实际上光线也被玻璃和水弯曲。因此,实际上,当您沿光的方向从笔上的某个点投射阴影光线时,该光线方向实际上是错误的,因为您应该考虑折射和反射(当光线从一种介质入射时,光线应该弯曲在这种情况下(从水到空气)转移到另一个目标),但是用这种算法/方法是不可能的。
尽管总的来说,这个结果可以接受,所以没有人会在意,但是如果您的目标实际上是身体合理的图像,那么这种方法是错误的。因此,当光子撞击玻璃/水表面时,它会由于折射定律而弯曲,并最终“沉积”在笔上。这在这里很难解释,但是希望将来在Scratchapixel上有一个教训。希望对您有所帮助。
编辑
>感谢您的澄清,但是我还有另一个问题。在该课程的网站上提供的源代码中,似乎没有代码在光线离开玻璃圆柱体时生成射线。这是不必要的,还是实际上已经存在?如果可以的话,您可以指出代码中的何处?
如果你问这个问题,似乎我的解释不清楚。当折射光线击中笔的表面时,您需要在该交点(折射光线与笔的几何形状相交)处计算笔的颜色。现在,例如,笔是由木头制成的,为简单起见,它仅是一种漫射材料(无镜面反射)。因此,为了计算笔在交点处的颜色,您只需执行传统的照明循环即可:
Vec3f computePenColor(const Vec3f& objectColor, const Vec3f& intersectionPoint, normalAtIntersection) {
Vec3f shadedPointColor = 0;
// loop over all lights in the scene to cadd their contribution
for (i = 0; i < scene.lights.size(); ++i) {
Light currentLight = scene.lights[i];
// let's say this light source is a point light source
Vec3f lightDirection = currentLight.position - intersectionPoint;
// this cast a shadow ray from the shaded point to the light position
// if this ray intersects an object along the way then this point is in the shadow of this light and the function returns true
bool shadow = castShadowRay(lightDirection.length(), lightDirection.normalize());
// this point is not in the shadow of the light so add its contribution
if (!shadow) {
// the object is diffuse so apply cosine law only
shadedPointColor += max(0, normalAtIntersection.dot(lightDirection)) * currentLight->intensity * currentLight->color;
}
}
return shadedPointColor * objectColor;
}
所以也许您可以从中更清楚地看到例如,当我说“从相交点向光线投射光线”时,我的意思是投射阴影光线。该阴影射线有助于确定阴影点是否在该光线的阴影中。如果不是,那么您可以将该光的贡献添加到阴影点。
现在您还可以看到,阴影射线的方向被计算为笔上该点的位置(即相交点或阴影点)到光照位置(为简单起见,此处假定为点光源)。所以这是笔上的点和灯之间的直线。
但这是错的地方。这不应该是一条直线,因为当“阴影射线”(也是光线,但是我们从笔上的点到光而不是从光到笔,但是从本质上讲,它们是相同的东西)离开应因折射而弯曲的水量(部分水应反射)。您可以根据需要计算这个新方向,但是如果阴影射线离开水面时弯曲,那么它将不再沿光的方向传播(如下图所示),因此您可以不再使用它作为阴影或光线。
因此,如果您真的想考虑光线的弯曲,则不能使用这种由灯光循环和投射阴影光线组成的算法由于反射和折射。尽管正如我所说,很少有人关心它,因为实际上,忽略这一事实,在99%的案例中并没有太大的视觉差异。正如我所指出的那样,如果真正需要的话,使其正确的解决方案是诸如光子映射之类的,其中光的光子从光投射到场景中成为预通过,从而可以遵循光线进入场景的路径。因为它们被折射/反射表面弯曲。但是光子映射是一种相当复杂的实现算法。
评论
$ \ begingroup $
@trichoplax很抱歉,我自己对此进行了一些编辑以解决错误/错别字。
$ \ endgroup $
–user18490
17年1月20日在8:28
$ \ begingroup $
好答案!该图和附加说明很棒,我想因为您是scratchapixel教程的作者,所以应该添加此内容,以便毫无疑问地回答相同的问题。另外,你们会上光子贴图课吗?那真是太棒了!
$ \ endgroup $
– Arjan Singh
17年1月20日在10:32
$ \ begingroup $
@Arjan。我没有写本课,但我会转达您的评论。另外,是的,我们有计划编写有关“光子映射”的课程,但仅在第3卷中,我们需要在今年完成第2卷...如果您希望支持此计划,请在您周围进行推广))
$ \ endgroup $
–user18490
17年1月20日在11:16
$ \ begingroup $
我想以更有意义的方式为这个项目做贡献,有什么方法可以联系您的团队?我在scratchapixel Facebook页面上发了消息,但没有得到回复。
$ \ endgroup $
– Arjan Singh
17年1月20日在12:02
$ \ begingroup $
真的吗?我会让他们知道。我想我们在网站首页的底部有一封联系电子邮件)
$ \ endgroup $
–user18490
17年1月20日在15:48
#2 楼
您需要在每个IOR接口处生成新的射线。因此,假设您的射线照射到了玻璃物体的表面。您从相交点沿着新的IOR方向为空气->玻璃接口生成了一条新射线,该新射线在玻璃内部。然后,该射线将撞击固体物体或玻璃的内表面。如果我们碰到一个固体物体,我们可以照常照亮它。如果光线从玻璃射出,则可以在出口点再次产生新的光线,但现在使用“玻璃”->“空气” IOR。
您还可以进行全内反射,其中出射的光线被反射回内部而不是离开玻璃,同样的原理适用,使用反射方向从相交位置生成新的光线。 br />
评论
是的,当您与玻璃内的非玻璃物体相交时,应该使用BRDF。您要使用相同的光方向还是要折射该光方向? (因为玻璃内部的物体是由折射光源发出的光我可以取消删除它,我略读了您的问题,但没有意识到您已经了解如何通过折射材料投射光线,因此我的回答大部分是多余的。