必威体育Betway必威体育官网
当前位置:首页 > IT技术

兰伯特(Lambert)模型

时间:2019-10-12 07:43:24来源:IT技术作者:seo实验室小编阅读:59次「手机版」
 

兰伯特

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

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

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

                            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最初也是被用于游戏半条命的画面渲染,为了防止某个物体的背光面丢失形状并且显得太过平面化。这个技术是完全没有基于任何物理原理的,而仅仅是一种感性的视觉增强

相关阅读

【Unity Shader】 Lambert(兰伯特)光照模型

Unity Shader Lambert光照模型与漫反射 一、漫反射简介 漫反射的特点 二、漫反射光照模型-Lambert(兰伯特)光照模型 Lambert 余弦

分享到:

栏目导航

推荐阅读

热门阅读