Unity Shader-Phong光照模型与Specular

简介


学完了兰伯特光照模型,再来学习一个更加高级一点的光照模型-Phong光照模型。光除了漫反射,还有镜面反射。一些金属类型的材质,往往表现出一种高光效果,用兰伯特模型是模拟不出来的,所以就有了Phong模型。Phong模型主要有三部分构成,第一部分是上一篇中介绍了的Diffuse,也就是漫反射,第二部分是环境光,在非全局光照的情况下,我们一般是通过一个环境光来模拟物体的间接照明,这个值在shader中可以通过一个宏来直接获取,而第三部分Specular,也就是高光部分的计算,是一种模拟镜面反射的效果,也是本篇文章重点介绍的内容。


在现实世界中,粗糙的物体一般会是漫反射,而光滑的物体呈现得较多的就是镜面反射,最明显的现象就是光线照射的反射方向有一个亮斑。再来复习一下镜面反射的概念:当平行入射的光线射到这个反射面时,仍会平行地向一个方向反射出来,这种反射就属于镜面反射,其反射波的方向与反射平面的法线夹角(反射角),与入射波方向与该反射平面法线的夹角(入射角)相等,且入射波、反射波,及平面法线同处于一个平面内。反射光的亮度不仅与光线的入射角有关,还与观察者视线和物体表面之间的角度有关。镜面反射通常会造成物体表面上的“闪烁”和“高光”现象,镜面反射的强度也与物体的材质有关,无光泽的木材很少会有镜面反射发生,而高光泽的金属则会有大量镜面反射。


Phong光照模型


Phong光照模型中主要的部分就是对高光的计算,首先来看下面这张图片:

理想情况下,光源射出的光线,通过镜面反射,正好在反射光方向观察,观察者可以接受到的反射光最多,那么观察者与反射方向之间的夹角就决定了能够观察到高光的多少。夹角越大,高光越小,夹角越小,高光越大。而另一个影响高光大小的因素是表面的光滑程度,表面越光滑,高光越强,表面月粗糙,高光越弱。L代表光源方向,N代表顶点法线方向,V代表观察者方向,R代表反射光方向。首先需要计算反射光的方向R,反射光方向R可以通过入射光方向和法向量求出,R + L = 2dot(N,L)N,进而推出R = 2dot(N,L)N - L。关于R计算的推导,可以看下面这张图:


不过在cg中,我们不用这么麻烦,cg为我们提供了一个计算反射光方向的函数reflect函数,我们只需要传入入射光方向(光源方向的反方向)和表面法线方向,就可以计算得出反射光的方向。然后,我们通过dot(R,V)就可以得到反射光方向和观察者方向之间的夹角余弦值了。下面给出冯氏反射模型公式:

I(spcular) = I * k * pow(max(0,dot(R,V)), gloss) ,其中I为入射光颜色向量,k为镜面反射系数,gloss为光滑程度。

通过上面的公式,我们可以看出,镜面反射强度跟反射向量与观察向量的余弦值呈指数关系,指数为gloss,该系数反映了物体表面的光滑程度,该值越大,表示物体越光滑,反射光越集中,当偏离反射方向时,光线衰减程度越大,只有当视线方向与反射光方向非常接近时才能看到高光现象,镜面反射光形成的光斑较亮并且较小;该值越小,表示物体越粗糙,反射光越分散,可以观察到光斑的区域越广,光斑大并且强度较弱。

Phong光照模型在Unity中的实现


下面看一下冯氏光照模型在Unity中的实现,由于有高光,为了更好的效果,我们将主要的计算放到了fragment shader中进行。不多说,上代码:
Shader "ApcShader/SpecularPerPixel"
{
	//属性
	Properties
	{
		_Diffuse("Diffuse", Color) = (1,1,1,1)
		_Specular("Specular", Color) = (1,1,1,1)
		_Gloss("Gloss", Range(1.0, 255)) = 20
	}

	//子着色器	
	SubShader
	{
		Pass
		{
			//定义Tags
			Tags{ "LightingMode" = "ForwardBase" }

			CGPROGRAM
			//引入头文件
			#include "Lighting.cginc"

			//定义函数
			#pragma vertex vert
			#pragma fragment frag

			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;

			//定义结构体:应用阶段到vertex shader阶段的数据
			struct a2v
			{
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};

			//定义结构体:vertex shader阶段输出的内容
			struct v2f
			{
				float4 pos : SV_POSITION;
				float3 worldNormal : NORMAL;
				float3 worldPos : TEXCOORD1;
			};

			//顶点shader
			v2f vert(a2v v)
			{
				v2f o;
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				//法线转化到世界空间
				o.worldNormal = n
  • 7
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值