Real-Time Shadow Technique

LiSPSM+VSM( shdowMap + Light space perspective shadow map + variance shadow map) 介绍 

1:shadow map

      Shadow map 是目前实现阴影效果的主流做法,可以做到self-shadow.

      假设在3D环境中只有太阳光源,如果太阳光可以直射到物体上某个位置A时,A点就不会落在阴影中。如果太阳光移动到物体表面上的位置B前,先照射到另一个物体,太阳的光线会被切断,B就落在阴影区里。可以把阴影的计算视为碰撞检测,A点不在阴影区内,也就是从A点到太阳之间可以直达,完全没有遮蔽;B点在阴影区内,也就等于是从B点到太阳之间的有某样东西切断了它们的直接连接。

      使用shadow map 来实现阴影效果,相当于使用GPU做碰撞检测,测试物体表面与光源之间是否存在任何阻隔。做隐藏面消除用的Z Buffer事件上就是对镜头做类似的工作。镜头里所看到的物体,它们到镜头之间没有任何阻隔,Z Buffer Test其实就是在做碰撞检测。如果物体到镜头之间有阻隔,就算它没有被略过不画,也会在后面的步骤中被其它对象所覆盖。

      把镜头移到光源位置,再把物体到光源间的距离值填入到动态贴图中,它就具有足够的信息来对光源做碰撞检测。把距离值画入动态贴图中是一件简单的事,不需要特别的技巧,因为Z Buffer存储的就是这个距离值,只要把 Z Buffer做为贴图直接拿来使用就可以了。没有规定动态贴图只能使用存储颜色的RGBA类型,有些硬件允许把存储距离的Z Buffer用来存储动态贴图:

      Uniform Shadow map 的原理很容易理解,有很多资料,这里不再介绍。shadow map 需要一次额外的渲染pass( shadow mapping requirs only one additional rendering pass),

     在引擎中一般会Z pre-pass, 那么我们利用MRT(Multi Render Target)技术把这两个pass 合二为一,另外把表面法线等存入额外的表面。以供 Deferred Lighting, Depth of field, Bloom 等 后期处理使用

2:Focus region B

      Shadow map遇到的一个最大的问题就是当场景很大时图片分辨率不足。在一个很大的场景中,我们能看到的区域A只是场景的一小部分,能投射阴影到区域A的物体所形成的区域B也只是一小部分,我们只需要把B中物体拍摄到阴影贴图中就可以了。

     图:详见shaderX 4    Page313  Robust shadow mapping with light space perspective shadow maps.

3:light space perspective shadow map

     基于PSM改进的 Warp the shadow map 技术, 使离摄像机更近的物体在shadow map中所占的区域更大,离的远的减少。

4:Variance shadow map

     variance shadow map 是一种柔化阴影技术,即在阴影边缘产生软化的边缘.

     效果如下图:

     

     VSM主要利用了 "期望","方差","切比雪夫不等式"的概念。

     与常规阴影利用直接把深度存入阴影贴图不同,在此我们使用D3DFMT_G32R32F格式,分别记录深度,及深度平方到深度图中

// 计算矩
float2 ComputeMoments( float depth)
{
      float2 moments;

      // 第一个moment为深度本身
     moments.x = depth;
     moments.y = depth * depth;

    return moments;
}

float4 PS( VS_OUTPUT In) : COLOR
{
     float2  moments = ComputeMoments( In.screenPos.z / In.screenPos.w);
 
    return  float4( moments, 0.f, 1.f);
}

方差 σ^2 = E(x^2) - (E(x))^2;

切比雪夫不等式:

p (t) = σ^2 / (σ^2 + ( t-E(x))^2);

 

//--------------------------------------------------------
// Computes Chebyshev's Inequality
//
float ChebyshevUpperBound( float2 moments, float t, float minVariance)
{
 // Standard shadow map comparison
 float p = ( t<=moments.x);

 // compute probabilistic upper bound
 float variance = moments.y - ( moments.x*moments.x);
 variance = max( variance, minVariance);

 // compute probabilistic upper bound
 float d = t - moments.x;
 float p_max = variance / ( variance + d*d);

 return max( p, p_max);
}

//--------------------------------------------------------
// variance shadow map
//
uniform float g_lbrAmount = 0.18f;

float tex2DSM( sampler2D shadowSampler, float4 posInLightSpace)
{
 float4 projPos = posInLightSpace / posInLightSpace.w;

 // soft shadow
 float2 moments = tex2D( shadowSampler, projPos.xy).xy;

 float shadowContrib = ChebyshevUpperBound( moments, projPos.z, 0.00001f);

 float lbr = clamp( (shadowContrib - g_lbrAmount)/(1-g_lbrAmount), 0, 1);
 //float lbr = smoothstep( g_lbrAmount, 1, shadowContrib);

 return saturate( lbr + 0.4f);
}

详见 gpu gems  3第八章节

5:Parallel-Split Shadow Maps

       平行分隔阴影基于以上的工作,还是很容易实现的。本人在此未实现,详情可参考 gpu gems3 第十章节!


你可能感兴趣的:(Real-Time Shadow Technique)