学习UnityShader入门精要笔记3——更复杂的光照

这部分笔记对应书中第九章内容,内容涉及了很多unity底层为我们做好的事情的实现方法,譬如光的衰减,阴影实现以及渲染路径的理解。我对这部分理解还是不怎么深刻,这里还会以摘抄为主:)

先说渲染路径,渲染路径决定光照如何应用到shader中。渲染路径分为三种:前向渲染,延迟渲染和顶点渲染。U5以后舍弃了顶点渲染,替换了新的延迟渲染,但也提供对旧版本的兼容。开发过程中可统一设定渲染路径,也可以针对camera设置不同的渲染路径。设定好了渲染路径,才能保证接下来使用的一些光照变量被正确赋值。

前向渲染:每进行一次完整的前向渲染,会计算两个缓冲区的信息:颜色缓冲区和深度缓冲区。深度缓冲来决定一个片元是否会被渲染,如果是就更新颜色缓冲区的颜色值。一个物体在渲染处理光照过程中,每个pass通道都会执行一次。逐像素光源增多,物体增多,要处理的pass就会变多。

前向渲染有三种处理光照的方式:逐顶点处理,逐像素处理,球谐函数(SH)。我们可以手动设置light里的选项卡告诉unity这个light是否是important,是则会视作逐像素光源处理。Unity自己也会有一套规则来对光源进行排序,规则如下:

1、场景中最亮的平行光总是按照逐像素处理

2、渲染模式设置为not important的光源会按逐顶点或SH处理

3、渲染模式被设置为Important的光源会按照逐像素处理

4、若按以上规则得到的逐像素光源数量小于Quality Setting中的逐像素光源数量则会有更多的光源按照逐像素的方式渲染

延迟渲染:照前边说的,当场景包含大量实时光源时,由于要执行的pass很多,前向渲染的性能会急速下降。延迟渲染比前向渲染更古老,不过由于上边所说的问题又流行起来。在延迟渲染中会用到三个缓冲区,除了颜色缓冲区和深度缓冲区,还用到了一个额外的缓冲区被称为G缓冲(G-buffer)。延迟渲染主要包括两个pass。在第一个pass中通过深度缓冲来计算需要被渲染的片元,并将其相关信息存储到G缓冲区,此pass只会执行一次。之后,再在第二个pass中利用G-buffer中各片元的信息进行真正的光照计算。可见延迟渲染使用的pass数目和场景中包含的光源数目无关,其效率不依赖于场景复杂度。

延迟渲染最适合场景中光源数目多的情况使用,并且每个光源都可以按逐像素的方式处理。其缺点在于:

1、不支持真正的抗锯齿功能

2、不能处理半透明物体

3、对显卡有要求


Unity默认使用一张纹理作为查找表来在片元着色器中计算逐像素光照的衰减。纹理名为_LightTexture0。_LightMatrix0可以把顶点从世界空间变换到光源空间因此可依此计算光源空间顶点坐标位置:

float3 lightCoord = mul(_LightMatrix0,float4(i.worldPosition,1)).xyz;

然后,使用这个坐标的模的平方对衰减纹理进行采样得到衰减值:

fixed atten = tex2D(_LightTexture0,dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;

上述代码使用光源空间顶点距离的平方来对纹理采样,然后使用宏UNITY_ATTEN_CHANNEL来得到衰减纹理中衰减值所在的分量以得到最终衰减值。


阴影

实时渲染中常使用Shadow Map技术。他会把camera放在和光源重合的位置上,则场景中该光源的阴影区就是camera看不到地方。在前向渲染中如果场景中最重要的平行光开启了阴影,Unity会为该光源计算他的阴影影射纹理。这个纹理本质也是一张深度图,记录了从该光源的位置出发、能看到的场景中距离它最近的表面位置、

Unity里会使用一个额外的LightMode被设置为ShadowCaster的Pass来更新光源的阴影影射纹理。传统的阴影映射纹理实现中,会在正常渲染的pass中把顶点位置变换到光源空间下,得到她在光源空间中的三维位置信息。然后使用xy分量对阴影影射纹理采样,得到其深度信息。若该深度值小于该顶点的深度值则说明该点位于阴影中。但是在U5中,Unity使用了另一种阴影采样技术——屏幕空间的阴影映射技术。但是有的移动平台不支持此特性。

当使用了屏幕空间的阴影映射技术,Unity首先会调用LightMode为ShadowCaster的Pass来得到可投射阴影的光源的阴影映射纹理以及摄像机的深度纹理。然后根据光源的阴影映射纹理和摄像机的深度纹理来得到屏幕空间的阴影图。若摄像机的深度图中记录的表面深度大鱼转换到阴影映射纹理中的深度值就说明该表面虽然是可见的,但是处于该光源的阴影中。因此吧物体表面坐标从模型空间转换到屏幕空间,然后使用这个坐标对阴影图采样即可。

做阴影投射时,通常使用Fallback,去寻找LightMode为ShadowCaster的Pass。

阴影接收时,需要用到三个内置宏:SHADOW_COORDS、TRANSFER_SHADOW、SHADOW_ATTENUATION。这几个宏里unity为了处理不同光源类型不同平台做了大量处理。

struct a2v
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;

				SHADOW_COORDS(2)
			};

			
			v2f vert (a2v v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;

				TRANSFER_SHADOW(o);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLightDir));
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
				fixed3 halfDir = normalize(worldLightDir+viewDir);
				fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);

				fixed atten = 1.0;

				fixed shadow = SHADOW_ATTENUATION(i);

				return fixed4(ambient+(diffuse+specular)*atten*shadow,1.0);
			}

使用内置宏同时处理阴影和光照衰减:

struct a2v
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			struct v2f
			{
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
				SHADOW_COORDS(2)
			};

			
			v2f vert (a2v v)
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;

				TRANSFER_SHADOW(o);
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));

				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				fixed3 diffuse = _LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLightDir));
				fixed viewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
				fixed3 halfDir = normalize(worldLightDir+viewDir);
				fixed3 specular = _LightColor0.rgb *_Specular.rgb*pow(max(0,dot(worldNormal,halfDir)),_Gloss);

				UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);

				return fixed4(ambient+(diffuse+specular)*atten,1.0);
			}


透明度测试物体的阴影

透明度测试会在片元着色器舍弃某些片元,而fallback中并没有这样的操作,故可能得到错误的阴影效果。如果想得到经过透明度测试后的阴影效果,需要提供一个有透明度测试功能的ShadowCaster Pass。可采用内置的unity shader来节省代码量:FallBack "Transparent/Cutout/VertexLit"


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值