菲涅尔效果 (Fresnel Effect)

物体边缘发光的物理原理

📚 什么是菲涅尔效应

菲涅尔效应是一种光学现象:当光线以掠射角度射向表面时,反射率会增加。在 Shader 中常用于制作边缘光、能量护盾、水面反射等效果。

视线 N·V ≈ 1 弱反射 N·V ≈ 0 强反射

🧮 数学原理

菲涅尔公式(简化版)

基础公式:Fresnel = 1 - (N·V)

幂次调整:Fresnel = pow(1 - (N·V), power)

其中:

  • N:表面法线
  • V:视线方向
  • power:控制边缘范围(通常 1-5)

Schlick 近似(物理更准确)

F = F0 + (1 - F0) × (1 - N·V)⁵

F0:基础反射率(垂直入射时的反射率)

  • 水:约 0.02
  • 玻璃:约 0.04
  • 金属:0.5 - 1.0

💻 代码实现

基础菲涅尔

// Properties
_FresnelPower("Fresnel Power", Range(0.1, 5)) = 2
_FresnelColor("Fresnel Color", Color) = (0, 0.5, 1, 1)

// Fragment Shader
half4 frag(Varyings input) : SV_Target
{
    float3 N = normalize(input.normalWS);
    float3 V = normalize(_WorldSpaceCameraPos - input.positionWS);
    
    // 计算菲涅尔
    float NdotV = saturate(dot(N, V));
    float fresnel = pow(1.0 - NdotV, _FresnelPower);
    
    // 应用颜色
    half4 baseColor = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, input.uv);
    half3 finalColor = lerp(baseColor.rgb, _FresnelColor.rgb, fresnel);
    
    return half4(finalColor, baseColor.a);
}

Schlick 近似

float FresnelSchlick(float cosTheta, float F0)
{
    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}

// 使用
float NdotV = saturate(dot(N, V));
float fresnel = FresnelSchlick(NdotV, 0.04); // 0.04 为非金属默认值

带颜色的菲涅尔

float3 FresnelSchlickColor(float cosTheta, float3 F0)
{
    return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}

// 金属使用 albedo 作为 F0
float3 F0 = lerp(0.04, albedo, metallic);
float3 fresnel = FresnelSchlickColor(NdotV, F0);

🎮 应用场景

🛡️ 能量护盾

科幻护盾的边缘发光效果

🌊 水面反射

水面边缘反射天空

👤 角色轮廓

角色选中或被击中时的高亮

🚗 车漆效果

双层漆面的边缘色变

💡 常用组合

菲涅尔 + 自发光

// 护盾效果
float fresnel = pow(1.0 - NdotV, _FresnelPower);
float3 emission = _FresnelColor.rgb * fresnel * _EmissionStrength;
return half4(baseColor.rgb + emission, baseColor.a * fresnel);