【图形学】19 光照模型(四、Blinn-Phong光照模型)

来源:《UNITY SHADER入门精要》

1、回忆Blinn光照模型

  Blinn光照模型没有使用 反射方向、引入了新的半程向量(bisecoter) h ^ \boldsymbol{\hat{h}} h^,它是通过对视角方向 v ^ \hat{v} v^ 和光照方向和 r ^ \hat{r} r^ 想加再归一化得到的:
h ^ = v ^ + I ∣ v ^ + I ∣ \mathbf{\hat{h}}=\frac{\mathbf{\hat{v}}+\boldsymbol{I}}{|\mathbf{\hat{v}}+\boldsymbol{I}|} h^=v^+Iv^+I
  Blinn模型计算高光反射公式如下:
c s p c u l a r = ( c l i g h t ⋅ m s p e c u l a r ) max ⁡ ( 0 , n ^    ⋅    h ^ ) m g l o s s \boldsymbol{c}_{spcular}=\left( \boldsymbol{c}_{light}\cdot \boldsymbol{m}_{specular} \right) \max \left( 0,\hat{n}\,\,\cdot \,\,\hat{h} \right) ^{m_{gloss}} cspcular=(clightmspecular)max(0,n^h^)mgloss

2、Blinn-Phong光照模型的实现

  这里只用给出逐像素的实现。

Shader "Unity Shaders Book/Chapter 6/Blinn-Phong" {
	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 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 o;
				// Transform the vertex from object space to projection space
				o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
				
				// Transform the normal from object space to world space
				o.worldNormal = mul(v.normal, (float3x3)_World2Object);
				
				// Transform the vertex from object spacet to world space
				o.worldPos = mul(_Object2World, v.vertex).xyz;
				
				return o;
			}

  前面的代码和之前的一致,根据公式,我们只用改变片元着色器的代码中的一部分。
  回想为什么第 39 行是左乘,因为它们两换了位置,而避免了取逆的操作。

			fixed4 frag(v2f i) : SV_Target {
				// Get ambient term
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				// Compute diffuse term
				fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));
				
				// Get the view direction in world space
				fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
				// Get the half direction in world space
				fixed3 halfDir = normalize(worldLightDir + viewDir);
				// Compute specular term
				fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);
				
				return fixed4(ambient + diffuse + specular, 1.0);
			}
			
			ENDCG
		}
	} 
	FallBack "Specular"
}

  和之前的代码有所不一样的地方在于 第14行,采用的是 半程向量(bisecoter),就是worldLightDir + viewDir ,然后单位化一下,然后参与计算。

3、Unity的内置帮助函数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U7lgGW6J-1659936047643)(assets/image-20220620171848498.png)]

  其中的几个函数比较简单,不用帮助函数我们照样能够很快的写出来,也能够在 UnityCG.cginc 中找到。比如:

  ①WorldSpaceViewDir(float4 v)

// Computes world space view direction, from object space position
// *Legacy* Please use UnityWorldSpaceViewDir instead
inline float3 WorldSpaceViewDir( in float4 localPos )
{
    float3 worldPos = mul(unity_ObjectToWorld, localPos).xyz;
    return UnityWorldSpaceViewDir(worldPos);
}

  传入的参数是 模型空间 中的位置,函数先转转换到世界空间,再传入unityworldSpaceViewDir(worldPos)函数
  当然,我们可以从官方注释里看到,这个是个遗留下来的老函数了,不如直接使用 加了 unity 的版本。


  ②UnityWorldSpaceViewDir( in float3 worldPos )

扫描二维码关注公众号,回复: 14662878 查看本文章
inline float3 UnityWorldSpaceViewDir( in float3 worldPos )
{
    return _WorldSpaceCameraPos.xyz - worldPos;
}

  这里面也就执行了 摄像机-顶点位置,这个操作了。其他也没啥了。


  ⑤UnityWroldSpaceLightDir(float4 v)函数
表中其他三个关于 LightDir 的函数都是使用了 _WorldSpaceLightPos0 这个内置来获取光照方向的。必须是前向渲染的时候它才会被正确赋值。


  ⑦ UnityObjectToWorldNormal( in float3 norm )函数

// Transforms normal from object to world space
inline float3 UnityObjectToWorldNormal( in float3 norm )
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
    return UnityObjectToWorldDir(norm);
#else
    return normalize(mul(norm, (float3x3)unity_WorldToObject));
#endif
}

  法线的 模型空间 到 世界空间 的变换和其他普通向量不太一样。如果对此有疑问,可以参考《UnityShader入门精要》的4.7节。也可以参考本系列的[13 UnityShader入门(四)](13 UnityShader入门(四).md/## 6、法线变换问题),通过公式查看为什么。


  ⑧UnityObjectToWorldDir( in float3 dir )函数

inline float3 UnityObjectToWorldDir( in float3 dir )
{
    return normalize(mul((float3x3)unity_ObjectToWorld, dir));
}

  把方向矢量从 模型空间 转到 世界空间。

猜你喜欢

转载自blog.csdn.net/qq_40891541/article/details/126225690