shader入门12 多光源与光照衰减

渲染路径

渲染路径的作用是准备光照相关的属性,如果没有指定渲染路径,相关的属性例如_LightColor0,_WorldSpaceLightPos0将无法正常使用.
目前unity中主要使用前向渲染路径,延迟渲染路径,两种渲染路径. 本篇中主要使用前向渲染路径.

在pass中使用了标签Tags{ “LightMode” = “ForwardBase” }指定前向渲染路径.
另外还有一种标签Tags{ “LightMode” = “ForwardAdd” }也用来指定前向渲染路径
目前已知的区别是ForwardBase有阴影效果,ForwardAdd没有,其他区别暂时不清楚.

Tags{ "LightMode" = "ForwardBase" }
Tags{ "LightMode" = "ForwardAdd" }

多光源代码

我们以高光反射为例,写一个多光源的shader.

Shader "Unity/Custom/NewSurfaceShader" {
	Properties{
		_Diffuse("Diffuse", Color) = (1, 1, 1, 1)//漫反射颜色
		_Specular("Specular", Color) = (1, 1, 1, 1)//高光反射颜色
		_Gloss("Gloss", Range(8.0, 256)) = 20//光泽度
	}
	SubShader{
		Pass{
			Tags{ "LightMode" = "ForwardBase" }
			CGPROGRAM
			#pragma multi_compile_fwdbase//在forwardBase中获取正确的光照变量
			#pragma vertex vert//声明顶点处理方法
			#pragma fragment frag//声明片段处理方法
			#include "Lighting.cginc"//调用光照函数库
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			struct a2v {
				float4 vertex : POSITION;//顶点的模型空间位置
				float3 normal : NORMAL;//模型空间法线
			};
			struct v2f {
				float4 pos : SV_POSITION;//输出给剪裁空间的顶点坐标
				float3 worldNormal : TEXCOORD0;//世界坐标系法线
				float3 worldPos : TEXCOORD1;//世界坐标系坐标
			};
			v2f vert(a2v v) {
				v2f f;
				f.pos = UnityObjectToClipPos(v.vertex);//模型空间到剪裁空间转换
				f.worldNormal = UnityObjectToWorldNormal(v.normal);//模型空间到世界坐标系法线
				f.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;//模型空间到世界坐标系位置
				return f;
			}
			fixed4 frag(v2f f) : SV_Target{
				fixed3 worldNormal = normalize(f.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 - f.worldPos.xyz);//朝向相机的方向
				fixed3 halfDir = normalize(worldLightDir + viewDir);//h方向 用于计算高光反射补偿度
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);//高光反射
				fixed atten = 1.0;//光照衰减 在这个pass中是平行光 没有衰减
				return fixed4(ambient + (diffuse + specular) * atten, 1.0);
			}
			ENDCG
		}
		Pass{
			Tags{ "LightMode" = "ForwardAdd" }
			Blend One One//开启混合 我们希望forwardAdd增加额外的光照效果,并不是覆盖原效果
			CGPROGRAM
			#pragma multi_compile_fwdadd//用于在forwardAdd中获取正确的光照变量
			#pragma vertex vert
			#pragma fragment frag
			#include "Lighting.cginc"
			#include "AutoLight.cginc"
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			struct a2v {
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			struct v2f {
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};
			v2f vert(a2v v) {
				v2f f;
				f.pos = UnityObjectToClipPos(v.vertex);
				f.worldNormal = UnityObjectToWorldNormal(v.normal);
				f.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				return f;
			}
			fixed4 frag(v2f f) : SV_Target{
				fixed3 worldNormal = normalize(f.worldNormal);
				#ifdef USING_DIRECTIONAL_LIGHT//根据是否使用平行光 决定光照的方向
					fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				#else
					fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz - f.worldPos.xyz);
				#endif
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - f.worldPos.xyz);
				fixed3 halfDir = normalize(worldLightDir + viewDir);
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				#ifdef USING_DIRECTIONAL_LIGHT//根据光源 计算光照衰减
					fixed atten = 1.0;
				#else
					#if defined (POINT)
						float3 lightCoord = mul(unity_WorldToLight, float4(f.worldPos, 1)).xyz;
						fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
					#elif defined (SPOT)
						float4 lightCoord = mul(unity_WorldToLight, float4(f.worldPos, 1));
						fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
					#else
						fixed atten = 1.0;
					#endif
				#endif
				return fixed4((diffuse + specular) * atten, 1.0);
			}
			ENDCG
		}
	}
	FallBack "Diffuse"
}

效果

在这里插入图片描述

光照衰减

在上述代码中,光照衰减的计算过程如下.
我们发现光照衰减的计算量太大了

#ifdef USING_DIRECTIONAL_LIGHT//根据光源 计算光照衰减
	fixed atten = 1.0;
#else
	#if defined (POINT)
		float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
		fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
	#elif defined (SPOT)
		float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
		fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
	#else
		fixed atten = 1.0;
	#endif
#endif

我们有两种方式优化光照衰减,
1.简化数学公式
缺点是效果不太好,偶尔有bug

float distance = length(_WorldSpaceLightPos0.xyz - v.worldPosition.xyz);//计算光源距离
atten=1/distance;//计算衰减

2.使用一张纹理计算光照衰减
Unity默认的方式,缺点是精度低,不直观

float3 lightcoord=mul(_LightMatrix0,float4 (v.worldPosition,1)).xyz;//将坐标转换到光源空间
fixed atten=tex2D(_LightTex,dot(lightcoord,lightcoord).rr).UNITY_ATTEN_CHANNEL;//用坐标模的平方对纹理进行采样,得到衰减值
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值