Unity游戏图形渲染效果系列之阴影篇(二)

继上一章从阴影产生的原理去讲解和实现阴影效果,本章将继续优化阴影效果和实现抗锯齿,以及阴影的多种产生方式及其应用场合,下面进入正题:


一.高精度锯齿优化

该方法实现简单,但是效果也是最好的一种,不过消耗非常大,所以适合的场合是精度要求较高的模型 比如主角渲染等,不适合全场景渲染。该方法下Shader代码基本不需要改什么,只需要提高精度就够了,其一是提高存储阴影相机深度图的分辨率,其二是减少阴影相机的渲染范围,也就是花最大的贴图显示出最小的场景,这样精度自然是最高的。

如下图,显示的是阴影相机渲染的场景阴影范围的深度法线图:




图一是用阴影相机渲染了场景20米范围的所有物体 把他渲染到一张512大小的rendertexture上面,图二是用阴影相机渲染了场景5米范围的物体,rendertexture精度达到2048。那么实际的阴影渲染效果要从下面的图可以看出:

如下图一所示,锯齿感非常强烈,是因为使用了低精度的rendertexture并且渲染范围很大,再看如图二精度大幅度提高后,锯齿感已经非常小了,原则上来说,根据显示要求 不断提高精度 可以得到更高的阴影效果。






可以开看到几乎不需要任何算法的情况下,就使阴影效果得到了很大的提升。不过在实际应用中,用这种方式提高精度消耗比较大,特别是实时渲染,一般应用于离线渲染或者实时渲染局部模型,比如对主角细节阴影要求很高的情况下,可以尽可能的让阴影相机渲染范围只包含主角那么大,然后rendertexture精度尽可能提高,不过考虑实时渲染的消耗,还是要选一个折中大小比较合适。然后下图是用高精度渲染模式得到的实时阴影效果。




二.常规抗阴影效果

通常在实时渲染过程中,根本没有那么高的配置去实现高精度渲染,特别是在手机游戏上面,通常情况下的实时阴影渲染都是采用上面图一的方式,阴影相机一次性渲染场景范围几十米的范围,而且为了性能,贴图精度可能尽可能的低,这就导致了阴影锯齿感必然非常严重,那么这种普通的实时阴影就只能通过算法去抗锯齿了。

从上一篇实现阴影的教程中,我们可以看出标准情况下只对阴影进行了一次采样进行深度对比 然后计算产生阴影,再实际应用中,我们可能会对阴影进行分级,比如:

1级:基础阴影,可能只对深度贴图采样一次参与计算,性能高,但是效果很差。

2级:中级阴影,对深度贴图进行2-4次采样参与计算,通过在y方向lerp的方式,会对阴影效果有一点点软化效果。

3级:高级阴影或者叫软阴影,对深度贴图进行9次以上的采样和lerp,效果可以达到很好的效果了。

如下三张图,分别对应了我们实现的不同阴影效果:



从上面的三张对比图就可以看出阴影分级的效果,效果越好消耗自然也会越大,在实际应用中可以根据实际情况去开启对应的等级,下面我们来简单讲下实现过程,其实主要集中讲解的是关于阴影系数的求解方法:

       在第一章的文章中,我们实现了最简单的阴影系数的求解方法,如下:

 float4 col = tex2D(_ShadowDepth, depuv);
		float olddepth = DecodeDepth(col.xy);//0-1
		float newdepth = i.shadowvpos.z  / invShadowViewport.w;//farclip
		float occ=newdepth-olddepth;
 return occ;//其中的ooc就是阴影系数,要么是0,要么是1,就把阴影和非阴影的区域分开了。
 

 
在实际应用中,可能会对阴影进行多次采样,如下:采样的方式可以多种多样,对周围按一定规律去多次采样就好了,主要是插值问题,插值我们采用对uv坐标对应的世界坐标的小数部分进行一个x方向插值后,再进行另一个y方向二次插值,下面的代码仅供一种参考,具体还得自己实现。 

		float2 offsetuv[10]={float2(-1,-1),float2(-1,0),float2(0,1),float2(1,1),float2(1,-1)...};
 //这里分别对应坐标的周围一圈的点,也可以取上下左右四个点,或者周围其他点。
		float occ=0;
		float Num=9;
		for (int i = 0; i < Num; i++)//这里的Num可以根据自己调整可以是4 也可以是9,更好的效果 16或者20都行。
		{
			float2 temp = uv+offsetuv[i]*0.01f;
			float2 temp2 = uv+offsetuv[i+1]*0.02f;
			float4 col = tex2D(_ShadowDepth, temp);
			float4 col2 = tex2D(_ShadowDepth, temp2);
			float olddepth = DecodeDepth(col.xy);
			float olddepth2 = DecodeDepth(col2.xy);
			float dot_val.x=olddepth-olddepth2>0;
			.
			.
			.
			float2 lerp_val = frac((uv+invVP.xy)*1024.0f);
			float2 temp = lerp(dot_val.xy, dot_val.zw, lerp_val.y);
			occ+= lerp(temp.x, temp.y, lerp_val.x);
		}

		return occ/Num;



三.阴影的多种生成方式

阴影的生成方式有很多种,在常规的前向渲染下,阴影生成和光照计算放在一块,也就是对光照计算的同时乘以计算出来的阴影系数来得到结果,一般没事建议就用这种好了。另外还有两种阴影的生成方式大家可以了解下,通常用在屏幕后处理阶段去处理,其一是采用DrawMesh的方式实时去绘制阴影,对于屏幕后处理效果较多的情况下,可以采用这种实时绘制模式。另外一种方式是通过BlendOneOne的方式去绘制阴影,通常情况下阴影系数乘在光照里边然后把暗处进行缩放,得到阴影效果,不过我们也可以用Blend方式去加强亮出的效果,也就是亮处部分进行叠加变亮,但是阴影部分只计算一次 显示就会暗一点,这种能得到更好的场景阴影叠加效果。更好的效果也必然会导致性能稍微第一点,然后光照参数稍微难控制一点,大家根据各自场景的特征去选取效果就好了。


好了,今天就到此为止了,下章考虑讲解SSAO或者SSS的相关实现,- -也有可能讲其他的...反正你又管不了我!

相关文章
相关标签/搜索