【《Unity Shader入门精要》 提炼总结】(八)第八章·Phong公式介绍&高光反射Shader编写&逐顶点光照&逐像素光照&Blinn-Phong光照

 

 

 

本文由@唐三十胖子出品,转载请注明出处。  
文章链接:https://blog.csdn.net/iceSony/article/details/84349017

 

 

这篇文章将总结和提炼《Unity Shader入门精要》的第六章“Unity中的基础光照”的内容。

通过这篇文章,你可以知道

1)Phong公式介绍

2)高光反射的逐顶点光照模型

3)高光反射的逐像素光照模型

4)高光反射的Blinn-Phong光照模型

一.Phong公式介绍

Phong公式:高光反射颜色=光的颜色*高光反射系数*max(0,反射光在视角方向的投影)^反射度

(ShaderLab编写中,高光反射系数与反射度都是在属性中定义的)

那么反射光与视角方向怎么获取呢?

通过上一章节的学习我们知道NORMAL语义是模型空间法线,_WorldLightPos0是世界空间光照方向

可以通过内置函数reflect(法线,光照方向)

由于获取反射方向由于光线i是指向顶点的所以我们relect最后要加个负号:)

视角方向其实Unity中也有相关获取方式:_WorldSpaceCameraPos.xyz

好了有了这些参数我们开始实现吧。

二.高光反射的逐顶点光照

第一步设置属性

Properties
{
    _Specular("反射系数",Color) = (1.0,1.0,1.0,1.0)
    _Gloss("高光区域",Range(8, 256)) = 20
}

第二步设置pass的调用与光照模式(结构体和漫反射光照没什么区别)

Pass
{
    Tags{ "LightMode" = "ForwardBase"}
    CGPROGRAM
    #pragma vertex vert
    #pragma fragment frag
    #include"Lighting.cginc"
    float4 _Diffuse;
    float4 _Specular;
    float _Gloss;
    struct a2v 
    {
        float4 pos : POSITION;
        float3 normal :NORMAL;
    };
    struct v2f
    {
        float4 pos : SV_POSITION;
	float3 color :COLOR;
    };

重点来了顶点着色器编写

v2f vert(a2v v) 
{
    v2f o;
    o.pos = UnityObjectToClipPos(v.pos);
    float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
    float3 normal2 = (mul(v.normal, (float3x3)unity_WorldToObject));

    float3 lightDir = (_WorldSpaceLightPos0.xyz);
    float3 reflectDir = -(reflect(lightDir, normal2));
    float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - o.pos);
    float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(reflectDir, viewDir)), _Gloss);
			
    o.color = ambient + specular;
    return o;
}

lightDir 获取光照方向、reflectDir 通过法线与光照方向获取反射方向、_WorldSpaceCameraPos代表世界坐标下摄像机位向量,再减去世界坐标下的顶点向量,得到的就是视角方向viewDir

唯一需要注意的是视角方向viewDir必须加获取单位向量,很明显这里的pow代表的是颜色的深度,如果你viewDir不加限制会影响数据的!

最后效果

因为没有添加漫反射的原因,所以物体默认是黑色的

和漫反射类似的问题,高光部分不平滑,原因是逐顶点计算是非线性的。

完整代码如下

Shader"sony/Shader150"
{
	Properties
	{
		_Specular("反射系数",Color) = (1.0,1.0,1.0,1.0)
		_Gloss("高光区域",Range(8, 256)) = 20
	}
	SubShader
	{
		Pass
		{
			Tags{ "LightMode" = "ForwardBase"}
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#include"Lighting.cginc"
			float4 _Diffuse;
			float4 _Specular;
			float _Gloss;
			struct a2v 
			{
				float4 pos : POSITION;
				float3 normal :NORMAL;
			};
			struct v2f
			{
				float4 pos : SV_POSITION;
				float3 color :COLOR;
			};
			v2f vert(a2v v) 
			{
				v2f o;
				o.pos = UnityObjectToClipPos(v.pos);
				float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				float3 normal2 = (mul(v.normal, (float3x3)unity_WorldToObject));

				float3 lightDir = (_WorldSpaceLightPos0.xyz);
				float3 reflectDir = -(reflect(lightDir, normal2));
				float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - o.pos);//必须加normalize
				float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(viewDir,reflectDir)), _Gloss);

				o.color = ambient + specular;
				return o;
			}
			fixed4 frag(v2f i) : SV_Target
			{
				return fixed4(i.color,1.0);
			}
			ENDCG
		}
	}
	FallBack "Diffuse"
}

三.逐像素光照

先看实现,右边逐像素光照看起来润滑很多

结构体的修改

struct v2f
{
    float4 pos : SV_POSITION;
    float3 texcoord :TEXCOORD0;
    float3 texcoord1 :TEXCOORD1;
};

函数的修改

v2f vert(a2v v)
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.pos);
		o.texcoord = (mul(v.normal, (float3x3)unity_WorldToObject));
		o.texcoord1 = mul(unity_ObjectToWorld, v.normal).xyz;
		return o;
	}
	fixed4 frag(v2f i) : SV_Target
	{
		float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

		float3 lightDir = (_WorldSpaceLightPos0.xyz);
		float3 reflectDir = -(reflect(lightDir, i.texcoord));
		float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.texcoord1);//必须加normalize
		float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(viewDir,reflectDir)), _Gloss);
		return fixed4(ambient + specular,1.0);
	}

注意这里的结构体里面删掉了颜色,改变成TEXCOORD0

如果不知道什么是TEXCOORD的点击☞   https://blog.csdn.net/iceSony/article/details/84309811

 

这里其实也可以指定类型为NORMAL

但是如果texcoord:NORMAL ,texcoord1:NORMAL ,这样编辑器会报错

所以为了指定float3类型我们常用TEXTCOORD

完整代码

Shader"sony/Shader152"
{
	Properties
	{
		_Specular("反射系数",Color) = (1.0,1.0,1.0,1.0)
		_Gloss("高光区域",Range(8, 256)) = 20
	}
		SubShader
	{
		Pass
	{
		Tags{ "LightMode" = "ForwardBase" }
		CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include"Lighting.cginc"
		float4 _Diffuse;
	float4 _Specular;
	float _Gloss;
	struct a2v
	{
		float4 pos : POSITION;
		float3 normal :NORMAL;
	};
	struct v2f
	{
		float4 pos : SV_POSITION;
		float3 texcoord :NORMAL;
		float3 texcoord1 :TEXCOORD1;
	};
	v2f vert(a2v v)
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.pos);
		o.texcoord = (mul(v.normal, (float3x3)unity_WorldToObject));
		o.texcoord1 = mul(unity_ObjectToWorld, v.normal).xyz;
		return o;
	}
	fixed4 frag(v2f i) : SV_Target
	{
		float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

		float3 lightDir = (_WorldSpaceLightPos0.xyz);
		float3 reflectDir = -(reflect(lightDir, i.texcoord));
                //必须加normalize
                float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.texcoord1);
		float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(viewDir,reflectDir)), _Gloss);
		return fixed4(ambient + specular,1.0);
	}
		ENDCG
	}
	}
	FallBack "Diffuse"
}

四.Blinn-Phong光照模型

最后来介绍一种真实开发中使用的高光反射模型

逐顶点光照的改版frag函数,代码修改仅两行

float3 halfDir = normalize(lightDir + viewDir);
float3 specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(halfDir,reflectDir)), _Gloss);

这种光照模型计算最贴近真实情况

代码如下

Shader"sony/Shader154"
{
	Properties
	{
		_Specular("反射系数",Color) = (1.0,1.0,1.0,1.0)
		_Gloss("高光区域",Range(8, 256)) = 20
	}
		SubShader
	{
		Pass
	{
		Tags{ "LightMode" = "ForwardBase" }
		CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include"Lighting.cginc"
		float4 _Diffuse;
	float4 _Specular;
	float _Gloss;
	struct a2v
	{
		float4 pos : POSITION;
		float3 normal :NORMAL;
	};
	struct v2f
	{
		float4 pos : SV_POSITION;
		float3 texcoord :NORMAL;
		float3 texcoord1 :TEXCOORD1;
	};
	v2f vert(a2v v)
	{
		v2f o;
		o.pos = UnityObjectToClipPos(v.pos);
		o.texcoord = (mul(v.normal, (float3x3)unity_WorldToObject));
		o.texcoord1 = mul(unity_ObjectToWorld, v.normal).xyz;
		return o;
	}
	fixed4 frag(v2f i) : SV_Target
	{
		float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

		float3 lightDir = (_WorldSpaceLightPos0.xyz);
		float3 reflectDir = -(reflect(lightDir, i.texcoord));
		float3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.texcoord1);//必须加normalize
		float3 specular = _LightColor0.rgb*_Specular.rgb*pow(saturate(dot(viewDir,reflectDir)), _Gloss);
		return fixed4(ambient + specular,1.0);
	}
		ENDCG
	}
	}
		FallBack "Diffuse"
}

 

事实上除了使用mul矩阵计算来转化向量空间,UNITY也提供一些CG函数帮助计算

下一章节我们将介绍Shader编程的纹理相关知识,毕竟好的效果离不开图片的支撑。

Excelsior!

©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页