Chapter 9 经典光照模型
前言
最近再阅读《GPU 编程与 CG 语言之阳春白雪下里巴人》 就想着用Unity Shader实现其中的代码,文章夹杂了自己的一些理解,难免会有很多错误,希望多多指出,一起交流进步。
阅读本文需要有一定的基础 0基础可以先去阅读 零基础入门Unity Shader 系列文章
漫反射
Lambert 光照模型 :
其中的 表示环境光的强度, 表示材质对环境光的反射系数(通常用Color值来表示) 表示入射光方向与顶点法线的夹角
最后公式为:
是法向量 是光的方向
Unity中的Shader 代码如下:
Shader "Unlit/diffuse" { Properties { // 控制Kd的强度 _KdIntensity ("Kd Intensity", Range(0, 1)) = 0.5 // 系数 _Kd ("Kd", Color) = (1, 1, 1, 1) } SubShader { Tags { "RenderType"="Opaque" } Pass { Tags{ "LightMode" = "ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" float _KdIntensity; float4 _Kd; struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 normal : TEXCOORD0; float3 worldPos : TEXCOORD1; }; v2f vert (a2v v) { v2f o; // 裁切空间 o.pos = UnityObjectToClipPos(v.vertex); // 世界坐标法向量 o.normal = UnityObjectToWorldNormal(v.normal); // 世界坐标 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } float4 frag (v2f i) : SV_Target { float3 N = normalize(i.normal); float3 L = normalize(UnityWorldSpaceLightDir(i.worldPos)).xyz; // 环境光 float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Kd; //漫反射 float3 diffuse =_Kd * _LightColor0.rgb * _KdIntensity * max(dot(N, L), 0); return float4(ambient + diffuse, 1.0); } ENDCG } } }
效果如下
镜面反射 Phong模型
这个属于高光反射
公式:
其中为镜面反射系数, 是高光系数, 观察方向, 反射光方向
是法向量 是光的方向
图示如下:
图中我写错了 应该是的
Unity Shader代码如下
Shader "Unlit/Phong" { Properties { _Glossness ("Shininess", Range(8, 256)) = 8 _Kd ("Diffuse Color", Color) = (1, 1, 1, 1) _Ks ("Specular Color", Color) = (1, 1, 1, 1) // 强度 _KdIntensity ("Kd Intensity", Range(0, 1)) = 0.5 _KsIntensity ("Ks Intensity", Range(0, 1)) = 0.5 } SubShader { Tags { "RenderType"="Opaque" } Pass { Tags{ "LightMode" = "ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" float _Glossness; float _KdIntensity; float _KsIntensity; float4 _Kd; float4 _Ks; struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 normal : TEXCOORD0; float3 worldPos : TEXCOORD1; }; v2f vert (a2v v) { v2f o; // 裁切空间 o.pos = UnityObjectToClipPos(v.vertex); // 世界坐标法向量 o.normal = UnityObjectToWorldNormal(v.normal); // 世界坐标 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } float4 frag (v2f i) : SV_Target { float3 N = normalize(i.normal); float3 L = normalize(UnityWorldSpaceLightDir(i.worldPos)).xyz; float3 V = normalize(UnityWorldSpaceViewDir(i.worldPos)).xyz; // 当入射角度和N为负数的时候说明接受不到高光 float3 R = normalize(2 * max(dot(N, L), 0) * N - L); // 环境光 float3 ambient = _Kd * UNITY_LIGHTMODEL_AMBIENT.xyz; //漫反射 float3 diffuse = _Kd.rgb * _LightColor0.rgb * _KdIntensity * max(dot(N, L), 0); // 高光 float3 specular = _Ks.rgb * _LightColor0.rgb * _KsIntensity * pow(max(dot(V, R), 0), _Glossness); return float4(ambient + diffuse + specular, 1.0); } ENDCG } } }
效果如下:
Blinn-Phong模型
在Phong 光照模型中 必须计算的值,在Blinn-Phong中我们用取代
公式如下:
其中 是法向量 是 入射方向 和视点方向的中间向量
和 的含义同上
如图所示:
代码如下:
Shader "Unlit/Blinn-Phong" { Properties { _Glossness ("Shininess", Range(8, 256)) = 8 _Kd ("Diffuse Color", Color) = (1, 1, 1, 1) _Ks ("Specular Color", Color) = (1, 1, 1, 1) // 系数 _KdIntensity ("Kd Intensity", Range(0, 1)) = 0.5 _KsIntensity ("Ks Intensity", Range(0, 1)) = 0.5 } SubShader { Tags { "RenderType"="Opaque" } Pass { Tags{ "LightMode" = "ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" float _Glossness; float _KdIntensity; float _KsIntensity; float4 _Kd; float4 _Ks; struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 normal : TEXCOORD0; float3 worldPos : TEXCOORD1; }; v2f vert (a2v v) { v2f o; // 裁切空间 o.pos = UnityObjectToClipPos(v.vertex); // 世界坐标法向量 o.normal = UnityObjectToWorldNormal(v.normal); // 世界坐标 o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } float4 frag (v2f i) : SV_Target { float3 N = normalize(i.normal); float3 L = normalize(UnityWorldSpaceLightDir(i.worldPos)).xyz; float3 V = normalize(UnityWorldSpaceViewDir(i.worldPos)).xyz; float3 R = normalize(2 * max(dot(N, L), 0) * N - L); float3 H = normalize(L + V); // 环境光 float3 ambient = _Kd * UNITY_LIGHTMODEL_AMBIENT.xyz; //漫反射 float3 diffuse = _Kd * _LightColor0.rgb * _KdIntensity * max(dot(N, L), 0); // 高光 float3 specular = _Ks * _LightColor0.rgb * _KsIntensity* pow(max(dot(H, N), 0), _Glossness); return float4(ambient + diffuse + specular, 1.0); } ENDCG } } }
效果如下
本章节 完