IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 环境光重要性采样 -> 正文阅读

[游戏开发]环境光重要性采样

重要性采样概述

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

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;
}

由于在Long-Latitude-Map中的θ和φ并不是从0开始,上下两级中的θ分别是π/2到-π/2,φ沿着纬线方向是0到2π,见下图:

?所以映射到uv的公式是:

f(\theta, \phi) \rightarrow g(u, v) = (0.5 - \frac{\theta}{\pi}, 1 - \frac{\phi}{2\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)

由于分布的概率不受偏移的影响,所以有:

g(u, v) = (\pi v , 2\pi u )

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

?这里设x是(u, v),y是(θ, φ),Jg(u,v) = 2π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);
	// Convert infinite light sample point to direction
	float theta = uv.y * PI;
	float phi = uv.x * 2 * PI;
	float cosTheta = cos(theta);
	float sinTheta = sin(theta);
	float sinPhi = sin(phi);
	float cosPhi = cos(phi);
	wi = float3(sinTheta * cosPhi, sinTheta * sinPhi, cosTheta);

	// 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;
}

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-03-22 20:55:47  更:2022-03-22 20:57:49 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/29 12:34:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码