兰伯特(Lambert)模型

漫反射,是投射在粗糙表面上的光向各个方向反射的现象。当一束平行的入射光线射到粗糙的表面时,表面会把光线向着四面八方反射,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同的方向无规则地反射,这种反射称之为“漫反射”或“漫射”。这种反射的光称为漫射光。很多物体,如植物、墙壁、衣服等,其表面粗看起来似乎是平滑,但用放大镜仔细观察,就会看到其表面是凹凸不平的,所以本来是平行的太阳光被这些表面反射后,弥漫地射向不同方向。


(这图盗的别人的,直观,非原创)

漫反射光照符合兰伯特定律:反射光线的强度与表面法线和光源方向之间的夹角的余弦成正比,因此漫反射的部分计算如下:

                                    Cdiffuse =( Clight *Mdiffuse )max(0,dot(N,L))

N是表面法线,L是光源方向的单位矢量,Mdiffuse是材质的漫反射颜色,Clight是光源颜色我们使用取最大值函数来将其截取到0,还可以防止物体被从后面来的光源照亮。

逐顶点计算着色shader
我们在shader中需要计算输出的颜色,逐顶点着色也就是说我们的计算主要放在了vertex shader中,根据顶点来计算,每个顶点中计算出了该点的颜色,直接作为vertex shader的输出,pixel(fragment) shader的输入,当到达pixel阶段时,直接输出顶点shader的结果。比如一个三角形面片,在vertex阶段,分别计算了每个顶点的颜色值,在pixel阶段时,这个面片经过投影,最终显示在屏幕上的像素,会根据该像素周围的顶点来插值计算像素的最终颜色,这种着色方式也叫做 高洛德着色

Shader "ApcShader/DiffusePerVetex"
{
	//属性  
	Properties{
		_Diffuse("Diffuse", Color) = (1,1,1,1)
	}

		//子着色器    
		SubShader
	{
		Pass
	{
		//定义Tags  
		Tags{ "RenderType" = "Opaque" }

		CGPROGRAM
		//引入头文件 ,一些内置变量,如_LightColor0,需要使用到
#include "Lighting.cginc"  
		//定义Properties中的变量  
		fixed4 _Diffuse;
	//使用vert函数和frag函数  
#pragma vertex vert  
#pragma fragment frag  
	//定义结构体:应用阶段到vertex shader阶段的数据,如果定义了 
	struct a2v
	{
		float4 vertex : POSITION;
		float3 normal : NORMAL;
	};
	//定义结构体:vertex shader阶段输出的内容  
	struct v2f
	{
		float4 pos : SV_POSITION;
		fixed4 color : COLOR;
	};

	//定义顶点shader  
	v2f vert(a2v v)
	{
		v2f o;
		//模型顶点转换
		o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
		//环境光
		fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz; 
		//把法线转化到世界空间  
		float3 worldNormal = mul(v.normal, (float3x3)_World2Object);
		//归一化法线  
		worldNormal = normalize(worldNormal);
		//把光照方向归一化  
		fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
		//根据兰伯特模型计算顶点的光照信息,dot可能有负值,小于0的部分可以理解为看不见,直接取0 ,lambert光强*材质diffuse颜色*光颜色 
		fixed3 diffuse = _LightColor0.xyz*_Diffuse.xyz*max(0.0, dot(worldNormal, worldLightDir));		 
		o.color = fixed4(diffuse + ambient, 1.0);
		return o;
	}

	//定义片元shader  
	fixed4 frag(v2f i) : SV_Target
	{
		return i.color;
	}
		ENDCG
	}

	}
		//前面的SubShader失效的话,使用默认的Diffuse  
		FallBack "Diffuse"
}

逐像素计算着色shader


逐像素计算时,我们的主要计算放到了pixel shader里,在vertex shader阶段只是进行了基本的顶点变换操作,以及顶点的法线转化到世界空间的操作,然后将转化后的法线作为参数传递给pixel shader。其他的计算都放到了pixel shader阶段,这样,针对每个像素,我们都可以来计算这个像素的光照情况,而不是像逐顶点计算时,先计算好顶点的颜色,然后差值得到中间的像素颜色。这种逐像素着色的方式也叫作Phone着色(非Phone光照模型)
Shader "Custom1/LambertFragment"
{
		//属性  
		Properties{
			_Diffuse("Diffuse", Color) = (1,1,1,1)
		}

			//子着色器    
			SubShader
		{
			Pass
		{
			//定义Tags  
			Tags{ "RenderType" = "Opaque" }

			CGPROGRAM
			//引入头文件  
#include "Lighting.cginc"  
			//定义Properties中的变量  
			fixed4 _Diffuse;
		//定义结构体:应用阶段到vertex shader阶段的数据  
		struct a2v
		{
			float4 vertex : POSITION;
			float3 normal : NORMAL;
		};
		//定义结构体:vertex shader阶段输出的内容  
		struct v2f
		{
			float4 pos : SV_POSITION;
			float3 worldNormal : TEXCOORD0;
		};

		//定义顶点shader  
		v2f vert(a2v v)
		{
			v2f o;
			o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
			//把法线转化到世界空间  
			o.worldNormal = mul(v.normal, (float3x3)_World2Object);
			return o;
		}

		//定义片元shader  
		fixed4 frag(v2f i) : SV_Target
		{
		fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
		//归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的  
		fixed3 worldNormal = normalize(i.worldNormal);
		//把光照方向归一化  
		fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
		//根据兰伯特模型计算像素的光照信息,小于0的部分理解为看不见,置为0  
		fixed3 lambert = max(0.0, dot(worldNormal, worldLightDir));
		//最终输出颜色为lambert光强*材质diffuse颜色*光颜色  
		fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz;
		return fixed4(diffuse+ambient, 1.0);
		}

			//使用vert函数和frag函数  
#pragma vertex vert  
#pragma fragment frag     

			ENDCG
		}

		}
			//前面的Shader失效的话,使用默认的Diffuse  
			FallBack "Diffuse"
}
半兰伯特模型

广义的半兰伯特模型公式如下:

                                 Cdiffuse =( Clight *Mdiffuse )*(阿鲁法dot(N,L)+贝塔)

可以看到与原兰伯特模型相比,半兰伯特没有使用max操作来防止点积为负,而是对结果进行了一个阿鲁法倍的缩放再加上一个贝塔大小的偏移。当然绝大多情况阿鲁法和贝塔的值都为0.5
Shader "Custom1/LambertFragment"
{
		//属性  
		Properties{
			_Diffuse("Diffuse", Color) = (1,1,1,1)
		}

			//子着色器    
			SubShader
		{
			Pass
		{
			//定义Tags  
			Tags{ "RenderType" = "Opaque" }

			CGPROGRAM
			//引入头文件  
#include "Lighting.cginc"  
			//定义Properties中的变量  
			fixed4 _Diffuse;
		//定义结构体:应用阶段到vertex shader阶段的数据  
		struct a2v
		{
			float4 vertex : POSITION;
			float3 normal : NORMAL;
		};
		//定义结构体:vertex shader阶段输出的内容  
		struct v2f
		{
			float4 pos : SV_POSITION;
			float3 worldNormal : TEXCOORD0;
		};

		//定义顶点shader  
		v2f vert(a2v v)
		{
			v2f o;
			o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
			//把法线转化到世界空间  
			o.worldNormal = mul(v.normal, (float3x3)_World2Object);
			return o;
		}

		//定义片元shader  
		fixed4 frag(v2f i) : SV_Target
		{
		fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
		//归一化法线,即使在vert归一化也不行,从vert到frag阶段有差值处理,传入的法线方向并不是vertex shader直接传出的  
		fixed3 worldNormal = normalize(i.worldNormal);
		//把光照方向归一化  
		fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
		//根据半兰伯特模型计算像素的光照信息
		fixed3 lambert = 0.5* dot(worldNormal, worldLightDir)+0.5;
		//最终输出颜色为lambert光强*材质diffuse颜色*光颜色  
		fixed3 diffuse = lambert * _Diffuse.xyz * _LightColor0.xyz;
		return fixed4(diffuse+ambient, 1.0);
		}

			//使用vert函数和frag函数  
#pragma vertex vert  
#pragma fragment frag     

			ENDCG
		}

		}
			//前面的Shader失效的话,使用默认的Diffuse  
			FallBack "Diffuse"
}

如图,左边是兰伯特,右边是半兰伯特。对比效果还是比较明显的

.Half Lambert最初是由Valve(游戏半条命2使用的引擎即是其开发的)提出来,用于提高物体在一些光线无法照射到的区域的亮度的。简单说来,它提高了漫反射光照的亮度,使得漫反射光线可以看起来照射到一个物体的各个表面。而Half Lambert最初也是被用于游戏半条命的画面渲染,为了防止某个物体的背光面丢失形状并且显得太过平面化。这个技术是完全没有基于任何物理原理的,而仅仅是一种感性的视觉增强



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值