• 【图形学】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;
    			}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

      前面的代码和之前的一致,根据公式,我们只用改变片元着色器的代码中的一部分。
      回想为什么第 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"
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

      和之前的代码有所不一样的地方在于 第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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

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


      ②UnityWorldSpaceViewDir( in float3 worldPos )

    inline float3 UnityWorldSpaceViewDir( in float3 worldPos )
    {
        return _WorldSpaceCameraPos.xyz - worldPos;
    }
    
    • 1
    • 2
    • 3
    • 4

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


      ⑤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
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

      法线的 模型空间 到 世界空间 的变换和其他普通向量不太一样。如果对此有疑问,可以参考《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));
    }
    
    • 1
    • 2
    • 3
    • 4

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

  • 相关阅读:
    机器学习:基于梯度下降算法的线性拟合实现和原理解析
    uni-app —— 小程序登录功能的相关实现
    Mac版快速切换工具:One Switch中文 for mac
    总结Redis的原理
    Django实现音乐网站 ⒅
    KS和IV的区分比较
    适用于STM32的U8G2回调函数例程
    Web自动化测试怎么做?Web自动化测试的详细流程和步骤
    Python + re + scrapy.Selector: 分析提取某在线征信站体系内容(一)
    基于人工水母优化的BP神经网络(分类应用) - 附代码
  • 原文地址:https://blog.csdn.net/qq_40891541/article/details/126225690