在计算机视觉中实现三维物体阴影比较出名的一种方案是ShadowMapping,它的基本思路如下:
第一步:
1. 创建camera:创建一个位于光源位置的camera,叫做lightCamera。
2. 创建shadowMap:创建一个有深度纹理的FBO,这个FBO只需要存储深度纹理,不需要颜色缓存区。深度纹理的长宽我们可以根据不同的应用场景自定义,不过在大多数情况下都是和viewPort的长宽一致。
3. 渲染场景深度到shadowMap: 将上一步创建的FBO设置为当前的FrameBuffer,然后从lightCamera渲染场景。
第二步:
1. 创建camera: 创建一个用于正常场景渲染camera,叫做normalCamera。
2. 正常渲染场景:从normalCamera渲染场景,并且把shadowMap传递到fragment shader。
3. 顶点转换:顶点坐标在vertext shader需要做两次转换:
(1): 使用正常渲染场景中的MVP矩阵对顶点做一次转换,然后将新的顶点通过gl_position输出。
(2): 使用光源位置渲染场景的MVP矩阵对顶点做一次转换,将这个新的顶点命名为lightCoordinate,然后将lightCoordinate通过varying输出。
4. 将lightCoordinate转换为纹理坐标:在fragment shader中lightCoordinate的坐标范围为[-1, 1],但是纹理坐标的范围在[0,1]之间。把lightCoordinate坐标转换为纹理坐标空间需要用一个特别的矩阵乘以lightCoordinate,然后得到新的[x,y,z,w],将x,y视为纹理坐标。这个特别的矩阵如下:
0.5,0.0,0.0,0.0,
0.0,0.5,0.0,0.0,
0.0,0.0,0.5,0.0,
0.0,0.0,0.0,1.0
5. 深度比较:根据上一步得到的纹理坐标从shadowMap中取得深度值shadowZ,把shadowZ和lightCoordinate的Z值做比较,如果shadowZ比较小,就可以认为当前的位置处于阴影中。
以上只是原理介绍,在我的github中有专门的demo实现,地址如下:shadowMapping,有兴趣的同学可以参考下,希望对你有所帮助。