【发布时间】:2021-09-23 19:55:05
【问题描述】:
我一直在尝试组合一种折射方法,该方法采用 Eigen::Vector3d 的射线和表面法线,以及界面前后的折射率。
它需要处理任意表面法线、入射光线和折射率。它不需要处理全内反射,我可以在方法之外检测到并避免它。
我有两个,但他们不同意,而且似乎都不符合 Snell 定律。我认为这两个可能最初都假设界面的一侧始终是空气,这在我的情况下不正确。
它是:
Eigen::Vector3d refract(
const Eigen::Vector3d &incVector,
Eigen::Vector3d surfaceNormal,
double etai,
double etat )
{
Eigen::Vector3d incidentVector = incVector.normalized();
double cosTheta = std::clamp( incidentVector.dot( surfaceNormal ), -1.0, 1.0 );
if ( cosTheta < 0.0 ) {
cosTheta = -cosTheta;
} else {
std::swap( etai, etat );
surfaceNormal = -surfaceNormal;
}
double eta = etai / etat;
Eigen::Vector3d newVector;
double k = 1 - eta * eta * ( 1 - cosTheta * cosTheta );
if ( k < 0 ) {
// Total internal reflection, not handled ATM
// Prefer to detect this outside?
newVector = incidentVector;
//fprintf(stderr, "total internal reflection, ignore\n");
} else {
newVector = eta * incidentVector + ( eta * cosTheta - std::sqrt( k ) ) * surfaceNormal;
//fprintf(stderr, "good data:\n");
}
return newVector;
}
第二个实现来自这里:https://raytracing.github.io/books/RayTracingInOneWeekend.html#dielectrics/snell'slaw
它是:
Eigen::Vector3d refract2(
const Eigen::Vector3d &incVector,
Eigen::Vector3d surfaceNormal,
double etai,
double etat )
{
Eigen::Vector3d incidentVector = incVector.normalized();
double eta = etai / etat;
double cosTheta = std::min( ( -incidentVector ).dot( surfaceNormal ), 1.0 );
Eigen::Vector3d rOutPerpendicular = eta * ( incidentVector + cosTheta * surfaceNormal );
double rOutPerpSq = std::pow( rOutPerpendicular.norm(), 2 );
Eigen::Vector3d rOutParallel = -std::sqrt( std::abs( 1.0 - rOutPerpSq ) ) * surfaceNormal;
Eigen::Vector3d newVector = rOutPerpendicular + rOutParallel;
newVector.normalize(); // unsure if needed
return newVector;
}
(需要说明的是,目前在测试时我只查看没有发生全内反射时的结果,所以我没有比较垃圾数据)
失败的例子:
入射光线:{0.18942838831751452, 0.013336588641010838, -0.62199774163029875}
正常:{-0.27689443647572254, -0.47422711874752033, 0.13779107028412740}
第一个 IOR:1.6393627735221412
第二个 IOR:1.9297769096957305
第一种方法结果:{0.35033145873009269, 0.19363689493923739, -0.86368789659903644}
第二种方法结果:{0.37149016792865353, 0.21243159055249269, -0.90380743218214299}
这些很接近,但不一样。
如果我理解正确的话,他们似乎也不同意斯涅尔定律:
IOR1/IOR2 = 0.84950895892967282
sin(点积(折射,正常) / sin(点积(事件,正常) = 2.1044755362563823
sin(点积(折射,正常)/ sin(点积(事件,正常)= 2.2383953074347627
我哪里做错了?
【问题讨论】:
-
@Vertexwahn 该方法似乎与#2一致。不过,我的测试一定做错了,因为结果似乎仍然不正确。 sin(dot product(refracted, normal) / sin(dot product(incident, normal) 的比率应该等于 etai/etat,不是吗?我是否遗漏了什么?
标签: c++ eigen raytracing