• Shadow Mapping 的原理与实践(二)


    3) 定义并生成Shadow Map纹理

     1 texture2D Lamp0ShadowMapColor : RENDERCOLORTARGET
     2 <
     3       float2 ViewPortRatio = {1.0,1.0};
     4       int MipLevels = 1;
     5       string Format = "A8R8G8B8" ;
     6 >;
     7 
     8 sampler2D Lamp0ShadowMapSampler = sampler_state {
     9       Texture = <Lamp0ShadowMapColor>;
    10       FILTER = MIN_MAG_MIP_LINEAR;
    11       AddressU = Clamp;
    12       AddressV = Clamp;
    13 };

        第3行的作用是使生成的Shadaow Map纹理大小与渲染窗口自动保持一致,这样可以很方便地观察到Shadow Map纹理大小改变时,对最终生成的阴影效果的影响。 

     1 float4x4 matWorld : World;
     2 float4x4 matView : View;
     3 float4x4 matProject : Projection;
     4 
     5 struct SourceData
     6 {
     7     float3 pos3 : POSITION;
     8     float4 n : NORMAL;
     9 };
    10 
    11 struct VertexOutput
    12 {
    13     float4 pos4 : POSITION;
    14 
    15     float4 rpos4 : TEXCOORD3;
    16     float4 n : NORMAL;
    17     
    18     float4 lpos4 : TEXCOORD2;
    19     float4 ldirt4 : TEXCOORD6;
    20     float4 uvd : TEXCOORD5;
    21 };
    22 
    23 static float4x4 matLightView = LightViewMat(Lamp0Point, Lamp0LookAt); 
    24 static float4x4 matLightProj = LightProjcetMat();
    25 
    26 VertexOutput makeShadowVS(SourceData vData)
    27 {
    28     VertexOutput vOut = (VertexOutput)0;
    29     
    30     float4x4 matTmp = mul(matWorld, matLightView);
    31     matTmp = mul(matTmp, matLightProj);
    32     
    33     
    34     float4 coordCVV = mul(float4(vData.pos3.xyz, 1.0f), matTmp);
    35     
    36     float4 m = 1/coordCVV.w;
    37 
    38     vOut.pos4.xyz = m*coordCVV.xyz;
    39     vOut.pos4.w = 1.0f;
    40     
    41     vOut.lpos4 = vOut.pos4;
    42     vOut.lpos4.z *= fat;
    43     
    44     return vOut;
    45 }
    46 
    47 float4 makeShadowPS(VertexOutput In) : COLOR 
    48 {    
    49     return float4(In.lpos4.z, 0, 0, 1);
    50 }

           在生成纹理时,将Z-Buffer Test 设为Enable状态,这样就可以保证纹理中保存的深度值始终是离光源最近的那个点的。另外,可以修改上段代码第5行的纹理像素格式,就能方便地得到更精确的深度值。

    4) 使用Shadow Map纹理生成阴影

          以图一为例,直观来看,生成阴影前应该先将相应观察平面S上的像素对应的空间点(如b'对应的b)的位置计算出来,再用之前生成的Light Space的matLightView和matLightProj把点b投射到平面H上。这样就需要进行从b'到b的变换,很显然观察窗口S的透视矩阵的逆矩阵是存在的。但实际上还有更简易的做法:

     1 VertexOutput useShadowVS(SourceData vData)
     2 {
     3     VertexOutput v = (VertexOutput)0;
     4     v.pos4 = mul(float4(vData.pos3, 1.0f), matWorldViewProj);
     5     
     6 
     7     v.n = mul(float4(vData.n.xyz, 0.0f), matWorld);
     8     v.n = normalize(v.n);
     9     v.rpos4 = mul(float4(vData.pos3, 1.0f), matWorld);
    10         
    11     float3 vLightDirect = Lamp0Point - v.rpos4.xyz;
    12     vLightDirect = normalize(vLightDirect);
    13     v.ldirt4 = float4(vLightDirect, 0.0f);
    14     
    15     float4x4 matTmp = mul(matWorld, matLightView);
    16     matTmp = mul(matTmp, matLightProj);
    17     
    18     float4 lightCVV = mul(float4(vData.pos3, 1.0f), matTmp);
    19     lightCVV.z -= 0.1f;
    20     
    21     float m = 1/lightCVV.w;
    22     lightCVV.xyz = m*lightCVV.xyz;
    23     lightCVV.w = 1.0f;
    24     
    25     v.lpos4 = lightCVV;
    26     
    27     
    28     float2 uv = (float2)0;
    29     uv.x = (1.0f+v.lpos4.x)/2.0f;
    30     uv.y = (1.0f-v.lpos4.y)/2.0f;
    31     v.uvd.xy = uv;
    32     v.uvd.z = v.lpos4.z;
    33     
    34     return v;
    35 }
    36 
    37 
    38 float4 useShadowPS(VertexOutput v) : COLOR
    39 {
    40 
    41     float2 uv = v.uvd.xy;
    42     float dep = v.uvd.z;
    43     
    44     float3 samplerCol = (float3)0;
    45     float c = -1;
    46     float tmpLm = 0.0f;
    47     
    48     float3 sdp = tex2D(Lamp0ShadowMapSampler, uv).rgb;    
    49     if( dep < sdp.x )
    50     {
    51         tmpLm = 1.0f;
    52     }
    53     float fall = 1.0/dot(v.ldirt4.xyz, v.ldirt4.xyz);
    54     
    55     float3 ld = v.ldirt4.xyz;
    56     float3 n = v.n;
    57     float diffuse = dot(ld, n);
    58     float3 col = float3(1,1,1);
    59     float linf = 0.8f;
    60     //col = diffuse * col;
    61     
    62     tmpLm = (tmpLm)*diffuse*fall*linf;
    63     col = tmpLm * col;
    64 
    65     return float4(col, 1);
    66 }

           第15到第32行,直接计算出每一个顶点在Light Space投影平面上的点的x、y、 z坐标值;在进入到观察者投影变换时,可见像素的x、y、z坐标就可以据此通过插值得到。这样做好处是,避免计算透视变换的逆运算,能使代码更简洁,不足之处是增加了大量多余的运算。        

           第19行,对lightCVV的z值做了一个偏移运算,作用是校正浮点运算可能出现的误差。以图一中的点b为例,由于基于浮点数的空间变换运算会出现计算误差,因此位于W表面上的b点经投影变换后,本应等于Z-Buffer中相应像素的深度值,有可能变得大于此值,从而导致其后的逻辑判断出错(第49行),所以需要对运算结果做一个误差校正。更一般的做法是将lightCVV乘以一个事先设置好的误差校正矩阵。

           第53行,计算光照强度衰减因子(与距离的平方成反比)。初始光照强度在第59行设定。

         

  • 相关阅读:
    02-自定义CALayer
    01-CALayer的基本操作
    抽屉效果
    手势识别
    事件响应
    寻找最合适的view
    hitTest方法与PointInside方法
    02-事件的产生与传递
    OC图标+启动图
    OC多线程操作
  • 原文地址:https://www.cnblogs.com/yzwalkman/p/3220651.html
Copyright © 2020-2023  润新知