在Physically Based Rendering一书中,朗伯曲面通过以下方式进行采样(参见http://www.pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Sampling_Reflection_Functions.html#):
void Sample_f(Vector3f const& wo, Vector3f* wi, const Point2f& u)
{
// Cosine-sample the hemisphere, flipping the direction if necessary
*wi = CosineSampleHemisphere(u);
if (wo.z < 0) wi->z *= -1;
}
inline Vector3f CosineSampleHemisphere(Point2f const& u)
{
Point2f d = ConcentricSampleDisk(u);
Float z = std::sqrt(std::max((Float)0, 1 - d.x * d.x - d.y * d.y));
return Vector3f(d.x, d.y, z);
}
Point2f ConcentricSampleDisk(Point2f const& u)
{
// Map uniform random numbers to $[-1,1]^2$
Point2f uOffset = 2.f * u - Vector2f(1, 1);
// Handle degeneracy at the origin
if (uOffset.x == 0 && uOffset.y == 0) return Point2f(0, 0);
// Apply concentric mapping to point
Float theta, r;
if (std::abs(uOffset.x) > std::abs(uOffset.y)) {
r = uOffset.x;
theta = PiOver4 * (uOffset.y / uOffset.x);
} else {
r = uOffset.y;
theta = PiOver2 - PiOver4 * (uOffset.x / uOffset.y);
}
return r * Point2f(std::cos(theta), std::sin(theta));
}
我现在想要做的是,给定
wo
和wi
,计算u
使得调用Sample_f(wo, &wi_other, u)
产量wi_other == wi
(至少近似)。
虽然基本上解决这个问题并不难,但我的解决方案是浮点不精确。如果您熟悉光线追踪:如果遵循精确计算的方向的光线wi
撞击表面点p
,则可能会证明近似计算的方向wi_other
与所在的整个表面非常接近p
。
到目前为止,这是我的解决方案:
Point2f invert_sample_f(pbrt::Vector3f wi, pbrt::Vector3f const& wo)
{
if (wo.z < 0)
wi.z *= -1;
return cosine_sample_hemisphere_inverse(wi);
}
template<typename RealType = pbrt::Float>
pbrt::Point2<RealType> cosine_sample_hemisphere_inverse(pbrt::Vector3<RealType> const& w) {
return concentric_map_inverse<RealType>({ w.x, w.y });
}
template<typename RealType = pbrt::Float>
pbrt::Point2<RealType> concentric_map_inverse(pbrt::Point2<RealType> u)
{
u = cartesian_to_polar(u);
auto const& r = u.x;
auto& phi = u.y;
if (r == 0)
return { 0, 0 };
// wrap ϕ -> [-π/4, 7π/4)
if (phi >= 7 * pbrt::PiOver4)
phi -= 2 * pbrt::Pi;
if (-pbrt::PiOver4 < phi && phi < pbrt::PiOver4)
{// sector 1
u = { r, r * phi / pbrt::PiOver4 };
}
else if (pbrt::PiOver4 <= phi && phi <= 3 * pbrt::PiOver4)
{// sector 2
u = { r * (2 - phi / pbrt::PiOver4), r };
}
else if (3 * pbrt::PiOver4 < phi && phi < 5 * pbrt::PiOver4)
{// sector 3
u = { -r, r * (4 - phi / pbrt::PiOver4) };
}
else // 5 * pbrt::PiOver4 <= phi && phi <= -pbrt::PiOver4
{// sector 4
u = { r * (phi / pbrt::PiOver4 - 6), -r };
}
return (u + pbrt::Vector2<RealType>{ 1, 1 }) / 2;
}
template<typename RealType = pbrt::Float>
pbrt::Point2<RealType> cartesian_to_polar(pbrt::Point2<RealType> const& p)
{
auto const &x = p.x,
&y = p.y;
RealType phi;
if (x < 0)
phi = pbrt::Pi + std::atan(y / x);
else if (x > 0)
phi = y < 0 ? 2 * pbrt::Pi + std::atan(y / x) : std::atan(y / x);
else // x == 0
phi = y < 0 ? 3 * pbrt::PiOver2 : pbrt::PiOver2;
RealType const r = std::sqrt(x * x + y * y);
return { r, phi };
}
我们能以某种方式减少解决方案的错误吗?