环境光重要性采样

重要性采样概述

计算积分需要利用蒙特卡洛方法去近似,蒙特卡洛方法我这里就不讲了,有兴趣的可以看看我的知乎:蒙特卡洛方法的简单总结 - 知乎

F_N = \frac{1}{N}\sum_{i=1}^{N}{\frac{f(X_i)}{p(X_i)}}

重要性采样的目的就是加快收敛速度,所以选择pdf比较重要。

所以对于环境光采样,我们需要知道环境光每个方向上的概率密度。

环境光贴图

环境光贴图能用整个球的所有方向去采样,球面方向能转成极坐标θ[0, π]和φ[0, 2π]表示,所以我们用经纬图(long_latitude_map)来做环境光贴图。纬度可以用θ,经度用φ,例如地球仪的贴图展开:

采样long_latitude_map可以把方向转成θ和φ再转成uv来做采样:

float2 DirectionToPolar(float3 direction)
{
	float3 normalizedCoords = normalize(direction);
	float latitude = acos(normalizedCoords.y);
	float longitude = atan2(normalizedCoords.z, normalizedCoords.x);
	float2 sphereCoords = float2(longitude, latitude) * float2(0.5 / PI, 1.0 / PI);
	return float2(0.5, 1.0) - sphereCoords;
}

float3 SampleEnviromentLight(float2 uv)
{
	if (enviromentTextureMask == 1)
	{
		return _LatitudeLongitudeMap.SampleLevel(_LatitudeLongitudeMap_linear_repeat_sampler, uv, 0).rgb;
	}
	return enviromentColor;
}

在某些引擎中,uv的方向和经纬方向不一定是相同的,例如Unity,uv是从左下角开始。

而沿着贴图u的方向,也不一定是从0到2π,例如在左手左边系下,沿着φ方向是2π到0,见下图:

 下面是左手坐标下的俯视图,表面了u和φ的关系:

 

如果我们把u从φ = π开始,沿着u的方向φ的范围就是[-π, π]了。 

 所以映射到uv的公式是:

f(\theta, \phi) \rightarrow g(u, v) = (0.5 - \frac{\phi}{2\pi}, 1 - \frac{\theta}{\pi})

环境光贴图重要性采样

构建分布

由于环境光贴图是一个离散的二维像素组成,没有通用函数,所以分布也是一个二维的分布。

但分布仅仅是用像素的亮度来决定吗?

我们可以看地球仪的展开图,极点坐标展开后,足足占了一整行像素,也就是说采样的频率和第下面赤道附近的频率一样,这样明显不符合分布,所以我们需要乘以一个sinθ来改变他们的分布,也就是说维度越高的,概率会越低。

我这里手画了一个图方便理解:

所以最后的分布构建是:亮度乘以sinθ。

for (int v = 0; v < height; ++v)
{
    float vp = (float)v / (float)height;
    float sinTheta = Mathf.Sin(Mathf.PI * (float)(v + 0.5f) / (float)height);
    for (int u = 0; u < width; ++u)
    {
        float y = pixels[u + v * width].Y();
        float distribution = y;
        if (distribution == 0)
            distribution = float.Epsilon;
        distributions[u + v * width] = distribution * sinTheta;
    }
}

概率密度函数转换推导

前面说了这么多,我们最后的目的是采样环境光贴图的uv来构建光照方向\omega,然后计算出p(ω)来做蒙特卡洛计算,ω可以通过θ和φ构造。

然而我们的分布是通过θ和φ构造的,所以必须做一个分布的转换满足(θ, φ) -> (u, v)。

以下参考pbrtv3。

首先构建映射函数:

g(u, v) = (\pi (v -0.5), 2\pi (u - 1)) = (\theta, \phi)

根据概率密度的转换函数:

 这里设x是(u, v),y是(θ, φ),Jg(u,v) = 2π²,最后得出分布函数的转换:

p(ω) = p(θ, φ) / sinθ,代入最后得到:

 代码如下:

float3 ImportanceSampleEnviromentLight(float2 u, out float pdf, out float3 wi)
{
	float mapPdf = 0;
	pdf = 0;
	wi = 0;
	float2 uv = Sample2DContinuous(u, mapPdf);
	if (mapPdf == 0)
		return float3(0, 0, 0);
	
    float theta = (1.0 - uv.y) * PI;
	float phi = (0.5 - uv.x) * 2 * PI;
	float cosTheta = cos(theta);
	float sinTheta = sin(theta);
	float sinPhi = sin(phi);
	float cosPhi = cos(phi);
	//left hand coordinate and y is up
	float x = sinTheta * cosPhi;
	float y = cosTheta;
	float z = sinTheta * sinPhi;
	wi = float3(x, y, z);
    // Compute PDF for sampled infinite light direction
	pdf = mapPdf / (2 * PI * PI * sinTheta);
	if (sinTheta == 0)
	{
		pdf = 0;
		return 0;
	}
	
	return _LatitudeLongitudeMap.SampleLevel(_LatitudeLongitudeMap_linear_repeat_sampler, uv, 0) * 100;
}

效果对比

64spp,importance sampling envmap

64spp,uniform sampling envmap 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值