菲涅尔反射
一、菲涅尔反射是什么
一种光学效果 实现垂直于表面观察,反射较弱,而当实现非垂直于表面观察,夹角越小,反射越明显
二、菲涅尔反射效果在 UnityShader 中的作用
在 Unity 中,使用菲涅尔反射比直接使用反射能更加贴近真实世界的效果,在实现一些金属感、陶瓷感、玻璃感时效果更好
三、菲涅尔反射实现效果的原理
菲涅尔反射的原理还是利用立方体纹理进行环境映射 我们在计算反射时,还是利用之前的那一套反射计算规则 只是为我们在决定采样颜色时,需要使用的反射率变量添加新的计算规则
反射率使用菲涅尔等式来计算 但是在物理学中,菲涅尔等式非常复杂 所以在 UnityShader 中我们使用Schlick 菲尼尔近似等式来计算 R(θ) = R0 + (1 - R0 )(1 - cos(θ))⁵ ⬇ R(θ) = R0 + (1 - R0 )(1 - V·N)⁵
- R(θ) 表示入射角为 θ 时的反射率
- R0 表示垂直入射时某介质的反射率
- V 表示视角方向的单位向量
- N 表示顶点法向量
四、菲尼尔反射的基本实现
1.实现思路
- 在普通反射的基础上进行修改,将普通的反射率通过为菲涅尔近似等式计算菲涅尔反射率
- 属性声明 将反射率变量修改为 _Fresnel 菲涅尔反射中的介质反射率
- 结构体 添加
- 视角方向
- 法线方向
- 顶点着色器 结构体中变量的赋值
- 片元着色器 通过 Schlick 菲涅尔近似公式(
R(θ) = R0 + (1 - R0 )(1 - V·N)⁵) 计算菲尼尔反射率
2.实现示例
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| Shader "Unlit/FresnelBase" { Properties { _Cube("Cubemap",Cube) = ""{} _FresnelScale("FresnelBase",Range(0,1)) = 1 } SubShader { Tags{"RenderType" = "Opaque" "Queue" = "Geometry"}
Pass { Tags{"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc"
samplerCUBE _Cube; float _FresnelScale;
struct v2f { float4 pos:SV_POSITION; float3 worldNormal:NORMAL; float3 worldViewDir:TEXCOORD0; float3 worldRefl:TEXCOORD1;
}; v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); fixed3 worldPos = mul(unity_ObjectToWorld,v.vertex).xyz; o.worldViewDir = UnityWorldSpaceViewDir(worldPos); o.worldRefl = reflect(-o.worldViewDir,o.worldNormal); return o; } fixed4 frag(v2f i):SV_TARGET { fixed4 cubemapColor = texCUBE(_Cube,i.worldRefl); fixed fresenl = _FresnelScale + (1 - _FresnelScale)*pow(1-dot(normalize(i.worldNormal),normalize(i.worldViewDir)),5); return cubemapColor * fresenl; } ENDCG } } }
|
五、菲涅尔反射结合漫反射实现
1.基本思路
- 基于反射结合漫反射的思路
- 属性修改
- 去掉反射的颜色
- 修改反射率 为 菲涅尔反射相关的反射率 R0
- 修改 v2f 结构体
- 添加视角方向
- 顶点着色器
- 修改视角方向的临时变量
- 片元着色器
- 计算菲涅尔反射率
- 用菲涅尔反射率参与 lerp 计算
2.实现示例
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 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
| Shader "Unlit/Fresnel" { Properties { _Cube("Cubemap",Cube) = ""{} _FresnelScale("FresnelScale",Range(0,1)) = 1
_Color("Color",Color) = (1,1,1,1)
} SubShader { Tags{"RenderType" = "Opaque" "Queue" = "Geometry"}
Pass { Tags{"LightMode" = "ForwardBase"}
CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase
#include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc"
samplerCUBE _Cube; float _FresnelScale; fixed4 _Color;
struct v2f { float4 pos:SV_POSITION; float3 worldNormal:NORMAL; float3 worldPos:TEXCOORD0; fixed3 worldViewDir:TEXCOORD1; float3 worldRefl:TEXCOORD2;
SHADOW_COORDS(3)
}; v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.worldNormal = UnityObjectToWorldNormal(v.normal); o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz; o.worldViewDir = UnityWorldSpaceViewDir(o.worldPos); o.worldRefl = reflect(-o.worldViewDir,o.worldNormal); TRANSFER_SHADOW(o); return o; } fixed4 frag(v2f i):SV_TARGET { fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); fixed3 diffuse = _LightColor0.rgb * _Color.rgb * max(0,dot(normalize(i.worldNormal),worldLightDir));
fixed3 cubemapColor = texCUBE(_Cube,i.worldRefl).rgb; UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos); fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1-dot(normalize(i.worldNormal),normalize(i.worldViewDir)),5); fixed3 color = UNITY_LIGHTMODEL_AMBIENT.rgb + lerp(diffuse,cubemapColor,fresnel) * atten; return fixed4(color,1.0); } ENDCG } } FallBack "Reflective/VertexLit" }
|