菲涅尔效果,指当光到达两种材质的接触面时,一些光在接触面的表面被反射出去,而另一部分光将发生折射穿过接触面。
现在要用shader来实现这种效果,如果要精确地描述这种底层的物理,其计算公式是非常复杂的,性能消耗也比较大。我们的目的是使创建的图像看上去真实,因此我们不使用菲涅尔公式本身,而是使用以下经验公式,它能够用非常少的计算获得很好的效果。
reflectionCoefficient = max(0, min(1, bias + scale * pow(1 + dot(I,N), power)))
finalColor = reflectionCoefficient * reflectedColor + (1 - reflectionCoefficient ) * refractedColor
在unity3d中的渲染效果如下:
shader如下:
1 Shader "Custom/Test" 2 { 3 Properties 4 { 5 _Cube("Cube", Cube) = "white" {} 6 _EtaRatio("Eta ratio", float) = 0.8 7 _FresnelPower("Fresnel power", float) = 2 8 _FresnelScale("Fresnel scale", float) = 1 9 _FresnelBias("Fresnel bias", float) = 0 10 } 11 12 SubShader 13 { 14 Tags 15 { 16 "RenderType" = "Opaque" 17 } 18 19 Pass 20 { 21 CGPROGRAM 22 #pragma vertex Vert 23 #pragma fragment Frag 24 25 #include "UnityCG.cginc" 26 27 uniform samplerCUBE _Cube; 28 uniform float _EtaRatio; 29 uniform float _FresnelPower; 30 uniform float _FresnelScale; 31 uniform float _FresnelBias; 32 33 struct AppData 34 { 35 float4 pos : POSITION; 36 float3 nor : NORMAL; 37 }; 38 39 struct V2F 40 { 41 float4 pos : SV_POSITION; 42 float reflectionFactor : Color; 43 float3 r : TEXCOORD0; 44 float3 t : TEXCOORD1; 45 }; 46 47 V2F Vert(AppData vi) 48 { 49 V2F fi; 50 fi.pos = mul(UNITY_MATRIX_MVP, vi.pos); 51 52 float3 n = normalize(mul(vi.nor, (float3x3)_World2Object)); 53 float3 viewDir = WorldSpaceViewDir(vi.pos); 54 float3 i = normalize(-viewDir); 55 56 fi.r = reflect(i, n); // 反射向量 57 fi.t = refract(i, n, _EtaRatio); // 折射向量 58 fi.reflectionFactor = _FresnelBias + _FresnelScale * pow(1 + dot(i, n), _FresnelPower); // 反射颜色所占比例 59 60 return fi; 61 } 62 63 float4 Frag(V2F fi) : Color 64 { 65 float4 reflectC = texCUBE(_Cube, fi.r); 66 float4 refractC = texCUBE(_Cube, fi.t); 67 return lerp(refractC, reflectC, fi.reflectionFactor); 68 } 69 70 ENDCG 71 } 72 } 73 }