前言
项目中有一些第三方或者ASE生成的Shader代码,默认都是用的PBR光照,然而大部分材质BlinnPhong光照模型就够用了,尤其在移动端我们可以考虑优化。
如何使用最少的代码,将PBR使用的Metallic流程的材质参数快速转换到BlinnPhong光照呢,今天研究了一会儿效果还行,供大家参考。
Shader
//BlinnPhong额外加强高光,贴近PBR效果。
half4 UniversalFragmentBlinnPhongAddSpecular(InputData inputData, half3 diffuse, half4 specularGloss, half smoothness, half smoothnessPower, half3 emission, half alpha)
{
    // To ensure backward compatibility we have to avoid using shadowMask input, as it is not present in older shaders
    #if defined(SHADOWS_SHADOWMASK) && defined(LIGHTMAP_ON)
    half4 shadowMask = inputData.shadowMask;
    #elif !defined (LIGHTMAP_ON)
    half4 shadowMask = unity_ProbesOcclusion;
    #else
    half4 shadowMask = half4(1, 1, 1, 1);
    #endif
    Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, shadowMask);
    #if defined(_SCREEN_SPACE_OCCLUSION)
        AmbientOcclusionFactor aoFactor = GetScreenSpaceAmbientOcclusion(inputData.normalizedScreenSpaceUV);
        mainLight.color *= aoFactor.directAmbientOcclusion;
        inputData.bakedGI *= aoFactor.indirectAmbientOcclusion;
    #endif
    MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI);
    half3 attenuatedLightColor = mainLight.color * (mainLight.distanceAttenuation * mainLight.shadowAttenuation);
    half3 diffuseColor = inputData.bakedGI + LightingLambert(attenuatedLightColor, mainLight.direction, inputData.normalWS);
    #if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR) || defined(_MASKMAP)
    half3 specularColor = LightingSpecular(attenuatedLightColor, mainLight.direction, inputData.normalWS, inputData.viewDirectionWS, specularGloss, smoothness) * (smoothnessPower * 16);
    #endif
    #ifdef _ADDITIONAL_LIGHTS
    uint pixelLightCount = GetAdditionalLightsCount();
    for (uint lightIndex = 0u; lightIndex < pixelLightCount; ++lightIndex)
    {
        Light light = GetAdditionalLight(lightIndex, inputData.positionWS, shadowMask);
    #if defined(_SCREEN_SPACE_OCCLUSION)
            light.color *= aoFactor.directAmbientOcclusion;
    #endif
        half3 attenuatedLightColor = light.color * (light.distanceAttenuation * light.shadowAttenuation);
        diffuseColor += LightingLambert(attenuatedLightColor, light.direction, inputData.normalWS);
    #if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR) || defined(_MASKMAP)
        specularColor += LightingSpecular(attenuatedLightColor, light.direction, inputData.normalWS, inputData.viewDirectionWS, specularGloss, smoothness) * (smoothnessPower * 16);
    #endif
    }
    #endif
    #ifdef _ADDITIONAL_LIGHTS_VERTEX
    diffuseColor += inputData.vertexLighting;
    #endif
    half3 finalColor = diffuseColor * diffuse + emission;
    #if defined(_SPECGLOSSMAP) || defined(_SPECULAR_COLOR) || defined(_MASKMAP)
    finalColor += specularColor;
    #endif
    return half4(finalColor, alpha);
}
//metallic、smoothness 均为采样后已混合好的结果值
half4 MetallicBlinnPhong(InputData inputData, half3 albedo, half3 specularColor, half metallic, half smoothness, half3 emission, half alpha)
{
    const half smoothnessPower = smoothness;
    smoothness = exp2(10 * smoothness + 1); //SimpleLit做法
    const half oneMinusReflectivity = OneMinusReflectivityMetallic(metallic);
    albedo = albedo * oneMinusReflectivity;
    half3 specular = lerp(kDieletricSpec.rgb, albedo, metallic) * specularColor;
    half4 color = UniversalFragmentBlinnPhongAddSpecular(
        inputData,
        albedo,
        half4(specular.rgb, 0),
        smoothness,
        smoothnessPower,
        emission,
        alpha);
    return color;
}
对比
对比图偷懒就不放了🫣。
我们以 URP 自带的地形着色器 Universal Render Pipeline/Terrain/Lit 举个例子。
- 修改前:
    
half4 color = UniversalFragmentPBR(inputData, albedo, metallic, /* specular */ half3(0.0h, 0.0h, 0.0h), smoothness, occlusion, /* emission */ half3(0, 0, 0), alpha); - 修改后
    
half4 color = MetallicBlinnPhong( inputData, albedo, 1, metallic, smoothness, half3(0, 0, 0), alpha);