全局光照算法:reflective shadow maps
1. 技术理解
RSM的全称是reflective shadow maps
,受到Instant Radiosity这个离线技术的启发,其思想和ShadowMap的思想近似。在正式介绍和了解这个技术之前,我需要确定RSM用处何在?我想,《RTR4》中对它的分类很正确——Dynamic Diffuse Global Illumination
,这是一个处理动态全局漫反射的技术:
- GI(全局光照):用于二次及以上bounce造成的间接光。
- Dynamic(动态):可以实时更新,可作用于动态物体。
- Diffuse(漫反射):更加细致的说,这个技术考虑的是间接光照中的漫反射部分。
RSM和立即辐射度方法一样,都是在直接光照亮的区域,选择采样点作为发光物(虚拟点光源VPL
),来计算间接光照。主要分为两个Pass
:
Pass 1
:从光源的角度,对整个场景进行一次渲染。这个过程在引擎中可以直接和ShadowMap的生成放在一块。不过不同的是,除了存储深度之外(这个感觉就是直接使用ShadowMap
的结果),我们还需要存储世界空间位置、法线、辐射通量。Pass 2
:在正常的lighting pass
中,考虑基于RSM
的间接光。
1.1 推导
在RSM
中,每个像素都被解释为一个间接照亮场景的像素光。通量ϕp\phi_pϕp定义了其亮度。
辐射通量ϕp\phi_pϕp:radiant flux,单位是
W
(瓦特)。描述的是光源的总体能量——每秒的发出的辐射能量。而辐照度E
的单位是W/m2W/m^2W/m2,辐射强度I
的单位是W/srW/srW/sr。我们在光照积分中比较常见的是:辐射率L
,单位是W/(m2sr)W/(m^2sr)W/(m2sr)
注意:当光源的面积无限小时,基本可以将辐射强度I
等同于辐射度L
。
忽略可见性,我们的光照积分应该时如下形式:
Lo(p,wo)=∫ΩpacthLi(p,wi)V(p,wi)fr1(p,wi,wo)cosθpdwi=∫ApacthLi(p,wi)fr1(p,wi,wo)cosθpcosθq∣∣q−p∣∣2dAL_o(p,w_o)=\int_{\Omega_{pacth}}{L_i(p,w_i)V(p,w_i)f_{r1}(p,w_i,w_o)\cos{\theta_p}dw_i} \\ =\int _{A_{pacth}}L_i(p,w_i)f_{r1}(p,w_i,w_o)\frac{\cos{\theta_p} \cos{\theta_q}}{||q-p||^2} dA \\ Lo(p,wo)=∫ΩpacthLi(p,wi)V(p,wi)fr1(p,wi,wo)cosθpdwi=∫ApacthLi(p,wi)fr1(p,wi,wo)∣∣q−p∣∣2cosθpcosθqdA
参考上图,作者假设光源是无限小的,而且dw=dAcosθq∣∣x/−x∣∣2dw=\frac{dA\cos{\theta_q}}{||x^/-x||^2}dw=∣∣x/−x∣∣2dAcosθq,我们可以做出如下推导:法线为n
的表面点p
因像素光q
而产生的辐照度为:
dE(p)=Li(p,wi)cosθpdwiE(p)=∫Licosθpdwi=∫ApatchLicosθpcosθq∣∣q−p∣∣2dAdE(p)=L_i(p,w_i)\cos{\theta_p}dw_i \\ \\ E(p)=\int L_i\cos{\theta_p}dw_i=\int_{A_{patch}}L_i\frac{\cos{\theta_p} \cos{\theta_q}}{||q-p||^2} dA dE(p)=Li(p,wi)cosθpdwiE(p)=∫Licosθpdwi=∫ApatchLi∣∣q−p∣∣2cosθpcosθqdA
又因为对于一个diffuse patch
来说,所有方向的出射光都是相同的——可以有Li=fr⋅ΦdAL_i=f_r \cdot \frac{\Phi}{dA}Li=fr⋅dAΦ(这个公式如何理解?),所以:
E(p)=cosθpcosθq∣∣q−p∣∣2ΦpE(p)=\frac{\cos{\theta_p} \cos{\theta_q}}{||q-p||^2}\Phi_p E(p)=∣∣q−p∣∣2cosθpcosθqΦp
实际上:Φp=Φlight⋅frq\Phi_p=\Phi_{light}\cdot f_{r_q}Φp=Φlight⋅frq。最终推导为:
争议:∣∣x−xp∣∣||x-x_p||∣∣x−xp∣∣的上标是
4
,还是2
,有所争议。
参考:
2. Pass 1:Generation
2.1 Data
由果推因,最后的计算公式需要世界空间位置、法线、辐射通量,我们就存储它们。但现在,依然存在一个问题,我们存取的通量怎么获得?
如果说,平行光的通量是Φ\PhiΦ,那么照明这个像素块之后,出射辐射率是:Lo=fr⋅ΦdAL_o=f_r\cdot \frac{\Phi}{dA}Lo=fr⋅dAΦ。而这个像素块此时的辐射通量是:
Φp=∫Area∫Ω(Lo)dAdwo=∫Ω(fr⋅Φ)dwi\Phi_p=\int_{Area}\int_{\Omega}(L_o)dAdw_o=\int_{\Omega}(f_r\cdot \Phi) dw_i Φp=∫Area∫Ω(Lo)dAdwo=∫Ω(fr⋅Φ)dwi
由于这个patch
是漫反射的,所以Φ\PhiΦ和frf_rfr都是常量。而fr=ρ/πf_r=\rho/\pifr=ρ/π和∫(1)dwi=π\int(1) dw_i=\pi∫(1)dwi=π,最终:
Φp=ρ⋅Φ\Phi_p=\rho\cdot \Phi Φp=ρ⋅Φ
2.2 实现
所以,最终,我们在第一个Pass中,这样做:
代码来自:https://github.com/AngelMonica126/GraphicAlgorithm/blob/master/001_Reflective%20shadow%20map/RSMBuffer_FS.glsl
void main()
{vec3 TexelColor = texture(u_DiffuseTexture, v2f_TexCoords).rgb;//TexelColor = pow(TexelColor, vec3(2.2f));vec3 VPLFlux = u_LightColor * TexelColor;Flux_ = VPLFlux;Normal_ = v2f_Normal;Position_ = v2f_FragPosInViewSpace;
}
3.3 点光源
之前我们考虑的都是平行光,如果是点光源,我们或许应该在这里考虑一下光线衰减和余弦问题:
Φp=ρ⋅Φ⋅dot(xL−xp,np)/(∣∣xL−xp∣∣2)\Phi_p=\rho\cdot \Phi \cdot dot(x_L-x_p,n_p)/(||x_L-x_p||^2) Φp=ρ⋅Φ⋅dot(xL−xp,np)/(∣∣xL−xp∣∣2)
3 Pass 2:Lighting
3.1 基础实现
主要流程,读取上一个pass
存的数据,利用下面的公式,计算间接照明。
float3 indirectIllumination = float3(0, 0, 0);
//最远采样半径
float rMax = rsmRMax;// rsmSampleCount = hight * width(etc. 512*512)
for (uint i = 0; i < rsmSampleCount; ++i)
{// 这里就是随机值float2 rnd = rsmSamples[i].xy;float2 coords = textureSpacePosition.xy + rMax * rnd;// 依次读取位置、法线、通量float3 vplPositionWS = g_rsmPositionWsMap.Sample(g_clampedSampler, coords.xy).xyz;float3 vplNormalWS = g_rsmNormalWsMap.Sample(g_clampedSampler, coords.xy).xyz;float3 flux = g_rsmFluxMap.Sample(g_clampedSampler, coords.xy).xyz;// 计算当前像素在此RSM像素灯光的影响下,导致的辐照度Efloat3 result = flux* ((max(0, dot(vplNormalWS, P – vplPositionWS))* max(0, dot(N, vplPositionWS – P)))/ pow(length(P – vplPositionWS), 4));indirectIllumination += result;
}
indirectIllumination = result / rsmSampleCount;
return saturate(indirectIllumination * rsmIntensity);
3.2 改进方法
对于一个典型的阴影图来说,像素的数量是很大的(512×512512\times 512512×512),所以上述sum
计算是非常昂贵的,在实时情况下不实用。相反,作者必须将总和减少到有限的光源数量,例如400
个。作者使用重要性驱动的方法来做到这一点,试图将采样集中到相关像素灯上。
一般来说,可以说x
和阴影图中的像素光xpx_pxp之间的距离是它们在世界空间中的距离的合理近似值。如果相对于光源的深度值差别很大,世界空间的距离就会大得多,会高估其影响。然而,重要的间接光源总是很接近,这些光源在阴影图中也必须是接近的。
所以作者决定按以下方式获得像素光的样本:
首先,作者将
x
投影到阴影图(→(s,t))(→(s,t))(→(s,t))中。然后,作者选择(s,t)(s,t)(s,t)周围的像素光,样本密度随着与(s,t)(s,t)(s,t)距离的平方而减少。这可以通过选择相对于(s,t)(s,t)(s,t)的极坐标样本轻松实现,也就是说,如果ξ1ξ_1ξ1和ξ2ξ_2ξ2是均匀分布的随机数,作者选择位置:
然后,必须通过用ξ12ξ^2_1ξ12对样本进行加权,来补偿不同的采样密度(以及最后的归一化)。下图显示了一个采样模式的例子。
实际实现过程中,我们在CPU
端通过低差异序列,生成随机数(ξ1,ξ2)(\xi_1,\xi_2)(ξ1,ξ2)。s
和t
不用管,就是GPU
端像素的UV
坐标,我们只需要计算:rmaxξ1sin(2πξ2)r_{max}\xi_1\sin{(2\pi\xi_2)}rmaxξ1sin(2πξ2)、rmaxξ1cos(2πξ2)r_{max}\xi_1\cos{(2\pi\xi_2)}rmaxξ1cos(2πξ2)、ξ12\xi_1^2ξ12。将这三个数据存储四维向量数组,作为uniform data传入GPU
:
此代码非原创,来自:https://github.com/AngelMonica126/GraphicAlgorithm/blob/master/001_Reflective%20shadow%20map/ShadingWithRSMPass.cpp
std::default_random_engine e;
std::uniform_real_distribution<float> u(0, 1);
for (int i = 0; i < m_VPLNum; ++i)
{float xi1 = u(e);float xi2 = u(e);m_VPLsSampleCoordsAndWeights.push_back({ xi1 * sin(2 * ElayGraphics::PI * xi2), xi1 * cos(2 * ElayGraphics::PI * xi2), xi1 * xi1, 0 });
}genBuffer(GL_UNIFORM_BUFFER, m_VPLsSampleCoordsAndWeights.size() * 4 * sizeof(GL_FLOAT), m_VPLsSampleCoordsAndWeights.data(), GL_STATIC_DRAW, 1);
然后,在GPU
端主要加入的就是这个权重
:
for (int i = 0; i < u_NumSamples; i++)
{vec3 offset = texelFetch(s_Samples, ivec2(i, 0), 0).rgb;vec2 tex_coord = light_coord.xy + offset.xy * u_SampleRadius + (((offset.xy * u_SampleRadius) / 2.0) * dither_offset);vec3 vpl_pos = texture(s_RSMWorldPos, tex_coord).rgb;vec3 vpl_normal = normalize(texture(s_RSMNormals, tex_coord).rgb);vec3 vpl_flux = texture(s_RSMFlux, tex_coord).rgb;vec3 result = light_attenuation(vpl_pos) * vpl_flux * ((max(0.0, dot(vpl_normal, (P - vpl_pos))) * max(0.0, dot(N, (vpl_pos - P)))) / pow(length(P - vpl_pos), 4.0));// 权重result *= offset.z * offset.z;indirect += result;
}
3.3 关于最后的结果是否要乘上albedo
鄙人认为,最后得到的EpE_pEp,不是当前像素本身产生辐照度(对眼睛生效),而是其他像素灯对此像素产生的辐照度,或者推导上可以看看:
Lo(p,wo)=∫ApacthLi(p,wi)fr1(p,wi,wo)cosθpcosθq∣∣q−p∣∣2dAL_o(p,w_o) =\int _{A_{pacth}}L_i(p,w_i)f_{r1}(p,w_i,w_o)\frac{\cos{\theta_p} \cos{\theta_q}}{||q-p||^2} dA \\ Lo(p,wo)=∫ApacthLi(p,wi)fr1(p,wi,wo)∣∣q−p∣∣2cosθpcosθqdA
E(p)=∫Licosθpdwi=∫ApatchLicosθpcosθq∣∣q−p∣∣2dAE(p)=\int L_i\cos{\theta_p}dw_i=\int_{A_{patch}}L_i\frac{\cos{\theta_p} \cos{\theta_q}}{||q-p||^2} dA E(p)=∫Licosθpdwi=∫ApatchLi∣∣q−p∣∣2cosθpcosθqdA
而frf_rfr又和面积没有关系,所以:
Lo(p,wo)=E(p)∗fr1L_o(p,w_o)=E(p)*f_{r1} Lo(p,wo)=E(p)∗fr1
也就是说,我认为,最终的间接光照结果,应该是:
indirect = indirect * albedo / PI;
indirect = indirect / VPL_NUM;
light_result = directLight + indirect;
4 其他
ToDo
全局光照算法:reflective shadow maps相关推荐
- 实时全局光照(Real-time Global Illumination)与Reflective Shadow Maps(RSM)
文章目录 1 概述 1.1 什么是全局光照(GI)? 1.2 光照模型处理间接光照的方法 1.3 实时全局光照的思想 2 GI的主要问题 3 方法一:Reflective Shadow Maps(RS ...
- 计算机图形学 全局光照及方法,高真实感全局光照算法优化研究
摘要: 全局光照渲染技术是计算机图形学领域的热点问题之一,目前该领域中主要存在两个核心问题需要解决:渲染质量和渲染速度.而文物数字化领域对渲染技术具有以下需求:展示交互性以及渲染保真度.本文侧重于保证 ...
- Cryengine 3新的全局光照算法简介
[文章摘要]Carsten Dachsbacher在其斯图加特大学的个人页面上发表了一篇与Crytek共同创作的论文<实时间接光照中的渐进光照传播Cascaded Light Propagati ...
- 实时全局光照RSM-Reflective Shadow Maps(RSM)
全局光照介绍 现实世界光处于线性空间,光照效果是可以叠加的,最终的光照结果 = 直接光照 + 间接光照,结果也被称为全局光照(Global illumination).与之对比的是局部光照--仅考虑直 ...
- Q88:全局光照(Global Illumination)——Path Tracing
88.1 引入(Introduction) 截至当前,回忆一下我们学过的针对直接光照和间接光照的不同反射模型. 直接光照: Phong反射模型.包含漫反射部分和高光反射部分. 间接光照: 对于镜面材料 ...
- 实时高清渲染:全局光照(Global Illumination)[1]
目录 基础知识: Radiance: Irradiance: Radiant flux: Radiant Intensity: Solid Angle: Lambertian surface: Lam ...
- GPU Gems1 - 14 透视阴影贴图(Perspective Shadow Maps: Care and Feeding)
开篇先说,这文章好难懂啊,到现在也没完全弄明白,有大佬的话欢迎指点.这篇大体意思是对Perspective Shadow Maps进行部分优化.GEMS这书这点是真挺蛋疼,很多文章都是对某项技术的优化 ...
- 新版unity2019.3 全局光照GI 系统以及反射探针的详细说明教程
unity放话在2021年以前全面放弃Enlighten. 关于虚幻4和unity的光照渲染问题 两个软件各启动5分钟,只要眼睛还在,就能立即分辨出优劣.但需要说明的是虽然虚幻的渲染算法是公认的强,但 ...
- (中文详解篇)smallpt: 99行代码完成全局光照Path Tracing
目录 0. 什么是SmallPT Features 1. 光线追踪需要了解知识 1.1 什么是全局光照? 1.2 渲染方程 2. SmallPT代码分析 2.1 代码块1 2.2 代码块2 2.2.1 ...
最新文章
- 开启基于Query的实例分割新思路!腾讯华科提出QueryInst
- php上传过滤,编写PHP脚本过滤用户上传的图片_PHP
- Clojure开发OpenCV的简介
- 数据可视化|实验二 分析特征间关系
- linux qt sql,linux qt联接sqlserver怎么配置服务器
- Java 接受reactjs数据_ReactJS:从API获取数据
- Barh函数--Matplotlib
- 【2022最新罗技G502吃鸡宏】
- 共享单车项目计划书_2020年共享单车商业计划书模板
- 阿里云服务器的80端口被封了么?
- 世界主要经济体历年GDP排名一览(前50强)
- android显示输入法键盘布局,android 解决输入法键盘遮盖布局问题
- ISO7816 调试心得
- 天下难事必作于易,天下大事必作于细
- 南卡和Snowkids电容笔哪款更值得入手?口碑最佳的国产电容笔
- 2021-9.15基于目标速度的汽车 ACC系统油门控制策略研究-童宝锋
- UGUI 打字机效果
- Win32多线程调用gdal库接口
- Socket总结 node搭建简单的http服务器
- DSY2933*地图
热门文章
- HTML+CSS大作业——浪漫红色自适应网上鲜花店(16页) HTML+CSS+JavaScript 大学生鲜花网页作品 植物网页设计作业模板 学生网页制作源代码下载
- flowable 多人签收_一波年味请你签收!提前感受浓浓年味吧!
- PHP入门7-1函数
- nalu模式多slice_图像、帧、片、NALU
- 计算机图形学【1】基础概念,图片格式及C++实现
- python中的not具体使用及意思(关键词:Python/not)
- 虚拟机jvm和hotspot的联系与区别
- 学习情况及发展规划交流
- 前端设置input复选框的大小两种方式
- 404 NOT FOUND!---设置404的作用