一切被直接光照照到的物体,会作为次级光源。
使用ShadowMap就可以知道哪些面片被直接照亮
对渲染方程代入如上计算,得到如下结果
在上式中, 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)
对于特定情况效果比较好,如手电筒(用RSM做手电筒!!!?)
好处:非常好写,第一个Path生成RSM,第二个path用眼睛看向场景。
问题:
第一步,先找到哪些物体表面能够被直接照亮,使用ShadowMap,认为每一个ShadowMap的像素就是一个次级光源。
第二步,次级光源如何贡献到点P:我们认为所有的反射物(次级光源),都是Diffuse的。
RSM与离线渲染中的VPL方式是比较相似的,RSM是硬件加速版本的VPL(Virtual Point Light 虚拟点光源)。
在摄像机视角渲染整个场景,并记录场景中可见的像素点的颜色、视口矩阵下的法线、视口矩阵下的坐标。
得到一组场景信息,可以理解为GBuffer。
从光源视口下看向场景,生成RSM图。
记录的从光源视口下可见像素点的反射光颜色值、相机视角下的法线、相机视角下的坐标、光源视角下的坐标。
得到的结果是一组256x256的RSM图
············反射光颜色值 ······························· 相机视角下的法线 ··················
··········· 相机视角下的坐标 ·························· 光源视角下的坐标(主要保存z轴深度)·······························
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;
如果该像素点在光照空间外,则只提供一个基本的环境光照。
当该像素在光照空间内,首先判断该点是否在阴影中
// 直接光照&环境光照
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;
}
}
将该着色点投影到光照视口下的点,则已知该点在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;
得到结果,渲染!
vec3 Result = DirectIllumination + IndirectIllumination / u_VPLNum ;
区别【好像没太大区别 dog.jpg】