OpenGL阴影映射技术介绍
SM
概述: Shadow
Mapping,最基本的阴影算法之一,通过使用光源的视角渲染场景的深度信息来生成阴影,其核心思想时将光源的深度信息存储在一个深度贴图中,在进行渲染时,利用该深度信息来判断每个片段是否在阴影中
步骤:
- 生成深度贴图:对场景进行一次光源视角的渲染,生成一个深度贴图,该贴图存储了光源照射到场景中的每个像素的深度值
- 在光源的视角中,进行正交或透视投影渲染
- 将深度值存储在深度贴图中
- 使用深度贴图进行阴影测试:在场景的主渲染阶段,首先将每个片段(像素)的世界坐标转换到光源的视角空间,然后在光源的深度贴图查询该位置的深度值,如果当前片段的深度值大于深度贴图中的值,说明该片段在阴影中
算法实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| float SM(vec4 fragPosLightSpace,vec3 normal,vec3 lightDir,sampler2D shadowMap){ vec3 projCoords=fragPosLightSpace.xyz/fragPosLightSpace.w; projCoords=projCoords*.5+.5; if(projCoords.z>1.||projCoords.z<0.) return 0.; float closestDepth=texture(shadowMap,projCoords.xy).r; float currentDepth=projCoords.z; float bias=max(.05*(1.-dot(normal,lightDir)),.005); float shadow=currentDepth-bias>closestDepth?1.:0.; return shadow; }
|
效果图:
PCF
概述: Percentage Closer
Filter是对阴影贴图的一种后处理技术,用于减少阴影的锯齿状边缘,通过对阴影贴图中的多个样本进行采样,计算它们的平均值,从而实现阴影的模糊过渡
步骤:
- 多次采样:在阴影测试阶段,针对每个片段,不仅仅检查单个像素的深度值,而是对一个小范围内的小像素进行采样(例如3×3或5×5的采样区域)
- 计算平均值:对采样的范围进行阴影测试,如果当前片段大于从深度贴图采样的值,认为其在阴影中,累加到shadow变量中,最后对shadow变量取平均值
算法实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| float PCF(vec4 fragPosLightSpace,vec3 normal,vec3 lightDir,sampler2D shadowMap){ vec3 projCoords=fragPosLightSpace.xyz/fragPosLightSpace.w; projCoords=projCoords*.5+.5; if(projCoords.z>1.||projCoords.z<0.) return 0.; float closestDepth=texture(shadowMap,projCoords.xy).r; float currentDepth=projCoords.z; float bias=max(.05*(1.-dot(normal,lightDir)),.005); float shadow=0.; vec2 texelSize=1./textureSize(shadowMap,0); for(int x=-PCF_RADIUS;x<=PCF_RADIUS;++x) { for(int y=-PCF_RADIUS;y<=PCF_RADIUS;++y) { float pcfDepth=texture(shadowMap,projCoords.xy+vec2(x,y)*texelSize).r; shadow+=currentDepth-bias>pcfDepth?1.:0.; } } float total=2*PCF_RADIUS+1; shadow/=(total*total); return shadow; }
|
特点:
PCF的优点是相对简单,但可能会导致较为模糊的阴影,且在高分辨率阴影贴图上会更有效。
效果图:
PCSS
概述: Percentage Closer Soft
Shadows是一种基于PCF的扩展,旨在生成更自然的软阴影效果。PCSS通过在阴影贴图的采样过程中引入光源半径的概念,来根据片段距离光源的远近来调整阴影的柔和成都
步骤:
- 计算光源的大小:由于距离光源较远的区域阴影边缘应该更加模糊,PCSS根据光源的大小(即阴影的投射区域)来动态调整阴影的软化程度
- 应用阴影半径:对于每个片段,计算该片段距离光源的距离,并基于该距离来调整PCF的采样区域大小。距离较远的片段采用较大的采样区域,从而产生更软的阴影
算法实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| float PCSS(vec4 fragPosLightSpace,vec3 normal,vec3 lightDir,sampler2D shadowMap){ vec3 projCoords=fragPosLightSpace.xyz/fragPosLightSpace.w; projCoords=projCoords*.5+.5; if(projCoords.z>1.||projCoords.z<0.) return 0.; float closestDepth=texture(shadowMap,projCoords.xy).r; float currentDepth=projCoords.z; float bias=max(.05*(1.-dot(normal,lightDir)),.005); float avgDepth=findBlocker(projCoords.xy,currentDepth,shadowMap,bias); if(avgDepth==-1.){ return 0.; } float penumbra=(currentDepth-avgDepth)/avgDepth*lightWidth; float filterRadius=penumbra*NEAR_PLANE/currentDepth; filterRadius*=PCFSampleRadius; float shadow=0.; vec2 texelSize=1./textureSize(shadowMap,0); for(int x=-PCF_RADIUS;x<=PCF_RADIUS;++x) { for(int y=-PCF_RADIUS;y<=PCF_RADIUS;++y) { float shadowMapDepth=texture(shadowMap,projCoords.xy+filterRadius*vec2(x,y)*texelSize).r; shadow+=currentDepth-bias>shadowMapDepth?1.:0.; } } float total=2*PCF_RADIUS+1; shadow/=(total*total); return shadow; }
|
特点:
PCSS能够产生更加平滑和逼真的阴影效果,但相对于普通的PCF,计算开销较大。
效果图:
VSM
概述: Variance Shadow Mapping
是一种通过利用方差来改进传统阴影贴图的方法,与传统的阴影贴图不同,VSM使用每个光源的深度值的均值和方差来创建阴影,这样可以在计算中处理阴影的模糊化,同时避免了对深度贴图的多次采样
步骤:
- 存储深度和方差:在深度贴图中,不仅存储每个像素的深度值,还存储该深度值的方差,这样,光源照射到场景中的区域可以通过方差值来表示阴影的软化程度
- 第一通道存储深度值(z)
- 第二通道存储深度值的方差(\(\delta^2\))
- 深度比较:在片段着色器中,使用一个不等式来判断一个片段是否被阴影遮挡,由于存储了方差,阴影边缘会变得更平滑
算法实现:
- 需要两个纹理来存储对应点周围区域内的方差和均值
- 两趟pass可以把每个点的复杂度从\((2R+1)^2\)降到\((4R+2)\)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| float VSM(vec4 fragPosLightSpace,vec3 normal,vec3 lightDir,sampler2D d_d2_filter){ vec3 projCoords=fragPosLightSpace.xyz/fragPosLightSpace.w; projCoords=projCoords*.5+.5; if(projCoords.z>1.||projCoords.z<0.) return 0.; depth=projCoords.z; d_d2=texture(d_d2_filter,projCoords.xy).rg; float var=d_d2.y-d_d2.x*d_d2.x; float bias=max(.05*(1.-dot(normal,lightDir)),.005); float visibility; if(depth-bias<d_d2.x){ visibility=1.; } else{ float t_minus_mu=depth-d_d2.x; visibility=var/(var+t_minus_mu*t_minus_mu); } return 1.-visibility; }
|
特点:
VSM能提供相对较好的阴影效果,且具有较低的计算开销,但可能会面临一些噪声问题,特别是在低对比度区域
效果图:
与PCSS对比:
PCSS:
VSM:
CSM
概述: Cascaded shadow map(联级阴影贴图)
(待补充)
其他
参考: