Shadow mapping 3

先来复习一下ShadowMapping的原理.
首先以灯光的位置渲染场景,这里可以将灯光位置理解为场景的另一部相机.
在开启RTT(渲染到纹理) 的时候,开启深度测试,将深度信息纹理中.成为一张”深度图”
然后我们以正常的相机再渲染一次场景,在渲染的时候顶点着色器依然用正常的相机的投影矩阵输出,但是要把灯光试图投影矩阵转换后的顶点交给像素着色器.
在像素着色器中,将顶点和深度图的像素对齐,对比他们的深度值. 如果当前顶点的深度值大于深度图中的深度值,说明这个顶点肯定被小于它的某个顶点给挡住了,那么该顶点周围的像素都在阴影中. 否则就是在阳光中.

上一篇说到了制作深度图.
把颜色存进32位颜色中
通过
“mul ft0 ft0 fc1n”+
fc1 = 1,255,255*255,255*255*255

嘛~觉得这有点麻烦,毕竟解压的时候还需要将 RGB 反向解码.

换一种方式来搞深度图.

Vertex Program

                //将顶点转入灯光投影空间mvp
                "m44 vt0 va0 vc0n"+
                //输出顶点
                "mov op vt0n"+
                //传入像素着色器
                "mov v0 vt0n");

Fragment Program

                "mov ft0.xyz, v0.zzzn"+
                "mov ft0.w fc0.yn"+
                "div ft0.xyz, ft0.xyz, fc0.xn"+
                "mov oc ft0n");

ft0 即 rgb. 我们将 v0.zzz 赋值给 rgb 既是将深度值存进了rgb中
fc0.y = 1 将 ft0.w = 1 保持像素的透明度总是1
fc0.x = zFar div ft0.xyz ft0.xyz fc0.x 将深度值缩放到投影矩阵的 zFar 之内.
最后将像素输出.

输出后我们会得到如下的深度图. 由于是 0~255的灰度图,嘛~比较不明显,要仔细看
depthMap


之后我们要正常的渲染一次场景,同时将深度图作为参考图传入着色器
正常渲染的顶点着色器
Vertex Program

                //投影到场景相机
                "m44 vt0 va0 vc0n"+
                //投影到场景灯光相机
                "m44 vt1 va0 vc4n"+
                //投影到 modelToWorld
                "m44 vt2 va0 vc20n"+
                //投影到 lightViewToProj
                "m44 vt3 vt2 vc12n"+
               
                "mov v0 vt0n"+
                "mov v1 vt1n"+
                "mov v2 vt3n"+
               
                //uv
                "mov v3 va2n"+
                //worldPos
                "mov v4 vt2n"+
                "mov op vt0n");

这里我们需要3个矩阵.
cameraModelViewProj 矩阵, 用来正常输出模型位置的矩阵
ModelToWorld矩阵, 用来计算在世界空间中,顶点和灯光距离的矩阵
LightViewToProj, 将顶点转换到灯光投影空间计算深度值的矩阵

正常渲染的像素着色器
Fragmeng Program

                //纹理采样
                "tex ft1 v3 fs1<2d,wrap,nearst>n"+
               
                //将坐标转换到其次坐标
                "div ft2.xy v2.xy v2.zzn"+
               
                //将坐标转换到纹理UV fc2.y = 0.5
                // 0.5 * xy + 0.5;
               
                "mul ft2.xy ft2.xy fc2.yn"+
                "add ft2.xy ft2.xy fc2.yn"+
               
                "neg ft2.y ft2.yn"+
               
                //阴影图深度采样
                "tex ft3 ft2.xy fs0<2d,wrap,nearst>n"+
                "mul ft5.z ft3.z fc2.xn"+
                "mul ft5.z ft5.z fc2.wn"+
                "sge ft5.w ft5.z v2.zn"+
                "add ft5.w ft5.w fc2.yn" +
                "mul ft1 ft1 ft5.wn"+
                "mov oc ft1n");

这里涉及到一个知识点 从第二行 到 第六行
我们需要将三维空间转换到设备空间坐标下[-1,1]坐标下
只需要将顶点.xyzw/w 即可.
之后需要将转换到纹理空间下
将顶点.xy * 0.5 + 0.5 后,翻转y轴即可.
翻转y轴的原因是 在uv中,y轴向下为正.而在设备空间中,y轴向上为正.

阴影图采样完毕后, 我们需要还原深度值(在深度图的时候,我们将 z/zFar) 这里我们要还原 rgb * zFar = z; fc2.x = zFar
fc2.w 是一个可调节的距离值,用来放置z-Fighting的产生.

之后就是判断深度了. v2 是转换到灯光视图投影空间的顶点
ft5.w = 深度图.z >= v2.z ? 1 : 0; 如果v2深度小于深度图的深度,则表示在光照中
fc2.y 是投影的浓度. 在阴影中的时候 ft5.w = 0; ft5.w + 浓度 就是阴影中,阴影的浓度.
最后将阴影浓度和纹理混合
输出即可

最后是demo:
Demo is here: http://www.dreamfairy.cn/blog/work/flash/3d/stage3dshadowMapping/ShadowMapFinalTest_do.html

《Shadow mapping 3》有3个想法

    1. 报错的原因是git 不允许传obj格式的模型,所以某些类嵌入失败了.下次试试把obj改成md5然后用obj解析. 最新的ShadowMapFinalTest 还木有提交到github上面.

发表评论

电子邮件地址不会被公开。 必填项已用*标注