前言
在前两篇文章中,我系统介绍了2d的光照,完善了全局光和sprite光,在这一篇中,我们要用到全局方向光的方向属性,来投射阴影,比较硬核,咱们徐徐道来。
阴影(Shadow)
在3D世界,太阳就是平行光,物体会在平行光的背面,按照平行光的方向投射出阴影,而在2D场景中,我们只能模拟这种现象,按照指定图片的轮廓,在指定的位置,以指定的方向,绘制在接收投射的地方,一般就是地面
原理
道理我们都懂,但具体怎么做呢,想想前面两篇文章,我们得到了一个叫光照图的东西,有了这个图,我们场景的所有物体都可以通过采样它来改变自己的颜色。阴影其实也一样,我们需要一张阴影图,把这个阴影图和光照图合并在一起,得到一张光影图,在这张图里,有阴影的地方是暗色调的,没有阴影的地方就是光照图的原色,然后注意了,我只希望地面接收阴影,所以这个光影图是留给地面采样的,而场景中其他物体,不需要接收阴影,所以它们还采样没有阴影的光照图。
阴影
所以第一步,就是绘制阴影。在本案例中,我们的树就是投射阴影的物体,首先,我在树的Sprite下挂了一个cocos自带的mesh–Quad,起名shadow,如下图
之所以用Quad,而不用Sprite,是考虑到drawcall问题,也就是合批,树的材质和阴影的材质不一样,这样就会打断Sprite的合批,树的sprite会走动态合批,而Quad,我会让它们走gpuInstancing合批。然后给shadow换个Layer,我这里起名SHADOW,如上图,同时,增加一个Camera,只照阴影,所以它对应的Visibility也是SHADOW。
下面重点来了,就是我们的阴影材质,而投影的核心算法就在这里,给我们的阴影一个shader
vs部分:
大致解释一下,_LightAngle是方向光的角度,也就是阴影投射的角度;_ShadowStartOffset是阴影的起始偏移,因为原点是中心点,我们必须偏移到物体脚底下显示才正确;_ShadowScale是阴影的尺寸,想想昼夜变化,早晚时阴影会比较长,中午了阴影会变短,这个尺寸就是干这个的。
顶点着色器的核心作用是把要投影的物体的每个顶点,根据投影起始坐标和投影方向,计算得到新的位置。而核心算法,就是已知投影平面(空间一点和法线构成平面方程),投影方向,求某点在这个投影平面按投影方向投影的点
所以是3d空间映射到了2d空间的图像。这个虚拟的投影平面,就是点和法线构成的,点就是树根的世界坐标,如上的planePoint,法线是自定义的向量,如上的planeNormal,而投影方向向量就是projDir,最后用每个顶点到planePoint的向量planeVec,根据投影方程得到新的世界坐标即可。
fs部分:
fs部分比较简单,其实就是要图的轮廓,毕竟阴影就是个纯黑嘛,所以我只关心alpha部分就行了,col.a就是轮廓了,而_ShadowIntensity是阴影的透明度。
材质部分如图,除了参数部分的设置,可以看到USE INSTANCING也勾选了,可以做到gpuInstancing合批
光影图
有了阴影,我们就要生成阴影光照图了。上面已经提到了,我们要给阴影一个独立的Camera,照那些Shadow的mesh,就自然得到了阴影图,然后通过自定义后处理,结合我们之前得到的光照图,最终输出到一张rt中,也就是光影图了。
先看看Camera的设置
如图,我把最终结果输出到light-shadow-rt中,这就是我的光影图,在自定义的后处理中,我要把之前得到的光照rt传进来
代码很简单
没啥好解释的,就是把Camera的阴影图传给shader的_ShadowPPRt这个变量中
shader也很简单,采样阴影图_ShadowPPRt,在传进来的光照图_lightRt上,有阴影的地方做颜色的衰减而已,用的就是之前阴影shader的alpha值,只是要用1减去它,所以阴影越浓,光照色越暗
输出光影图
到了这一步,该得到的我们都已经得到了,场景物体对光影图的采样与第一篇无异,只是注意,地面的光影图要用我们这里新生成的光影图,而场景物体,比如树木篝火等,要采样之前的光照图,这样不会被阴影遮住。效果就如开篇所示了。
昼夜变换
题外话,之前光照我们做了昼夜变化,阴影自然也不能落下,前面我们提供了阴影的角度,尺寸,强度,就是干这个用的,想象一下,太阳东升西落,阴影除了角度变化,阴影的长短,浓淡都在变化。参数都有了,做个脚本支持一下而已。
在第一篇里面,我们做了个简单的昼夜编辑器,模拟阳光颜色的变化,现在我们再把上面的参数也都加进去。
startSunAngle和endSunAngle是方向光的起始角度和结束角度,也就是阴影的角度,在update中根据一天的时间在这两个角度中做插值,shadowIntensity是阴影强度,用了cocos的curve,也就是曲线编辑器,但吐槽一下,这东西感觉不太好用,不如unity好使,就是做了个曲线,模拟阴影强度的变化,早晚是0,中午达到最大。shadowScale是尺寸,也用曲线模拟,跟强度相反,早晚最长,中午最短。编辑器如下
最终上个动画效果
打完收工!!!