Reflective Shadow Maps(RSM)
一切被直接光照照到的物体,会作为次级光源。
问题1:哪些面片被直接照亮
使用ShadowMap就可以知道哪些面片被直接照亮
问题2:各个面片对P点的贡献分别是多少。
对渲染方程代入如上计算,得到如下结果
在上式中,
L
i
(
q
→
p
)
L_i(q\rightarrow p)
Li(q→p)可优化为如下函数
因为:
f
r
=
ρ
/
π
,
ρ
为反射率
L
i
=
f
r
⋅
ϕ
d
A
,
ϕ
是直接光照的光强
f_r = \rho / \pi,\rho 为反射率\\ \quad\\ L_i = f_r \cdot \frac{\phi}{dA},\phi 是直接光照的光强
fr=ρ/π,ρ为反射率Li=fr⋅dAϕ,ϕ是直接光照的光强
因此有次级光源光强
E
p
(
x
,
n
)
=
ϕ
p
m
a
x
{
0
,
d
o
t
(
n
p
,
(
x
−
x
p
)
}
m
a
x
{
0
,
d
o
t
(
n
,
x
p
−
x
)
}
∣
∣
x
−
x
p
∣
∣
4
E_p(x,n)=\phi_p \frac{max\{0,dot(n_p,(x-x_p)\}max\{0,dot(n,x_p-x)\}}{||x-x_p||^4}
Ep(x,n)=ϕp∣∣x−xp∣∣4max{0,dot(np,(x−xp)}max{0,dot(n,xp−x)}
L 0 = E p ( x , n ) ∗ f r ( p , p → q , w o ) L_0 = E_p(x,n) * f_r(p,p\rightarrow q,w_o) L0=Ep(x,n)∗fr(p,p→q,wo)
其他近似
- 不考虑Visbility项,认为次级光源不会被遮挡。
- 不对所有次级光源采样,只对该点在ShadowMap中周围的部分像素采样。
ShadowMap记录值
- 深度
- 世界坐标
- 法线
- 次级光源强度 ϕ \phi ϕ
特点与效果
对于特定情况效果比较好,如手电筒(用RSM做手电筒!!!?)
好处:非常好写,第一个Path生成RSM,第二个path用眼睛看向场景。
问题:
- 直接光源较多时,间接光源也会成倍增多
- 不会计算反射光的可见性检查
- 次级光源效果和运行速度,会根据采样率的不同的不同
如何实现
第一步,先找到哪些物体表面能够被直接照亮,使用ShadowMap,认为每一个ShadowMap的像素就是一个次级光源。
第二步,次级光源如何贡献到点P:我们认为所有的反射物(次级光源),都是Diffuse的。
RSM与VPL(Virtual Point Light 虚拟点光源)
RSM与离线渲染中的VPL方式是比较相似的,RSM是硬件加速版本的VPL(Virtual Point Light 虚拟点光源)。
RSM实现步骤
第一个Pass
在摄像机视角渲染整个场景,并记录场景中可见的像素点的颜色、视口矩阵下的法线、视口矩阵下的坐标。
得到一组场景信息,可以理解为GBuffer。
第2个Pass
从光源视口下看向场景,生成RSM图。
记录的从光源视口下可见像素点的反射光颜色值、相机视角下的法线、相机视角下的坐标、光源视角下的坐标。
得到的结果是一组256x256的RSM图
············反射光颜色值 ······························· 相机视角下的法线 ··················
··········· 相机视角下的坐标 ·························· 光源视角下的坐标(主要保存z轴深度)·······························
第3个Pass
1.将Pass1中保存的像素坐标转化到与RSM相同的光源坐标
ivec2 FragPos = ivec2(gl_GlobalInvocationID.xy);//当前执行单元在全局工作组中的位置的有效索引
vec3 FragViewNormal = normalize(texelFetch(u_NormalTexture, FragPos, 0).xyz);
vec3 FragAlbedo = texelFetch(u_AlbedoTexture, FragPos, 0).xyz;
vec3 FragViewPos = texelFetch(u_PositionTexture, FragPos, 0).xyz;
// 获取 摄像机视口下像素 在光源视口下的位置坐标
vec4 FragPosInLightSpace = u_LightVPMatrixMulInverseCameraViewMatrix * vec4(FragViewPos, 1);
FragPosInLightSpace /= FragPosInLightSpace.w;
vec2 FragNDCPos4Light = (FragPosInLightSpace.xy + 1) / 2;
2. 计算该像素点的直接光照
如果该像素点在光照空间外,则只提供一个基本的环境光照。
当该像素在光照空间内,首先判断该点是否在阴影中
- 如果在阴影中,则只计算环境光照
- 如果不在阴影中,则计算直接光照
// 直接光照&环境光照
vec3 testOutput;
vec3 DirectIllumination;
// 如果该像素在RSM范围之外,以0.1倍源像素作为其环境光照
// FragPosInLightSpace.z范围在【-1,0】的区间中.属于可被光照直接照射的范围
// 这里可通过与RSM中的深度进行比较,来确定是否有阴影生成,或是否可被作为次级光源
if( FragPosInLightSpace.z < -1.0f || FragPosInLightSpace.z > 0.0f||\
FragPosInLightSpace.x >= 1.0f || FragPosInLightSpace.x <= -1.0f||\
FragPosInLightSpace.y >= 1.0f || FragPosInLightSpace.y <= -1.0f )//
{
DirectIllumination = vec3(0.1) * FragAlbedo;
testOutput = vec3(0.f,0.f,0.f);
}
else//反之,将原像素经过夹角衰减后的值作为直接光照
{
// view space下的光照坐标
vec3 RSWLightPosition =texture(u_RSMLightPositionTexture,FragNDCPos4Light.xy).xyz;
// 判断是否在阴影中(近小【-1】远大【0】)
if(RSWLightPosition.z + 0.001 > FragPosInLightSpace.z){//不在阴影中
DirectIllumination = FragAlbedo* max(dot(-u_LightDirInViewSpace, FragViewNormal), 0.1);// ;
}
else{//在阴影中
DirectIllumination = vec3(0.1) * FragAlbedo;
}
}
3. 计算该像素点的间接光照
将该着色点投影到光照视口下的点,则已知该点在RSM纹理上的xy轴位置。
在RSM图中,在(x,y)像素上以R为半斤的周围进行随机采样,每一个采样点作为次级光源进行光照计算。
// 计算间接光照
vec3 IndirectIllumination = vec3(0);
float RSMTexelSize = 1.0 / u_RSMSize;
for(int i = 0; i < u_VPLNum; ++i)
{
if(FragPosInLightSpace.z > -1.0f && FragPosInLightSpace.z <0.0f){
//获取随机采样数组
vec3 VPLSampleCoordAndWeight = u_VPLsSampleCoordsAndWeights[i].xyz;
// 在光源视口下的该像素点周围一圈进行采样
vec2 VPLSamplePos = FragNDCPos4Light + u_MaxSampleRadius * VPLSampleCoordAndWeight.xy * RSMTexelSize;
// 在RSM图中获取采样点的次级光源颜色值
vec3 VPLFlux = texture(u_RSMFluxTexture, VPLSamplePos).xyz;
// 获取采样点在摄像机视口坐标下的的法线和位置坐标
vec3 VPLNormalInViewSpace = normalize(texture(u_RSMNormalTexture, VPLSamplePos).xyz);
vec3 VPLPositionInViewSpace = texture(u_RSMPositionTexture, VPLSamplePos).xyz;
// 计算该采样点对该像素的间接光照值
IndirectIllumination += calcVPLIrradiance(VPLFlux, VPLNormalInViewSpace, VPLPositionInViewSpace, FragViewPos, FragViewNormal, VPLSampleCoordAndWeight.z);
}
}
IndirectIllumination *= FragAlbedo;
//间接光
vec3 Result = IndirectIllumination / u_VPLNum;
4. 将直接光照与间接光照加起来
得到结果,渲染!
vec3 Result = DirectIllumination + IndirectIllumination / u_VPLNum ;
区别【好像没太大区别 dog.jpg】