前言
最近再阅读《GPU 编程与 CG 语言之阳春白雪下里巴人》 就想着用Unity Shader实现其中的代码,文章夹杂了自己的一些理解,我自己也刚入门shader,难免会有很多错误,希望多多指出,一起交流进步。
阅读本文需要有一定的基础 0基础可以先去阅读 零基础入门Unity Shader 系列文章
10.1 Cook-Torrance 光照模型
公式如下:
其中 是漫反射光强, 是镜面反射光强的计算方法
其中 是菲涅尔反射系数 即:
其中 是反射系数, 是指向视点的向量,半角向量
值用于度量表面的粗糙程度,较大的m 值对应于粗糙平面,较小的值对应与较光滑的表面; 是顶点法向量N 和半角向量H 的夹角
化简之后得
最后公式如下
Unity shader 代码如下
Shader "Unlit/Cook-Torrance" { Properties { _Ka ("Ambient Color", Color) = (1, 1, 1, 1) _Kd ("Diffuse Color", Color) = (1, 1, 1, 1) _Ks ("Specular Color", Color) = (1, 1, 1, 1) _F ("Fresnel", Range(0, 1)) = 0.5 _M ("roughness", Range(0, 1)) = 0.5 } SubShader { Tags { "RenderType"="Opaque" } CGINCLUDE // 重复使用 float pow2(float x){ return x * x; } ENDCG Pass { Tags{ "LightMode" = "ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 normal : TEXCOORD0; float3 worldPos : TEXCOORD1; }; float4 _Ka; float4 _Kd; float4 _Ks; float _F; float _M; v2f vert (a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.normal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { float3 N = normalize(i.normal); float3 L = normalize(UnityWorldSpaceLightDir(i.worldPos)); float3 V = normalize(UnityWorldSpaceViewDir(i.worldPos)); float3 H = normalize(L + V); float3 NL = max(dot(N, L), 0); float3 NV = max(dot(N, V), 0); float3 NH = dot(N, H); float3 VH = dot(V, H); // 环境光 float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Ka; //漫反射 float3 diffuse = _Kd.rgb * _LightColor0.rgb * NL; // 高光 float3 specular = (0, 0, 0); float temp = (pow2(NH) - 1)/(pow2(_M) * pow2(NH)); // 粗糙程度 float roughness = (exp(temp))/pow2(_M) * pow(NH, 4.0); float a = (2 * NH * NV)/VH; float b = (2 * NH * NL)/VH; // 几何衰减系数 float geometric = min(a, b); geometric = min (1, geometric); // 菲涅尔反射系数 float fresnelCoe = _F + (1 - _F) * pow (1 - VH, 5.0); float rs = (fresnelCoe * geometric * roughness)/(NV * NL); specular = rs * _LightColor0.rgb * NL * _Ks; return float4 (ambient + diffuse + specular, 1.0); } ENDCG } } }
效果如下:
10.3 Bank BRDF经验模型
Bank BRDF的镜面反射部分公式为
分别表示镜面反射系数和高光系数; 表示入射光线方向、 表示实现观察方向、 表示该点的切向量。尤其要注意切向量的计算方法,因为一个三维空间点可能存在无数个切向量,通常我采用“顶点的法向量和视线方向做叉积,其结果作为
Unity shader 代码如下
Shader "Unlit/Bank-BRDF" { Properties { _Ka ("Ambient Color", Color) = (1, 1, 1, 1) _Kd ("Diffuse Color", Color) = (1, 1, 1, 1) _Ks ("Specular Color", Color) = (1, 1, 1, 1) //ns 系数 _Shininess ("Shininess", Range(8, 256)) = 8 } SubShader { Tags { "RenderType"="Opaque" } CGINCLUDE // 重复使用 float pow2(float x){ return x * x; } ENDCG Pass { Tags{ "LightMode" = "ForwardBase" } CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" struct a2v { float4 vertex : POSITION; float3 normal : NORMAL; }; struct v2f { float4 pos : SV_POSITION; float3 normal : TEXCOORD0; float3 worldPos : TEXCOORD1; }; float4 _Ka; float4 _Kd; float4 _Ks; float _Shininess; v2f vert (a2v v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.normal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld, v.vertex); return o; } fixed4 frag (v2f i) : SV_Target { float3 N = normalize(i.normal); float3 L = normalize(UnityWorldSpaceLightDir(i.worldPos)); float3 V = normalize(UnityWorldSpaceViewDir(i.worldPos)); float3 H = normalize(L + V); // 切线方向 float3 T = normalize(cross(N, V)); float3 NL = max(dot(N, L), 0); float3 NV = max(dot(N, V), 0); float3 NH = dot(N, H); float3 VH = dot(V, H); // 环境光 float3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * _Ka; //漫反射 float3 diffuse = _Kd.rgb * _LightColor0.rgb * NL; // 高光 float3 specular = (0, 0, 0); float a = dot(L, T); float b = dot(V, T); // 计算bank BRDF 系数 float c = sqrt(1 - pow2(a)) * sqrt(1 - pow2(b)) - a * b; float brdf = _Ks * pow(c, _Shininess); specular = brdf * _LightColor0.rgb * NL; return float4 (ambient + diffuse + specular, 1.0); } ENDCG } } }
效果如下:
本章节完