GAMES202-高质量实时渲染(Assignment 4)
目录
- 知识回顾
- 蒙特卡洛积分
- 为什么渲染方程会出现能量损失
- Kulla-Conty
- 作业要求
- 预计算E(μ)
- 预计算Eavg
GitHub主页:https://github.com/sdpyy1
作业实现:https://github.com/sdpyy1/CppLearn/tree/main/games202
知识回顾
蒙特卡洛积分
首先需要一个知识点:如何求一个连续概率的期望
更一般的:
假设我们要求某个函数f(x)的积分。首先需要进行采样,采样的概率密度函数表示为g(x)
可以表示为
即f/g的期望
为什么渲染方程会出现能量损失
微表面模型只考虑了一次弹射:
当我们使用Mircofacet模型时,当材质的粗糙度越大,通过白炉测试会发现损失的能量越来越多。因为Mircofacet模型只涉及一次的光线弹射,当物体很粗糙时,一根光线很容易会与表面发生多次作用,Mircofacet忽略了这一点,因此粗糙度大的物体,渲染结果会有点暗。
解决思路就是:被遮挡掉的能量就是会在多次弹射中输出的能量, 也是我们需要补偿的能量。
Kulla-Conty
首先计算所有方向到着色点的Radiance都是1时,反射的irradiance,下面这个方程是把渲染方程还元得到的
这个公式算出来肯定是介于0-1之间的,剩下的部分就是损失的。也就是说当固定一个观察方向后,能量损失就可以通过来计算,所以再把这部分能量补上就对了。
具体来说,需要另外一种BRDF函数来使得使用这个BRDF积分出来的能量就是缺少的能量,具体如何设计BRDF,就是推导出来的。看下面这张图,论文设计出来的这个BRDF推导出来刚好就是损失的能量。(具体如何得到的就不学了)
其中的Eavg是一个数,但是计算很复杂,所以可以预计算。
作业要求
作业中的渲染方程设置
预计算E(μ)
本质就是计算一个渲染方程(当L=1时)的值,本次作业采用蒙特卡洛积分来计算
Vec3f IntegrateBRDF(Vec3f V, float roughness, float NdotV) {float A = 0.0;float B = 0.0;float C = 0.0;const int sample_count = 1024;Vec3f N = Vec3f(0.0, 0.0, 1.0);samplePoints sampleList = squareToCosineHemisphere(sample_count);for (int i = 0; i < sample_count; i++) {// TODO: To calculate (fr * ni) / p_o here}return {A / sample_count, B / sample_count, C / sample_count};
}
看原始代码可以看出采样点设置为1024,采样结果已经放装好了。
需要写的就是如何计算这个渲染方程
Vec3f IntegrateBRDF(Vec3f V, float roughness, float NdotV) {float A = 0.0;float B = 0.0;float C = 0.0;const int sample_count = 1024;Vec3f N = Vec3f(0.0, 0.0, 1.0);samplePoints sampleList = squareToCosineHemisphere(sample_count);for (int i = 0; i < sample_count; i++) {// TODO: To calculate (fr * ni) / p_o here// Edit StartVec3f L = normalize(sampleList.directions[i]);float pdf = sampleList.PDFs[i];Vec3f H = normalize(V + L);float NdotL = std::max(dot(N, L), 0.0f);float NDF = DistributionGGX(N, H, roughness);float G = GeometrySmith(roughness, NdotV, NdotL);float F = 1.0f;float mu = NdotL;float numerator = NDF * G * F;float denominator = 4.0 * NdotV * NdotL;A = B = C += numerator / denominator / pdf * mu;// Edit End}return {A / sample_count, B / sample_count, C / sample_count};
}
运行代码后得到
它存储的是任何粗糙度和任何观察角度下的E(μ)。可以看到图片有很多噪声,这是因为低粗糙度的微表面材质接近镜面反射材质,即微表面的法线m(即半程向量h)集中分布在几何法线n附近,而我们由采样入射光方向i计算出的微表面法向量m分布并不会集中在几何法线n附近,也就是说这与实际低粗糙度的微表面法线分布相差很大,因此积分值的方差就会很大。
预计算Eavg
要得到补充能量的BRDF,需要计算
这是通过推导出来的,我不考虑如何推导的,只需要知道,当BRDF是时,渲染方程计算结果就是损失的能量,这里不知道的数据就是Eavg
Vec3f IntegrateEmu(Vec3f V, float roughness, float NdotV, Vec3f Ei) {return Ei * NdotV * 2.0f;
}
结果为